Python prototype chain pollution (also known as Class Pollution / Attribute Injection) exploits Python class inheritance and the __globals__ mechanism, through recursive merge functions or deep attribute setting functions (such as pydash.set_()), to "break out" of the current scope from an ordinary object, modify global variables, Flask configuration, Jinja2 engine settings, and achieve RCE, file reading, or privilege escalation.
In-depth reference
- Flask/Jinja2 complete pollution target list → references/flask-jinja2-targets.md
- Phase 3A-3I detailed exploitation Payload and Phase 4 advanced chain → references/exploitation-payloads.md
- pydash path filter bypass + Cookie octal bypass → references/pydash-bypass.md
- Sanic framework pollution chain → references/sanic-pollution-chain.md
- Complete pollution chain quick reference list → references/quick-reference.md
Phase 0: Identify pollution entry points
Look for code patterns that recursively merge/set user JSON input into Python objects:
| Entry type | Code characteristics | Common interfaces |
|---|---|---|
| Custom merge function | def merge(src, dst) + setattr(dst, k, v) |
POST JSON configuration update |
| pydash.set_() | pydash.set_(obj, path, value) |
API attribute setting |
| pydash.get() | pydash.get(obj, path) |
API attribute query |
| pydash.invoke() | pydash.invoke(obj, path, arg) |
API method call |
| Other deep-set libraries | glom, box, any deep-set implementation |
Configuration update interface |
Identification signals:
- The interface accepts nested JSON objects (
{"a": {"b": {"c": "value"}}}format) - The interface accepts dot-separated path strings (
"a.b.c"format) - Response headers show Python/Flask/Werkzeug/uvicorn
- Page/source code/requirements.txt mentions pydash, merge, update
Phase 1: Confirm pollution feasibility
1.1 Determine obj type
| obj type | Starting path | Identification method |
|---|---|---|
| Custom class instance | __init__.__globals__ |
Directly setting __init__.__globals__.xxx is effective |
| Dictionary (dict) | __class__.__init__.__globals__ |
Need to first __class__ break out of dictionary key-value logic |
1.2 Harmless probing (without damaging the target)
# merge 函数场景: 嵌套 JSON
curl -X POST http://target/api/merge \
-H 'Content-Type: application/json' \
-d '{"__class__": {"__name__": "test"}}'
# pydash.set_ 场景: 点分路径
curl -X POST http://target/api/update \
-H 'Content-Type: application/json' \
-d '{"path": "__class__.__name__", "value": "test"}'
# pydash.get 场景: 信息泄露探测
curl -X POST http://target/api/get \
-H 'Content-Type: application/json' \
-d '{"path": "__class__"}'
Phase 2: Choose pollution target
Decision tree
确认 Python 原型链污染可行
├── 目标使用 pydash.invoke()?
│ └── 是 → 直接 RCE(Phase 3A)
├── 目标使用 Flask?
│ ├── 需要提权? → 污染类属性 is_admin(Phase 3B)
│ ├── 有 session? → 污染 SECRET_KEY → 伪造 session(Phase 3C)
│ ├── 有 SSTI 但被过滤? → 污染 Jinja2 定界符(Phase 3D)
│ ├── 有 render_template? → 污染 searchpath/静态目录(Phase 3E)
│ └── 有 before_first_request? → 重置 _got_first_request(Phase 3F)
├── 目标有文件上传?
│ ├── 代码调用相对路径命令 → PATH 劫持(Phase 3G)
│ └── 代码有懒加载 import → sys.path 劫持(Phase 3H)
└── 有函数 shell=False 等默认参数?
└── 污染 __defaults__ / __kwdefaults__(Phase 3I)
For detailed exploitation commands and code for each Phase, see references/exploitation-payloads.md
Recommended attack order
Escalate step by step from low risk to high risk:
- Harmless probing → Confirm pollution feasibility (Phase 1.2)
- Information disclosure → Read SECRET_KEY, app.config, etc. via get (Phase 3C step 1)
- Privilege escalation → Pollute is_admin or global variables (Phase 3B)
- File reading → _static_url_path or searchpath (Phase 3E)
- RCE → invoke / delimiter+SSTI / PATH hijacking (Phase 3A/3D/3G)
If there are pydash-related clues, simultaneously use search_vulndb("pydash") to obtain the detailed exploitation chain in the CVE-2023-26145 entry; the two are complementary.
Relationship with SSTI and deserialization
| Technique | Entry | Relationship |
|---|---|---|
| Prototype chain pollution | merge/set_ interface | This skill |
| SSTI | Template injection point | Prototype chain pollution can modify Jinja2 delimiters to bypass SSTI filtering |
| Deserialization | pickle/json | After SECRET_KEY is polluted, sessions can be forged → triggering pickle deserialization |
The three can be combined: prototype chain pollution → modify SECRET_KEY → forge session → pickle RCE.