July 24, 2023

DASCTF2023 X 0X401 REVERSE

WebServer

0x00 Daily Shell Check

无壳64位

image-20230725133623303

0x01 Bindiff

全程无符号,这个server也不是用啥写的,于是查看字符串得知oatpp

image-20230725133722469

于是自己编译一份最基本的,随后 bindiff 恢复即可

https://blog.51cto.com/u_13999641/5101994#Oat__1

恢复了符号,根据 createResponse 函数交叉引用找到关键函数 sub_40617E

0x02 Get 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
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
sub_405624(v15, v13);
if ( sub_4EF8C0(v15) == 40 )
{
for ( i = 0; i <= 3; ++i )
{
for ( j = 0; j <= 9; ++j )
v15[10 * i + 8 + j] = *(char *)sub_4EFBD0(v15, 10 * i + j);// 取值放入v15
}
qmemcpy(v17, &unk_61B0E0, 0x190uLL);
v16[0] = 33211;
v16[1] = 36113;
v16[2] = 28786;
v16[3] = 44634;
v16[4] = 30174;
v16[5] = 39163;
v16[6] = 34923;
v16[7] = 44333;
v16[8] = 33574;
v16[9] = 23555;
v16[10] = 35015;
v16[11] = 42724;
v16[12] = 34160;
v16[13] = 49166;
v16[14] = 35770;
v16[15] = 45984;
v16[16] = 39754;
v16[17] = 51672;
v16[18] = 38323;
v16[19] = 27511;
v16[20] = 31334;
v16[21] = 34214;
v16[22] = 28014;
v16[23] = 41090;
v16[24] = 29258;
v16[25] = 37905;
v16[26] = 33777;
v16[27] = 39812;
v16[28] = 29442;
v16[29] = 22225;
v16[30] = 30853;
v16[31] = 35330;
v16[32] = 30393;
v16[33] = 41247;
v16[34] = 30439;
v16[35] = 39434;
v16[36] = 31587;
v16[37] = 46815;
v16[38] = 35205;
v16[39] = 20689;
for ( k = 0; k <= 3; ++k )
{
for ( m = 0; m <= 9; ++m )
{
for ( n = 0; n <= 9; ++n )
v16[10 * k + m] -= v15[10 * k + 8 + n] * v17[10 * n + m];// 线性运算
}
}
for ( ii = 0; ii <= 3; ++ii )
{
for ( jj = 0; jj <= 9; ++jj )
{
if ( v16[10 * ii + jj] )
goto LABEL_4;
}
}

那么拿到v17的值,就可以逆推出明文

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
from z3 import *

enc = [0x000081BB, 0x00008D11, 0x00007072, 0x0000AE5A, 0x000075DE, 0x000098FB, 0x0000886B, 0x0000AD2D, 0x00008326, 0x00005C03, 0x000088C7, 0x0000A6E4, 0x00008570, 0x0000C00E, 0x00008BBA, 0x0000B3A0, 0x00009B4A, 0x0000C9D8, 0x000095B3, 0x00006B77, 0x00007A66, 0x000085A6, 0x00006D6E, 0x0000A082, 0x0000724A, 0x00009411, 0x000083F1, 0x00009B84, 0x00007302, 0x000056D1, 0x00007885, 0x00008A02, 0x000076B9, 0x0000A11F, 0x000076E7, 0x00009A0A, 0x00007B63, 0x0000B6DF, 0x00008985, 0x000050D1]
key = [0x00000017, 0x0000000D, 0x00000004, 0x00000030, 0x00000029, 0x00000029, 0x0000002A, 0x00000021, 0x0000001E, 0x00000003, 0x00000045, 0x00000001, 0x0000000D, 0x0000002D, 0x00000029, 0x00000040, 0x00000008, 0x00000050, 0x0000000F, 0x0000002A, 0x00000038, 0x00000013, 0x0000003E, 0x00000046, 0x00000017, 0x0000003F, 0x0000001E, 0x00000044, 0x00000011, 0x00000038, 0x0000005C, 0x0000000C, 0x00000010, 0x00000040, 0x0000001F, 0x00000003, 0x00000011, 0x00000047, 0x0000003A, 0x00000009, 0x00000040, 0x00000053, 0x00000047, 0x00000034, 0x00000063, 0x00000059, 0x0000004C, 0x00000044, 0x00000001, 0x00000063, 0x00000010, 0x00000010, 0x00000034, 0x0000002B, 0x00000000, 0x0000002C, 0x00000032, 0x00000020, 0x00000032, 0x0000001F, 0x00000014, 0x0000003F, 0x00000002, 0x00000063, 0x00000000, 0x00000039, 0x0000004F, 0x0000002B, 0x00000047, 0x00000013, 0x00000050, 0x0000005C, 0x0000005D, 0x0000003A, 0x00000054, 0x0000004A, 0x00000051, 0x0000002D, 0x00000037, 0x00000015, 0x00000001, 0x00000063, 0x0000001E, 0x0000001C, 0x00000038, 0x00000001, 0x0000000C, 0x0000004D, 0x0000005C, 0x00000004, 0x00000025, 0x00000043, 0x0000003C, 0x00000036, 0x00000033, 0x0000004F, 0x00000026, 0x00000057, 0x00000030, 0x00000010]
input = [BitVec("input%d" % i, 8) for i in range(40)]
sol = Solver()

tmp = [0] * 48

for i in range(40):
tmp[i] = input[i]

for k in range(4):
for m in range(10):
num = 0
for n in range(10):
num += tmp[10 * k + n] * key[10 * n + m]
sol.add(num == enc[10 * k + m])

assert sol.check() == sat
ans = sol.model()

for i in range(40):
try:
print(chr(ans[input[i]].as_long()), end = "")
except:
print(" ", end = "")

Get Flag!

image-20230725134331781

TCP

0x00 Daily Shell Check

无壳64位且流量分析

image-20230725134627622

0x01 Main

流量分析

我们为客户端 192.168.10.137 去连接服务端 192.168.25.54

那么发来发去的就是我们所需要的数据了,经过三次握手后(应该),就 recv 收到服务端的32个字节,随后对应程序的 send 与 recv 就能搞清楚收发的数据了

image-20230725134956504

RSA

接收到的数据进入 sub_2090 函数,这是个快速幂

https://oi-wiki.org/math/binary-exponentiation/#%E5%AE%9A%E4%B9%89

而在题目中的形式如下,也就是我们平常的 RSA

1
2
3
4
5
6
7
8
9
10
long long binpow(long long a, long long b, long long m) {
a %= m;
long long res = 1;
while (b > 0) {
if (b & 1) res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}

一次加密8个字节,随后存入Buf,用了16个字节存一组,所以最后加密完为96个字节

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
__int64 __fastcall RSA(_QWORD *recv_data, __int64 data, __int64 num_48, __int64 *Buf)
{
__int64 result; // rax
__int64 v5; // rdx
int v8; // [rsp+28h] [rbp-38h]
int i; // [rsp+2Ch] [rbp-34h]
__int128 data_num; // [rsp+30h] [rbp-30h]

v8 = 0;
while ( 1 )
{
result = v8;
if ( num_48 <= v8 )
break;
data_num = 0uLL;
for ( i = 0; i <= 7; ++i )
{
if ( num_48 <= v8 )
data_num *= 0x100LL;
else
data_num = *(unsigned __int8 *)(v8 + data) + data_num * 0x100;// 左移8位
++v8;
}
*Buf = binpow(recv_data, data_num, *((__int64 *)&data_num + 1), recv_data[2], recv_data[3]);
Buf[1] = v5;
Buf += 2; // 加密完8个字节存储在16个字节
}
return result;
}

而RSA加密的e与n就是我们第一次接收到的数据,8个字节为一组,注意小端序,前16个字节作为 n,后16个字节作文 e

1
2
3
4
5
recv_data
5f ce f0 e8 67 34 9f c6
8f 40 76 3a 6b 0b de 01
01 00 01 00 00 00 00 00
00 00 00 00 00 00 00 00

随后我们发送了96个字节,也就是加密后的 data

Decrypt data

无疑这个 data 的值肯定有用,标准RSA直接解密即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import *
import gmpy2 as gp
n=0x1DE0B6B3A76408FC69F3467E8F0CE5F #在线分解n
p=1152921504606848051
q=2152921504606847269
e=0x10001
d=gp.invert(e,(p-1)*(q-1))
# print(isPrime(p),isPrime(q))
data="7a3202cc78acb66216341041b18ea201a3eb93301b27a2b6e77cb244d2e02c0082cd6369f3a7c1d2a1dd9b561c98510017c911f2ac5ec565e2d9b9016df34900661212d889172b99954d25018b5d43012e81783c2d8cebedeb053ccd651de400"
k=b""
for i in range(0,len(data),32):
c=bytes.fromhex(data[i:i+32])[::-1]
c=int(c.hex(),16)
k+=long_to_bytes(pow(c,d,n))
print(long_to_bytes(pow(c,d,n)))

Flow

随后我们继续接收12个字节,再进入一个函数,对我们的数据进行一个解密,分别是 TEA 和 异或,而所需的密钥则是刚刚我们解密出来的 data 的值

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
unsigned __int64 __fastcall dec_data(_QWORD *key, __int64 data, int len, __int64 dataa)
{
unsigned __int64 result; // rax
int k; // [rsp+24h] [rbp-Ch]
int i; // [rsp+28h] [rbp-8h]
int j; // [rsp+2Ch] [rbp-4h]

if ( len > 15 )
{
for ( i = 0; i < len; ++i )
*(_BYTE *)(i + dataa) = *(_BYTE *)(i + data);
result = (unsigned int)(len - 16);
for ( j = len - 16; j >= 0; --j )
result = TEA(key, (unsigned __int64 *)(dataa + j));// 前32个是TEA的key
}
else
{
for ( k = 0; ; ++k )
{
result = (unsigned int)k;
if ( k >= len )
break;
*(_BYTE *)(k + dataa) = *((_BYTE *)key + k + 32) ^ *(_BYTE *)(k + data);// 后16个是异或的密钥
}
}
return result;
}

那么刚刚的 12 个字节直接拿 data 的后十六位解密即可,审计可以发现就是控制当前的运行流程,分别有5个case

1
2
3
4
5
case1: 解密接收数据并打印
case2: 解密接收数据并存储
case3: 接收输入
case4: 打印某些值
case5: 执行shellcode

那么后续的所需要解密的数据都在流量包里了

总结下flow如下,我们每次接收12个字节,4个为一组,第一个控制进哪个 case,第三个控制接收数据的长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# [1, 0, 34]
# Hello, this is the remote server.

# [2, 1, 24]
# Your input is incorrect

# [2, 2, 40]
# Your input is correct , Congratulations

# [1, 0, 27]
# Please enter your password

# [3, 0, 0]
# input_something

# [2, 8, 579]
# shellcode

# [2, 4, 160]
# write_data
# ...

TEA的解密脚本

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
#include <stdio.h>
#include <stdint.h>

void TEA(__int64 *a1, unsigned __int64 *a2)
{
unsigned __int64 result; // rax
int i; // [rsp+1Ch] [rbp-24h]
unsigned __int64 v4; // [rsp+20h] [rbp-20h]
unsigned __int64 v5; // [rsp+28h] [rbp-18h]
__int64 v6; // [rsp+30h] [rbp-10h]

v4 = *a2;
v5 = a2[1];

v6 = 0x13C6EF3720LL;
for ( i = 0; i <= 31; ++i )
{
v5 -= (v4 + v6) ^ (16 * v4 + a1[2]) ^ ((v4 >> 5) + a1[3]);
v4 -= (v5 + v6) ^ (16 * v5 + *a1) ^ ((v5 >> 5) + a1[1]);
v6 -= 0x9E3779B9LL;
}
*a2 = v4;
result = v5;
a2[1] = v5;
}

int main()
{
unsigned char v[]={213, 45, 29, 107, 22, 147, 22, 74, 19, 49, 106, 51, 190, 5, 188, 28, 74, 32, 41, 185, 239, 191, 150, 220, 145, 125, 165, 5, 109, 62, 228, 108, 43, 8};
uint64_t k[4]={0x11f694fd,0x1d4b5072,0x5cc0de04,0x50d9f4e1};
int i, j;
int len = 34;
unsigned int * p = (unsigned int *)v;

for ( j = len - 16; j >= 0; --j )
{
TEA(k, (unsigned __int64 *)(v + j));

}

for ( i = 0; i < len; i++ )
printf("%c", v[i]);


return 0;
}

程序很明显就是写了个 shellcode,我们相对应解密即可,拿到解密后的shellcode随便写入一个能写的地方即可

1
2
3
4
5
dt=[shellcode]
adr=0x30E4
for i in range(len(dt)):
patch_byte(adr + i, dt[i])
print("ok")

0x02 Get Flag

分析一下 shellcode,可以发现我们的输入经过一个异或然后加一个值,然后高16位低16位比对

image-20230725141225430

我们所需要的值对应的就是流量里解密后的值,所以搓出脚本

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
from Crypto.Util.number import *
import gmpy2 as gp
n=0x1DE0B6B3A76408FC69F3467E8F0CE5F #在线分解n
p=1152921504606848051
q=2152921504606847269
e=0x10001
d=gp.invert(e,(p-1)*(q-1))
# print(isPrime(p),isPrime(q))
data="7a3202cc78acb66216341041b18ea201a3eb93301b27a2b6e77cb244d2e02c0082cd6369f3a7c1d2a1dd9b561c98510017c911f2ac5ec565e2d9b9016df34900661212d889172b99954d25018b5d43012e81783c2d8cebedeb053ccd651de400"
k=b""
for i in range(0,len(data),32):
c=bytes.fromhex(data[i:i+32])[::-1]
c=int(c.hex(),16)
k+=long_to_bytes(pow(c,d,n))
print(long_to_bytes(pow(c,d,n)))

"""
databuf : DWORD *4 BYTE[16]
"""
teak=[int.from_bytes(k[i:i+8],"little") for i in range(0,32,8)]
for i in teak:
print(hex(i),end=',')
print()


s="16cb56c17a19fb2ddcdb8472d52d1d6b1693164a13316a33be05bc1c4a2029b9efbf96dc917da5056d3ee46c2b0815cb56c17b19fb2de6db847211fd382bc3eb739037f7dfa4b0ca553f4dc67f81b71b7f9215cb56c17819fb2dd6db84727c8526cc3d6383bbcf0159590648586e7dd08b49cc1ee7f519ec206592254842b7f33d25646640ab16cb56c17a19fb2de5db8472c008ba6c15334f76485d8c17082f4468addbb3a62d20e754105d1014cb56c17a19fb2dfedb847215cb56c17219fb2dbdd984723419b3ea63306ff1224173d834b1f9c436332f39ec2aa102bb7747859d50793674b05a56f14ee29491895944cbf1f6846a9831f8dd5666202c4b0d558a690239b1dece4322fe535c0742cc2f37378577b999eaab713d853643bc379dac7f7f471ef2240b8898fac4431149aa27a65507db39a9111dea802a803506fbd4a2d558068974f993dcab4587a5620db6909a951dcf8bb65d4f8e92ac8300fd049bb3218199705babb255e7daef40a28c1d7e3f74cb1d44687be127258aafc4dbdaf09266daa68605e250aa244c179d759d22e90ed3d8a9bf155fae86c721ed8ee5603c4613ac47e5a80079b909727277fff31d20529b3c022e3b1a3616d639ca036586ef17d3dc9bdf1eeec5859f0022514f29b240f30c818e7fdd478f36779978c08885b2266e3cf599d0a4c27bca4615720a08e13916ec4a6ef5c943ddb25566e620726f56c5160913564d683badcc7204ec8e7080221de325a431a0680e9e108e427c7bc3807b86c78820bc9c2340709da352d953aea805a04b53f2d88f7b8fae50e5ba2de55615bc9b2e526167226e75f5c8827593281f23a65e5098e0e8e2623ee4f892bac76e6987a05832bc0b84215bb74ed83786bd55265e61ba5702170fda63e01a44d6b8a6f1740a6c03803095e5176bde18ee873dcd6380de9f325f1c436c11e3250cebceceb319a49a4e3f2f62cfd137b602e27f3974a113df414287da48afd9e8c7ef0bdb4d345313b52d662202f543f426f8271432a20186b1dc7f70d4123def1c3745c1ffe7f4017ff60e0b7486f0e8db90889a36072583ddb26dbd37778115cb56c17e19fb2d5edb847263424fb4921f224eb2d445183f9bc6541d1ac77072a6e616e6826cbbebe96af229e79620462869e51686d4d523f782b2e5b43a769403bf62f1b87512013f95d068dc04beb337f8741b471386a7746ebc7b9c7a9568ed9b590936cdc533f6f943e3a37966362b62092ce5a3c53a95b33d0ff1ac2c5800b67b8cc40d614b8df74d4114d4bb7104b545ac7d0b9eb606578fd63561578740ce7eeaa189baacd436ae15cb56c17f19fb2d5edb8472ecc4942bbc4e2bbec5c4adb791a7096298f6347c4a277364cea894234bdcf69811f31ce644bae10edc0de4ccc200a44fa0e2faa4d2eb3b28dcecc3a468efdbfa7de2728b27dfafd1a5df4803a7986cfa768fb1f9751ba1a7d47c9a6928978810d76dceb819738f4684637d3ddd2cc41e2a4585a366d4a46b32db59508f34bf5c654be7b5c86e24cca5d1013738c32ba9b6083e76e0f93c80fe712261841e546315cb56c17c19fb2d5edb847260fd338fdcecb096e26a56603082a58f5b1fb0ffb7fc8faac33589ec52ed02256aba88b2c2836433a5d146c6efe784d47da7a7ffc4256f3dce73314c0171f89e9a4f6a95f59e8460d2b65fe178daba5faf8d34d99e32a50aefbbb7ed9c9507765958870dd2e1836fe608215ca753a7125f3cb86ac32d1149cf2a144b873fc7a96914d376ed2fe88d36a609596a68d8bb76e71d1b81a8db3618271f002ff6fb5815cb56c17d19fb2d5edb8472b72d657abc1a3c2133fa45fd834d28fce3b5bdd8f111bfa61386a306aa74598ea6e5022f4aac8d9acd442c44465eb334c26d060dfc8f4f59c13f3225a5ea111f9e3e9ef4c75f40b85c43dbdd5d30970cde507cd96fa6a88970e23c1dc1cb9a1eab2f4cc16444226aff6c49dd13e030240ea32267a1699b5b8c83d05c1cc257f5028d649e2b58d96a1f59fcd42f027179e7400f2c64c3063ae1f9c496ec019eba12cb56c17219fa2dfedb847213cb56c17919fb2dfedb847211cb56c17a19fb2dfedb8472"
cur=0

while cur<len(s):
op1 = bytearray(bytes.fromhex(s[cur:cur+24]))
cur+=24
for i in range(len(op1)):
op1[i] ^= k[i + 32]
op=[int.from_bytes(op1[i:i+4],"little") for i in range(0,12,4)]
print(op)
print(list(bytes.fromhex(s[cur:cur+2*op[-1]])))
cur+=2*op[-1]

# [1, 0, 34]
# Hello, this is the remote server.

# [2, 1, 24]
# Your input is incorrect

# [2, 2, 40]
# Your input is correct , Congratulations

# [1, 0, 27]
# Please enter your password

# [3, 0, 0]
# input_something

# [2, 8, 579]
# shellcode

# [2, 4, 160]
# write_data
# ...

# 2 4
ans6=[16,228,39,34,62,210,76,123,160,154,193,42,187,247,182,24,56,14,96,90,42,203,34,122,92,138,200,54,251,176,182,81,204,224,163,100,174,224,175,17,217,221,213,1,106,249,216,65,177,197,188,59,80,142,218,4,116,200,94,94,15,76,118,122,120,243,178,46,125,204,122,75,224,202,171,61,160,167,75,33,183,223,166,32,30,67,70,15,67,153,214,126,99,2,137,54,64,31,33,29,195,194,250,63,199,229,58,24,65,112,151,50,222,214,4,49,198,233,63,121,75,110,136,75,238,186,44,83,4,188,140,116,235,8,74,118,169,178,227,107,60,202,236,78,21,212,108,112,5,61,172,34,56,123,163,32,226,180,16,85]
# 2 5
ans7=[89,0,0,0,51,0,0,0,10,0,0,0,46,0,0,0,33,0,0,0,25,0,0,0,64,0,0,0,84,0,0,0,66,0,0,0,82,0,0,0,16,0,0,0,74,0,0,0,56,0,0,0,88,0,0,0,37,0,0,0,52,0,0,0,25,0,0,0,97,0,0,0,37,0,0,0,86,0,0,0,49,0,0,0,8,0,0,0,74,0,0,0,31,0,0,0,9,0,0,0,21,0,0,0,21,0,0,0,77,0,0,0,38,0,0,0,49,0,0,0,65,0,0,0,79,0,0,0,52,0,0,0,75,0,0,0,78,0,0,0,37,0,0,0,53,0,0,0,42,0,0,0,22,0,0,0,19,0,0,0]
# 2 6
ans8=[39,34,0,0,76,123,0,0,193,42,0,0,182,24,0,0,96,90,0,0,34,122,0,0,200,54,0,0,182,81,0,0,163,100,0,0,175,17,0,0,213,1,0,0,216,65,0,0,188,59,0,0,218,4,0,0,94,94,0,0,118,122,0,0,178,46,0,0,122,75,0,0,171,61,0,0,75,33,0,0,166,32,0,0,70,15,0,0,214,126,0,0,137,54,0,0,33,29,0,0,250,63,0,0,58,24,0,0,151,50,0,0,4,49,0,0,63,121,0,0,136,75,0,0,44,83,0,0,140,116,0,0,74,118,0,0,227,107,0,0,236,78,0,0,108,112,0,0,172,34,0,0,163,32,0,0,16,85,0,0]
# 2 7
ans9=[173,228,0,0,178,210,0,0,253,154,0,0,38,248,0,0,141,14,0,0,133,203,0,0,103,138,0,0,34,177,0,0,0,225,0,0,51,225,0,0,159,221,0,0,169,249,0,0,191,197,0,0,186,142,0,0,99,200,0,0,110,76,0,0,58,243,0,0,169,204,0,0,181,202,0,0,43,168,0,0,205,223,0,0,51,67,0,0,108,153,0,0,45,2,0,0,55,31,0,0,10,195,0,0,178,229,0,0,129,112,0,0,206,214,0,0,37,234,0,0,74,110,0,0,42,187,0,0,153,188,0,0,3,9,0,0,22,179,0,0,46,202,0,0,142,212,0,0,150,61,0,0,95,123,0,0,178,180,0,0]

k4=[int.from_bytes(bytes(ans6[i:i+4]),"little") for i in range(0,160,4)]
k5=[int.from_bytes(bytes(ans7[i:i+4]),"little") for i in range(0,160,4)]

k6=[int.from_bytes(bytes(ans8[i:i+4]),"little") for i in range(0,160,4)]
k7=[int.from_bytes(bytes(ans9[i:i+4]),"little") for i in range(0,160,4)]
for i in range(40):
c = k7[i] | (k6[i] << 16)
c -= k5[i]
c ^= k4[i]
print(chr(c),end='')

Get Flag!

image-20230725141409380

Reference

https://mp.weixin.qq.com/s?__biz=Mzg3MjcwOTAwNw==&mid=2247484933&idx=1&sn=60ae6e59096e22a2b60bb7ba4e80ce04&chksm=ceea68e5f99de1f3b072baf3c069c4f1530f5c17b34811e51133d42fbfe9de4638d5b5f14071&mpshare=1&scene=23&srcid=072376b235QJm9tNCYWMZFQU&sharer_sharetime=1690091289890&sharer_shareid=9c0f9b7dd366a1431577547d077f70be#rd

DASCTF X SU
🍬
HFCTF2022
🍪

About this Post

This post is written by P.Z, licensed under CC BY-NC 4.0.