python原型链污染

python原型链污染

原型链污染

  • python中,对象的属性和方法可以通过原型链来继承和获取
  • 每一个对象都有一个原型,定义了其可以访问的属性和方法,所以可以通过修改原型链中的属性来利用漏洞攻击
  • 当对象访问属性或方法时,会先对自身进行查找,找不到就一次往上级查找
  • 要求被污染的类能 被查到
  • 是对类的属性值的污染,只能污染类的属性,不能污染类的方法

污染条件

merge合并函数,将源参数赋值到目标参数,用merge函数来修改父类函数

原型链污染

def merge(src, dst):  #src为原字典,dst为目标字典
    # Recursive merge function
    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)

合并过程,对src中的值进行遍历,判断dst目标字典中是都有 __getitem__ 属性,以此来判断是否为字典形式

存在 __getitem__属性

  • dst中 k 存在,且值为字典,合并嵌套字典(merge对内部的字典再进行遍历,将对应的每个键值对都取出来)
  • dst中 k 不存在,或值不是字典,将键值对 (k, v) 添加到 dst

如果dst不含 __getitem__,直接检测dst中是否存在 k 值,值是否为字典

  • 是,merge函数进行遍历,k作为dst,v作为src 继续遍历

__getitem__

当访问一个对象的某个键时,会调用该方法

示例分析

污染类对象属性son.b()

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)
merge(payload, instance)
print(son_a.secret)
print(instance.secret)

image-20240710165425278

merge(payload, instance)payload为原字典,instance为目标字典

__class__作为k值, {"__base__": {"secret": "world"}}作为v值

v中不存在__getitem__,此时 v 被当作键值对,递归下去,v 将作为src再走一遍merge

__base__作为 k,{"secret": "world"}作为v (污染到父类中的secret参数)

接下来就是遍历到secret:world,进入setattr函数

k 为secret,v 为 world

至此 将instance.secret=world污染成功

instance是对象类型,则判断语句为

elif hasattr(dst, k) and type(v) == dict:
    merge(v, getattr(dst, k))
  • 第一次递归:执行语句 merge(v, getattr(dst, k))

因为instance=son_b,所以当instance与__class__合并时,其属性就变成了instance对象所属的类son_b

  • 第二次递归:执行语句 merge(v, getattr(dst, k))

接着与 __base__合并,属性就变成了son_b所属类的父类father

  • 第三次递归:执行语句为 type(v) == dict结果为FALSE,因为 v=world不是字典型,递归结束,执行语句 setattr(dst, k, v)。此时father的属性被重置为 world

__base__继承关系获取目标类

如果我们想要污染的目标类 和 作为切入点的类,两者没有父子继承关系,就无法使用 __base__

获取全局变量

__init____globals__

__init__:初始化方法;类的内置方法

__globals__属性返回字典,包含该函数所在模块的全局变量

image-20240710201044949

返回True,代表demo.__globals__globals() 以及 A.__init__.__globals__ 三者相等

  • demo.__globals__demo函数的全局命名空间,因为德莫实在全局命名空间时定义的,所以 demo.__globals__ 等于 全局命名空间 globals()
  • A.__init__.__globals__是类A.__init__的全局命名空间 = globals()
示例
#demo.py
a = 1

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)


def demo():
    pass


class A:
    def __init__(self):
        pass


class B:
    classa = 2


instance = A()
payload = {
    "__init__": {
        "__globals__": {
            "a": 4,
            "B": {
                "classa": 5
            }
        }
    }
}
print(B.classa)
print(a)
merge(payload, instance)
print(B.classa)
print(a)

image-20240710202924509

获取其他模块

以上实例是基于,操作对象或者类是在入口文件当中,如果目标对象不在入口文件中,需对其他啊记载过的模块进行获取

import模块加载获取

通过payload模块重新定位加载

image-20240710211347150

sys模块加载获取

引用sys模块下的module属性,这个属性能够加载出来在自运行开始所有已加载的模块,从而我们能够从属性中获取到我们想要污染的目标模块

同样用其加载demo.py

image-20240710211329951

注:在使用payload传参时,需要在有源码的基础上传参

加载器loader获取

通过 loader.__init__.__globals__['sys']来获取sys模块

(loader加载器的作用是实现模块加载,在内置模块importlib中具体实现,而importlib模块下所有的py文件中均引入了sys模块)

image-20240710212814103

math模块的__loader__属性包含了一个loader对象,负责加载math模块

  • 在python中还存在一个__spec__,包含了关于类加载时候的信息,他定义在Lib/importlib/_bootstrap.py的类ModuleSpec,所以可以直接采用<模块名>.__spec__.__init__.__globals__['sys']获取到sys模块

函数形参默认值替换

__defaults__元组,储存函数或方法的默认参数值,定义函数时,可为其参数指定默认值

image-20240710213640756

参考

posted @   Yolololololo  阅读(133)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
点击右上角即可分享
微信分享提示