Imported from Fasthei/DSHAIred (
skills/attack-output-handling/SKILL.md). Install upstream withnpx skills add Fasthei/DSHAIred --skill attack-output-handling. Copyright stays with the author.
概述与何时用
诚实标注:本 skill 覆盖的主题(LLM05 Improper Output Handling)不在 OSAI 课程的任何一章内,课程只在 Ch4(OSAI/EN/Ch4 的 Blind Command Execution Verification / SQL Injection Evasion / Malicious Link Evasion / 4.7 数据投毒)触及了相邻但不同的问题——课程内容讲的是"如何让模型把恶意指令当成合法请求执行"(输入侧、注入侧),而本 skill 讲的是"模型已经诚实地生成了一段文本(可能是被上游注入诱导生成的,也可能只是复述了用户输入或摄入数据中的原始片段),这段文本本身没有被模型执行任何动作,但下游系统在渲染/解析/执行它时没有把它当成不可信数据处理"。这是两个独立但常常首尾相接的问题:Ch3/Ch4 的注入让模型愿意"说"出攻击者想要的内容,本 skill 让"模型说出的内容"在下游产生真实副作用。
核心前提(依据 OWASP GenAI Top10 LLM05 与 MITRE ATLAS 对应技术):LLM 输出在很多集成方式里被当作"系统已核验、可信"的字符串直接拼进下一个处理阶段——HTML 模板、终端打印、CSV 单元格、日志聚合器、shell 命令行、SQL 语句、CI 流水线的代码提交——而没有对输出做与"外部不可信输入"同等强度的净化/转义/上下文隔离。只要能让模型的合法输出通道(文本回复、生成代码、导出内容)里出现攻击者控制的字节序列,下游那一层缺失的信任边界就是攻击面,与模型本身是否"被越狱"无关。
适用场景:
- 聊天界面/网页助手把模型回复用
innerHTML/未转义模板直接渲染 → 存储型/反射型 XSS、HTML 注入。 - 导出/报表功能把模型生成内容写入 CSV/Excel → 公式注入(DDE/宏触发)。
- CLI/终端 UI 直接打印模型输出 → ANSI 转义序列注入(隐藏文本、光标劫持、伪造终端提示)。
- 模型回复中的 Markdown 链接/图片被渲染客户端(聊天 UI、IDE 插件、笔记应用)自动跟随或加载 → 钓鱼跳转、图片外泄(beacon)。
- 模型输出被写入日志后由日志查看器/SIEM 前端渲染 → 日志查看器 XSS/日志注入伪造记录。
- 代码生成助手的输出被自动合并/自动运行进 CI/CD 流水线,未经人工审查。
- 模型输出被下游代码不加参数化地拼进 shell 命令、SQL 语句、模板引擎(Jinja2/Handlebars 等)字符串 → 命令注入/SQL 注入/模板注入,但注入点在输出侧而非通常理解的"用户直接输入侧"。
不适用于:让模型本身产生该输出的注入手法本身(见 attack-single-agent 的 3.2/3.3)、多 Agent 间 A2A 协议层的投毒与命令执行确认(见 attack-a2a-multi-agent,尤其其 Ch4 的 Blind Command Execution Verification / SQL Injection Evasion 与本 skill 是同一条链路的"上游"部分)。
在 SOP 中的位置
延续 attack-single-agent 建立的枚举→攻击→检测→规避→确认五步法框架,本 skill 补充其中经常被漏掉的一环:
- 枚举:不问"模型会不会被注入",而问"模型的输出去了哪里、被谁用什么方式消费"。列出目标系统里模型输出的每一个下游消费点(渲染层/导出功能/日志管道/CI 触发点/后端拼接点),逐一标注该消费点对输入的信任假设(是否转义、是否参数化、是否沙箱化)。
- 攻击(朴素):让模型的合法输出通道中原样出现一段测试载荷(可以是直接让它复述,也可以借助
attack-single-agent/attack-a2a-multi-agent的注入手法诱导生成),不做任何绕过,观察下游是否原样渲染/执行。 - 检测:查看目标是否有输出侧的净化/CSP/日志转义/CI 人工审查门禁,确认拦在哪一层。
- 规避:针对确认的具体拦截点改造载荷形态(编码、拆分标签、利用消费端解析差异)。
- 确认:必须在下游消费者的真实渲染/执行环境里观察到效果(浏览器 DOM、终端字节序列、CI 实际合并记录、日志查看器实际渲染),而不是仅凭模型把载荷复述了一遍就下结论。
与 attack-single-agent 输入/输出通道表的衔接:那张表里的"输出通道 × 滥用类型"(文本响应→数据外泄、工具调用→未授权操作、记忆写入→持久化后门)到本 skill 进一步细化为"输出内容被具体哪个下游解释器消费",是同一张地图的延伸列。
方法与技法(按类别)
以下每类给出:可观察信号(如何判断该下游消费点有暴露面)、泛化测试载荷形态(结构描述,非可直接复制的靶场值)、对应下游风险。
1. HTML/DOM 注入与存储型/反射型 XSS
- 可观察信号:模型回复的前端渲染方式——查看响应是否被
innerHTML/dangerouslySetInnerHTML/无转义模板插值直接写入 DOM;若前端把回复当 Markdown 渲染但未对内嵌 HTML 标签做剥离,同样暴露。 - 泛化载荷形态:诱导或直接让模型输出中包含形如
<img src=x onerror=...>、<script>标签、或利用 Markdown 渲染器对 HTML 透传的特性(如原始<a>/<iframe>标签);若目标内容会被持久化(如写入共享知识库、工单描述、评论字段)后再展示给其他用户,即构成存储型 XSS——参见attack-single-agent3.4 的记忆投毒手法,把注入点从"影响未来会话回答内容"换成"影响未来渲染时执行的脚本"。 - 下游风险:会话劫持、凭据窃取(伪造登录框)、对其他查看者的持久化攻击。
2. CSV/表格导出公式注入
- 可观察信号:目标是否提供"导出对话记录/导出报告为 CSV/Excel"功能,且导出内容包含模型生成的自由文本字段。
- 泛化载荷形态:让模型输出的字段以
=、+、-、@开头(公式注入的经典触发字符),后接可被 Excel/Google Sheets 解析为公式或 DDE 调用的结构;也可以是诱导模型在"产品名/备注"一类会被导出的字段里复述这类前缀内容。 - 下游风险:受害者在本地 Excel/Sheets 打开导出文件时触发公式求值、DDE 外部调用或宏警告社工。
3. 终端/CLI 转义序列注入
- 可观察信号:目标是否有 CLI 工具/终端 UI 直接
print/写 stdout 模型的原始输出,且未对 ANSI 转义序列做过滤。 - 泛化载荷形态:诱导模型输出中包含 ANSI 转义码(如清屏、光标定位、颜色反转、隐藏文本、伪造 shell 提示符的字节序列),使终端显示被篡改——例如让日志的后续几行看起来像是新的、无害的命令行提示,诱导操作员误输入敏感命令;或用清屏/覆盖序列隐藏此前的告警输出。
- 下游风险:欺骗人工审阅者(安全分析师/运维)误判终端状态,为社工或掩盖攻击痕迹创造条件。
4. Markdown 危险链接与图片外泄(beacon)
- 可观察信号:渲染客户端(聊天 UI、IDE 插件、笔记/wiki 应用)是否自动渲染模型输出中的 Markdown 图片语法
![](),是否自动允许链接跳转不做域名提示。 - 泛化载荷形态:让模型输出中包含指向攻击者可控主机的
图片引用(客户端渲染时自动发起请求,等价于被动信标/外泄触达信号),或包含视觉上伪装成合法域名的链接(Unicode 同形字符域名、超长子域名掩盖真实主机)。可用serve_payload工具在本机快速起一个测试端点,把返回的 URL 嵌入测试载荷,验证渲染客户端是否真的发起了请求(服务器收到命中记录即为确证信号,而非仅"链接看起来存在")。 - 下游风险:客户端环境探测(IP/UA 指纹外泄)、钓鱼跳转、诱导点击执行下一步攻击。
5. 日志查看器注入与日志伪造
- 可观察信号:模型输出(或摄入的用户输入被模型复述后的内容)是否原样写入应用日志,日志是否经由 Web 化的日志查看器/SIEM 前端渲染(而非纯文本终端)。
- 泛化载荷形态:让模型输出中包含伪造的日志行格式(时间戳+级别+消息,看起来像是系统自身产生的一条合法/无害记录),干扰审计时间线;若日志查看器是网页应用且对日志内容做 HTML 渲染,同类 XSS 载荷可复用第 1 类的手法,只是投放点变成日志字段。
- 下游风险:审计追溯被误导(日志注入)、分析师在查看器界面中被 XSS(日志查看器注入)。
6. 生成代码流入 CI/CD 缺乏审查
- 可观察信号:代码生成/代码审查类 Agent 的输出是否存在自动合并、自动 PR、自动触发构建/部署的路径,中间是否有强制人工审查门禁。
- 泛化载荷形态:诱导模型在生成的代码/配置/依赖清单中包含隐蔽的额外行为(如新增一个看似无害的 postinstall 脚本、在 CI 配置里追加一个额外的 step、引入一个新依赖项),利用生成代码"看起来是任务要求的合理实现"这一伪装,观察是否有自动化流程会不经审查直接合并/构建/部署它。
- 下游风险:供应链层面的持久化后门——但红队测试到此为止,止于观察和证明路径存在,不实际提交可执行的破坏性变更、不实际触发生产构建/部署。
7. 输出被下游 shell/SQL/模板引擎直接解释
- 可观察信号:后端代码是否把模型输出原样拼接进
subprocess/os.system调用、SQL 字符串拼接(而非参数化查询)、或模板引擎的渲染字符串(Jinja2render_template_string、Handlebars 等)。这与attack-a2a-multi-agent(OSAI/EN/Ch4SQL Injection Evasion)中"让模型自己决定要执行的 SQL/命令"是同一条链路的下游落点——那里关注的是如何让模型愿意生成/转发危险指令并规避检测(如 SQL 编码隐藏xp_cmdshell关键字),本类关注的是后端代码本身是否把任何字符串(不论来源)不加处理地喂给解释器,模型输出只是众多可控输入源之一。 - 泛化载荷形态:确认输出落点的解释器类型后,构造该解释器语法下的边界字符探测载荷(如引号/分号/花括号/管道符),先用无害探测(
echo、SELECT 1、模板表达式求值一个算式)确认解释确实发生,再考虑是否需要进一步的编码/分片规避(参考 Ch4 的编码绕过关键词过滤思路)。 - 下游风险:命令注入、SQL 注入、服务端模板注入(SSTI),影响面等同传统 Web 漏洞,只是触发路径经过了一次 LLM 生成。
可用 MCP 工具
output_handling_probe:针对已确认的下游消费点发送带标记载荷并读取该消费点的实际响应/渲染证据(而非仅模型的文本回复),用于区分"模型复述了载荷"与"下游真的处理了载荷"。工具返回的是观察数据,不是漏洞判定。serve_payload:本机起一个 127.0.0.1 测试 HTTP 服务,托管无害测试内容(如带唯一标记的图片/网页),返回可嵌入 Markdown/HTML 载荷的 URL;配合命中日志判断渲染客户端是否真的发起了请求。仅用于已授权目标的可交付测试,返回 URL 可访问不等于注入/外泄已被验证。
技战法编排:按“生成点 → 变换点 → 解释点 → 副作用”追踪
不要按载荷名称孤立测试,而要为每条输出链建立四段式追踪表:
- 生成点:记录攻击者可控内容怎样进入模型输出,是原样复述、摘要保留、结构化字段生成,还是由上游间接注入诱导产生。
- 变换点:逐层记录 Markdown 渲染、JSON 编解码、模板插值、CSV 序列化、日志格式化等变换;每经过一层都比较原始字节与变换后字节,定位究竟是哪一层改变或保留了危险语义。
- 解释点:确认最终消费者及其语法上下文,例如 HTML 文本/属性/URL、shell 参数/命令串、SQL 值/标识符、模板表达式、表格单元格或终端控制序列。载荷必须匹配具体上下文,不能拿同一字符串横扫所有解释器。
- 副作用:使用唯一标记确认解释行为产生的可观察结果,例如 DOM 节点变化、受控回连命中、公式被求值、模板表达式被计算或额外流水线步骤被创建。
采用递进探针减少误判:纯文本标记 → 上下文闭合字符 → 无害表达式求值 → 受控副作用。每一级只回答一个问题:内容是否到达、能否突破当前上下文、是否发生解释、是否产生影响。前一级未成立时不要直接升级载荷复杂度。
组合打法
- 间接注入 → 输出解释:先用
attack-single-agent或attack-rag-pipelines让受控标记进入输出,再由本 skill 验证下游解释器是否执行;分别记录“模型受影响”和“消费者受影响”,避免把一条链误报成一个漏洞。 - 持久化内容 → 第二查看者触发:把标记写入会被保存的会话、工单、知识条目或报告,由全新查看者打开,验证存储型效果与单会话反射型效果的差异。
- 多解释器链:当输出依次经过 Markdown、HTML、日志或模板时,设计在前一层表现为普通数据、到后一层才恢复语义的测试件,并逐层保存中间表示,定位真正的解释边界。
- 差分测试:保持业务内容不变,只改变编码、分隔、上下文位置或渲染入口;比较 Web、导出、日志、CLI 等多个消费者的结果。只有某一消费者产生差异时,才能把问题准确归因到该消费链。
验证标准
沿用同一套三层区分,本 skill 的关键点在于第 2 层必须落在下游消费者而非模型本身:
- 工具调用成功/模型复述成功:模型在回复中原样输出了测试载荷字符串。不构成任何漏洞——这只证明了"你能让模型说出这段文本",与它是否遵从注入指令、是否绕过了模型侧护栏无关;真正要验证的对象根本不是模型。
- 下游确有效果:必须在真实消费环境中观察到载荷被解释/渲染/执行——浏览器 DevTools 里看到脚本实际执行或 DOM 被篡改、终端截图显示转义序列生效、导出的 CSV 文件在 Excel 中打开触发公式栏、
serve_payload记录到来自渲染客户端的实际请求命中、CI 流水线日志显示额外代码被合并/构建、后端日志/数据库显示命令或 SQL 确实被解释器执行而非原样存为字符串。 - 发现已验证:在 2 的基础上完整走完枚举(找到具体消费点与其信任假设)→攻击→检测(该消费点是否有任何净化/CSP/审查门禁)→规避(若第一次被拦,改造后再次验证)→确认,并留存对应证据。
"模型愿意复述/生成一段包含尖括号或转义码的文本"是层级 1,禁止不经下游验证直接判定为 XSS/注入已确认。
常见失败与规避
- 载荷被模型的输出内容过滤器拦截,未能原样出现在回复中——参照
attack-single-agent3.2 的格式化绕过思路(编码、字符间插入空格、语言翻译)对同一载荷做变形,绕过的是模型侧的输出扫描,不代表下游侧也没有防护,两层需分别确认。 - 下游渲染层做了转义但只转义了部分字符(如转义了
<>但未转义引号/属性上下文),需要针对具体渲染上下文(HTML 标签内/属性内/JS 字符串内/URL 内)构造匹配该上下文的载荷变体,而不是套用同一个通用 XSS payload。 - Markdown 渲染器对裸 HTML 做剥离,但对 Markdown 原生语法(图片/链接)不做同等审查——退回用第 4 类的 Markdown 原生语法而非 HTML 标签。
- CSV 导出对首字符做了
=+-@过滤,但字段内某个内部位置仍可拼出公式(如通过换行符或分隔符位置绕过首字符检测),需要先确认过滤器的具体匹配位置(仅首字符 vs 全字段)再决定载荷形态。 - CI/CD 有人工审查门禁但审查者信任"看起来合理的小改动"——真正的失败点常常不是技术拦截而是人的信任偏差,规避方式是让新增行为伪装成任务描述里已经要求的必要实现的一部分(参照第 6 类),但此规避止于证明可绕过审查这一事实,不实际推进到破坏性变更。
- 反复用模型侧的话术尝试却始终无效:如果目标下游消费点根本不存在(例如导出功能只导出结构化字段不含自由文本,或前端确实做了严格的 DOM sanitizer),应该先回到枚举阶段确认消费点及其信任假设是否真实存在,而不是无限尝试载荷变体。
证据与报告
- 每个下游消费点的枚举证据:消费该输出的具体功能/端点截图或配置片段,标注它对输出内容的信任假设(转义与否、参数化与否、是否有审查门禁)。
- 每个已验证发现的载荷对:投放的原始载荷字符串(模型的原始输出,含时间戳)+ 下游实际处理结果的原始证据(浏览器 DevTools 截图/DOM 快照、终端渲染截图、导出文件本身、
serve_payload命中日志、CI 流水线运行记录)——必须是下游侧的证据,不能只附模型的聊天回复。 - 对比证据:同一载荷在模型侧被过滤/被下游拦截时的失败记录,与规避后成功记录的对照,体现"从被拦截到确认生效"的完整链路。
- 涉及持久化投毒类发现(如写入共享数据源导致其他用户渲染时触发):需要另一个干净会话/其他查看者视角下的复现证据,证明影响超出了测试者自身的会话,与
attack-single-agent3.4 记忆攻击的证据要求一致。 - 第 6 类(CI/CD)发现严格止步于"证明存在自动合并/自动构建且缺乏审查"这一事实层面的证据(如流水线配置截图、一次无害标记性改动被自动合并的记录),不得附带任何真实生产构建/部署的证据,因为该动作本身不应被执行。