Python原型链污染从基础到深入

696次阅读
没有评论

共计 2224 个字符,预计需要花费 6 分钟才能阅读完成。

之前看过许多有关Python原型链污染的题,自己一道也没能做出。

于是今天下定决心好好把这个问题解决一下

1.什么是Python原型链污染?

Python原型链污染是一种通过修改对象原型链中的属性,导致程序行为偏离预期的攻击技术。其核心原理与JavaScript原型链污染类似,但实现方式因语言特性而有所差异。

  • ​原型继承特性​
    Python中每个对象通过__class__属性指向其所属类,类通过__base__属性指向父类。当访问对象属性时,若当前对象/类中未定义,会沿原型链向上查找14
  • ​污染条件​
    需要存在递归合并函数(如merge)且未对特殊属性过滤

接下来我们来看一个最简单的原型链污染

class Config:
    is_admin = False
    def set_config(cls, key, value):
        setattr(cls, key, value)
    def get_config(cls, key):
        return getattr(cls, key, None)
instance=Config()
print(instance.is_admin)     #False
setattr(instance,'is_admin','True')
print(instance.is_admin)     #True

进入python的debug模式,给倒数三行打上断点。我们可以看到刚初始化的instance里面的is_admin为false

Python原型链污染从基础到深入

经过setattr函数后is_admin变成了true

Python原型链污染从基础到深入

这个就是最简单的原型链污染

接下来我们看看一段示例代码

from flask import Flask, request, jsonify
from config import Config
app = Flask(__name__)
@app.route('/update_config',</span> methods=['POST'])
def update_config():
    data = request.json
    for key, value in data.items():
        Config.set_config(key, value)
    return jsonify({"status": "success", "config": data})
@app.route('/check_admin',</span> methods=['GET'])
def check_admin():
    is_admin = Config.get_config('is_admin')
    return jsonify({"is_admin": is_admin})
if __name__ == '__main__':
    app.run(debug=True)

如果传入恶意参数将is_admin污染为true则可以实现对管理员校验的绕过

因为没有对传入参数进行校验我们可以试图传入{“is_admin”:True}

setattr()函数就会把is_admin参数污染为true

实战中我们更经常看到的是merge的合并函数

class father:
    secret = "hello"
class son_a(father):
    pass
class son_b(father):
    pass
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)
instance = son_b()
payload = {
    "__class__" : {
        "__base__" : {
            "secret" : "world"
        }
    }
}
print(son_a.secret)#hello
print(instance.secret)#hello
merge(payload, instance)#hello
print(son_a.secret)#hello
print(instance.secret)#hello

这里由于个人觉得这个函数比较绕,我们打上断点来一步一步的解析这个函数

Python原型链污染从基础到深入

刚进入循环时候对应的值,整个阶段我们注意下dst变量的变化

Python原型链污染从基础到深入

此时dst还是son_b

在经过判断语句后(进入elif分支,因为dst没有__getitem__属性)

此时我们将第二次进入merge函数

Python原型链污染从基础到深入

进入后:

Python原型链污染从基础到深入

我们发现dst变成了son_b.__class__而src则是{‘base‘: {‘secret’: ‘world’}}

这是因为getattr函数获取了son_b的class属性,也就是son_b.__class__

src则是之前的v

继续进行分支的判断,我们即将第三次进入merge函数

Python原型链污染从基础到深入

进入后:

Python原型链污染从基础到深入

和之前类似,获取了son_b的class的base属性,相当于son_b.__class__.__base__也就是father类

第三次循环的k和v

Python原型链污染从基础到深入

此时的v不是字典了,所以进入了else分支

Python原型链污染从基础到深入

到了setattr函数了

此时dst为father,k为secret,v为world

所以我们将father类的secret属性成功污染为了world

参考:https://www.7ntsec.cn/?p=56

https://xz.aliyun.com/news/12518

正文完
 0
Rycarl
版权声明:本站原创文章,由 Rycarl 于2025-04-28发表,共计2224字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)