用途
本技能是一份面向 JavaScript 技术栈的原型链污染(Prototype Pollution)专家级测试手册,用于客户端与服务端场景下的漏洞发现与验证。当用户可控输入被合并进对象时(如查询解析器、JSON 请求体、深度 assign),或应用使用 lodash.merge、deep-extend、hoek.applyToDefaults、部分 qs/query-string 配置时,应优先考虑原型链污染。
主要能力
- 覆盖两条污染路径:
__proto__与constructor.prototype,并说明二者在过滤、JSON 解析、Bun/Node 差异下并不总是等价,需要分别测试。 - 客户端检测:通过 URL fragment(如
#__proto__[admin]=1、#constructor[prototype][role]=admin)观察路由或分析代码是否将 fragment 解析为对象并合并;结合 DOM/属性注入思路(如__proto__[src]、__proto__[onerror])验证。 - 服务端黑盒检测(Express/Node):提供 JSON 载荷与可观察信号对照,例如污染
parameterLimit、ignoreQueryPrefix、allowDots、json spaces、exposedHeaders、status等,通过后续干净请求观察全局副作用是否持续。 - 利用链(gadget)思路:EJS 的
escapeFunction选项污染、Timelion 表达式链(CVE-2019-7609)、Node child_process 相关选项(shell、argv0、env、NODE_OPTIONS)污染,以及通用constructor.prototype绕过弱校验的路径。 - 决策树:从“输入是否被合并为嵌套对象”出发,判断解析器是否允许
__proto__/constructor.prototype键,确认全局影响,再判断是否存在 gadget,最终形成 DoS/逻辑影响或高影响 PoC。
使用方式
- 快速探测:客户端先尝试
#__proto__[polluted]=1、#constructor[prototype][polluted]=1;服务端发送{"__proto__":{"polluted":true}}或{"constructor":{"prototype":{"polluted":true}}}。 - 判断优先级:若目标使用 lodash.merge、deep-extend、hoek.applyToDefaults 或特定 qs 配置,提高优先级。
- 服务端验证:先发送污染请求,再发送干净请求,观察响应头、状态码、JSON 空格等异常是否持续(注意连接池与 worker 生命周期会影响全局可见性)。
- 客户端验证:打开不带 fragment 的新页面,在控制台检查测试键是否仍存在于 Object.prototype 上(需排除浏览器扩展与 DevTools 干扰)。
- 利用链构建:在确认污染后,寻找读取原型链属性且未做 hasOwnProperty 检查的依赖,尝试串联为 RCE、SSRF 或路径穿越。
适用场景
- 存在深度合并、递归 assign、JSON.parse 后接 Object.assign、URL 查询转换为嵌套对象的应用。
- 使用 Express/qs/body-parser 等解析栈的 Node 服务端。
- 使用模板引擎(如 EJS)或存在表达式执行能力的系统。
- 客户端框架将属性名作为对象键合并的场景。
相关工具
yeswehack/pp-finder(定位易受污染的合并点)、yuske/silent-spring、yuske/server-side-prototype-pollution、BlackFan/client-side-prototype-pollution、portswigger/server-side-prototype-pollution、msrkp/PPScan。
限制与注意事项
- 利用链(如 EJS RCE、Timelion 链)高度依赖具体版本与配置,并非通用。
- 自动化工具可能对有状态应用产生副作用,应仅在授权目标上使用。
- 部分载荷效果受解析器实现、过滤规则、运行时(Node/Bun)差异影响,需分别验证。
- 本技能假定使用者已掌握对象展开与原型继承基础。
相关路由
输入路由与多注入并行入口参见 Injection Testing Router;非原型链的模板执行链参见 SSTI;非 JS 原型的不安全反序列化参见 Deserialization。