初探Python原型链污染
初探Python原型链污染
漏洞成因
关键代码
def merge(src, dst): |
污染类属性
class father: |
这就是一个简单的原型链污染,这个过程相当于
instance.__class__.__base__.secert=world |
值得注意的是,object
是无法被污染的。
污染全局变量
在python的全局命名空间中,有一个叫__file__
的变量,可以用它来获取文件的路径。
print(__file__) |
通过原型链污染也可以修改这个全局变量
class cls(): |
__globals__
是一个特殊的属性,可以用来获取当前模块或函数的全局变量字典。__file__
当然也在里面。
Python中的每一个函数都拥有一个__globals__
属性,或者叫字典变量。
Python的命名空间
在 Python 中,命名空间(Namespace)是一个将名称(变量、函数、类等)与对应对象(值)关联起来的机制。它可以理解为一个容器,保存着当前环境中的所有变量名以及它们对应的值,包含已经实例化的对象。通过命名空间,Python 能够确保不同名称不会发生冲突,帮助我们在代码中区分不同作用域的变量。
其实常说的作用域可以理解为命名空间,局部变量对应着局部作用域,也就是局部命名空间。只不过命名空间这个词相比较作用域,除了变量,可能还包含函数,类等。
一般的,要得到模块级别(可以简单理解为当前文件)的全局命名空间,有三种方法。
golbals()函数
# 使用 globals() 获取当前模块的全局命名空间
global_vars = globals()
for name, value in global_vars.items():
print(name, ":", value)函数的
__globals__
属性def my_function():
pass
# 使用函数的 __globals__ 属性获取全局命名空间
global_vars = my_function.__globals__
for name, value in global_vars.items():
print(name, ":", value)模块对象的
__dict__
属性每个模块对象也有一个
__dict__
属性,它是一个字典,包含该模块的所有全局变量和定义。可以通过sys.modules
获取当前模块,然后使用其__dict__
来访问全局命名空间。import sys
# 通过模块的 __dict__ 属性获取全局命名空间
global_vars = sys.modules[__name__].__dict__
for name, value in global_vars.items():
print(name, ":", value)
既然可以污染当前命名空间的全局变量,模块也在当前命名空间中,所以理所应当的也可以被污染。
import math |
污染模块中的属性
例题
main.py
import sj1t |
sj1t __init__.py
class key(): |
可以发现类属性已经被污染了,后续此类的所有实例化属性都会带上这个被污染后的类属性,经过实验第三方库的类属性也可以被污染,但是内置模块的类属性不能被污染。同样的,也可以污染已经实例化的对象的实例属性。已经被实例化的实例也在全局命名空间中。
但是污染对象的属性并不会因此影响到其他已经实例化或者即将实例化的对象。
sys模块加载获取
在许多的环境中导入模块并不是简单的利用import进行导入同级目录下的文件,更多的是利用内置模块进行导入。这时候我们就无法简单的利用上面的payload进行重定向了,我们需要使用sys这个模块进行定向。
sys模块中有一个modules属性,这个属性可以加详细这个程序运行时导入的所有模块。所有我们可以通过他来进行重定向。
{"__init__":{"__globals__":{"sys":{"modules":{"DEMO":{"a":2}}}}}} |
获取sys模块
我们知道了可以使用sys模块来重定位,但是我相信我们都有一个疑问就是如何获取sys模块。
通过加载器loader获取sys
我们可以通过loader加载器来获取sys模块的
loader加载器在python中的作用是为实现模块加载而设计的类,其在importlib这一内置模块中有具体实现。而importlib模块下所有的py文件中均引入了sys模块,这样我们和上面的sys模块获取已加载模块就联系起来了,所以我们的目标就变成了只要获取了加载器loader,我们就可以通过loader.__init__.__globals__['sys']
来获取到sys模块,然后再获取到我们想要的模块。
那么现在我们的问题就是如何获取loader模块。
在Python中,loader是一个内置的属性,包含了加载模块的loader对象,Loader对象负责创建模块对象,通过loader属性,我们可以获取到加载特定模块的loader对象。
loader获取到sys
1.<模块名>.__spec__.__init__.__globals__['sys']获取到sys模块 |