justCTF 2025 WriteUp (Reverse方向)

4.1k 词

在这里仅给出我个人解出的题目的WriteUp.

baby-goes-re

go语言程序,主函数:

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
void __golang main_main()
{
int n; // r8
error_0 err_1; // r9
int n_1; // r8
int n_2; // r8
__int64 v4; // rsi
int n_3; // r8
_slice_interface_ a; // [rsp+0h] [rbp-50h] BYREF
string_0 *tab_1; // [rsp+18h] [rbp-38h]
_QWORD v8[2]; // [rsp+20h] [rbp-30h] BYREF
_QWORD v9[2]; // [rsp+30h] [rbp-20h] BYREF
string_0 *tab; // [rsp+40h] [rbp-10h]
error_0 v11; // 0:r9.16
error_0 err_2; // 0:r9.16
error_0 err_3; // 0:r9.16
io_Writer_0 w; // 0:rax.8,8:rbx.8
io_Writer_0 w_1; // 0:rax.8,8:rbx.8
string_0 babymemory; // 0:rcx.8,8:rdi.8
_slice_interface_ v17; // 0:rcx.8,8:rdi.16

v9[0] = &RTYPE_string;
v9[1] = &off_53C6C8; // "we told the baby what the flag is, so you can just tell ask him and get the easy flag!"
w.data = os_Stdout;
w.tab = (internal_abi_ITab *)&go_itab__ptr_os_File_comma_io_Writer;
v17.array = (interface__0 *)v9;
v17.len = 1;
v17.cap = 1;
fmt_Fprintln(w, v17, n, err_1);
runtime_newobject((internal_abi_Type *)&RTYPE_string, w.data);
tab = (string_0 *)w.tab;
v8[0] = &RTYPE_string;
v8[1] = &off_53C6D8; // "Type the information to be read out loud to the baby: "
w.data = os_Stdout;
w.tab = (internal_abi_ITab *)&go_itab__ptr_os_File_comma_io_Writer;
v17.array = (interface__0 *)v8;
v17.len = 1;
v17.cap = 1;
fmt_Fprint(w, v17, n_1, v11);
a.cap = (int)&RTYPE__ptr_string;
tab_1 = tab;
w.data = os_Stdin;
w.tab = (internal_abi_ITab *)&go_itab__ptr_os_File_comma_io_Reader;
v17.array = (interface__0 *)&a.cap;
v17.len = 1;
v17.cap = 1;
fmt_Fscanln(w, v17, n_2, err_2);
if ( tab->len != 53 )
main_fail();
babymemory.str = (uint8 *)aG9epaK5CBkDrZY;
babymemory.len = 338660;
main_CheckFlag(*tab, babymemory);
a.array = (interface__0 *)&RTYPE_string;
a.len = (int)&off_53C6E8; // "*baby goes*: YAAAY"
w_1.data = os_Stdout;
w_1.tab = (internal_abi_ITab *)&go_itab__ptr_os_File_comma_io_Writer;
babymemory.str = (uint8 *)&a;
babymemory.len = 1;
v4 = 1;
fmt_Fprint(w_1, *(_slice_interface_ *)&babymemory.str, n_3, err_3);
}

加载了一个长度33万的字符串,flag长度为53,查看checkflag:

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
// main.CheckFlag
void __golang main_CheckFlag(string_0 flag, string_0 babymemory)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND]

str = babymemory.str;
s_8 = flag.len;
s = flag.str;
len = babymemory.len;
k = 0;
pos = 0;
v4 = 0;
while ( flag.len > k )
{
*(_QWORD *)&v18[1] = v4;
n0x80 = flag.str[k];
if ( n0x80 >= 0x80 )
{
*(_QWORD *)&v18[3] = pos;
runtime_decoderune(flag, k, babymemory.len, pos);
babymemory.str = str;
pos = *(_QWORD *)&v18[3];
babymemory.len = len;
v4 = *(_QWORD *)&v18[1];
n0x80 = n0x80_1;
len_1 = flag.len;
}
else
{
len_1 = k + 1;
}
len_2 = v4 + pos + 0x1337;
v9 = v4 + pos;
v10 = v4 + pos + 0x1338;
if ( babymemory.len <= len_2 )
runtime_panicIndex();
v18[0] = n0x80;
*(_QWORD *)&v18[3] = v10;
len_3 = len_1;
r = babymemory.str[v9 + 4919];
runtime_intstring((uint8 (*)[4])buf, r, babymemory);
r_1 = r;
v21 = v12;
v13 = v18[0];
r_2 = v18[0];
runtime_intstring((uint8 (*)[4])buf_, v18[0], babymemory);
if ( r_1 != r_2 || (runtime_memequal(), !v15) )
main_fail();
v4 = *(_QWORD *)&v18[1] + 51LL;
flag.str = s;
babymemory.len = len;
flag.len = s_8;
babymemory.str = str;
pos = *(_QWORD *)&v18[3];
k = len_3;
}
}

从4919开始逐字节比较偏移量,如果不同直接返回失败,其中r = babymemory.str[v9 + 4919];是要比较的字符,编写脚本爆破:

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

BINARY="./baby-goes-re"
TEMP_GDB="/tmp/temp.gdb"
INPUT_FILE="/tmp/baby_input"
FLAG_LOG="flag.log"
FLAG=""

rm -f $TEMP_GDB $INPUT_FILE
> $FLAG_LOG

for k in $(seq 0 52); do
echo "Extracting flag[$k]..."
padding_len=$((53 - k - 1))
padding=$(printf 'A%.0s' $(seq 1 $padding_len))
test_input="${FLAG}A${padding}"
test_input="${test_input}"$'\n'
printf "%s" "$test_input" > $INPUT_FILE
echo -n "Input: " && echo -n "$test_input" | xxd -p
cat > $TEMP_GDB << EOF
set confirm off
set pagination off
file $BINARY
break *0x49E085
run < $INPUT_FILE
$(for i in $(seq 1 $k); do echo "continue"; done)
python
import gdb
try:
rbx_val = int(gdb.parse_and_eval("\$rbx"))
char = chr(rbx_val & 0xFF)
print(f"flag[$k] = '{char}' (0x{rbx_val:02x})")
with open('$FLAG_LOG', 'a') as f:
f.write(char)
except Exception as e:
print(f"Error reading RBX: {e}")
end
quit
EOF

timeout 10s gdb -batch -x $TEMP_GDB > /dev/null 2>&1 || true
if [ -f $FLAG_LOG ] && [ $(wc -c < $FLAG_LOG) -ge $((k+1)) ]; then
new_char=$(tail -c1 $FLAG_LOG)
FLAG="${FLAG}${new_char}"
echo -e "\nCurrent flag: $FLAG\n"
else
echo "Failed at $k"
FLAG="${FLAG}?"
fi
done
echo "Final flag: $FLAG"
rm -f $TEMP_GDB $INPUT_FILE

爆到一半断了,又写了一个可以从已知偏移量开始爆的:

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

BINARY="./baby-goes-re"
TEMP_GDB="/tmp/temp.gdb"
INPUT_FILE="/tmp/baby_input"
FLAG_LOG="flag.log"

START_POS=22
KNOWN_PREFIX="justCTF{W3lc0m3_t0_R3v1"
TOTAL_LEN=53

FLAG="$KNOWN_PREFIX"
> $FLAG_LOG
echo -n "$FLAG" > $FLAG_LOG

for k in $(seq $START_POS $((TOTAL_LEN-1))); do
echo "Extracting flag[$k]..."
padding_len=$((TOTAL_LEN - k - 1))
padding=$(printf 'A%.0s' $(seq 1 $padding_len))
test_input="${FLAG}A${padding}"
test_input="${test_input}"$'\n'
printf "%s" "$test_input" > $INPUT_FILE
echo -n "Input: " && echo -n "$test_input" | xxd -p
cat > $TEMP_GDB << EOF
set confirm off
set pagination off
file $BINARY
break *0x49E085
run < $INPUT_FILE
$(for i in $(seq 1 $k); do echo "continue"; done)
python
import gdb
try:
rbx_val = int(gdb.parse_and_eval("\$rbx")) & 0xFF
char = chr(rbx_val)
print(f"flag[$k] = '{char}' (0x{rbx_val:02x})")
with open('$FLAG_LOG', 'a') as f:
f.write(char)
except Exception as e:
print(f"Error: {e}")
end
quit
EOF
timeout 10s gdb -batch -x $TEMP_GDB > /dev/null 2>&1 || true
FLAG=$(cat $FLAG_LOG)
if [ ${#FLAG} -ge $((k+1)) ]; then
echo -e "\nCurrent flag: $FLAG\n"
else
echo "Failed at $k"
FLAG="${FLAG}?"
echo "?" >> $FLAG_LOG
fi
done

echo "Final flag: $FLAG"
rm -f $TEMP_GDB $INPUT_FILE

justCTF{W3lc0m3_t0_R3v1NG!_Th4t_w45nt-s0-B4d-w45_1t?}

Satellite

arm架构程序,给出的流量包中有密文:
5771D410CFFE844D24B50FCBBBDC1973A7A935E5C3468242950DFCCE94794B067F876A215D96EE09

字符串搜索到主逻辑:

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
int __fastcall sub_45EC(int a1)
{
int v2; // r4
int v3; // r6
int v4; // r5
int v5; // r7
int i; // r8
int n8; // r9
int v8; // r0
char *p_Failed_to_decrypt_data_or_no_printable_characters_found.; // r1
_BYTE v11[8]; // [sp+0h] [bp-38h] BYREF
int v12; // [sp+8h] [bp-30h]
_DWORD v13[11]; // [sp+Ch] [bp-2Ch] BYREF

sub_D9A4(&unk_20000854);
sub_D9C4(&unk_20000854, "Decrypting hex data...");
v2 = *(_DWORD *)(a1 + 8) >> 1;
v3 = 0;
v4 = sub_10964(v2);
v5 = v4 - 1;
while ( v3 < v2 )
{
sub_DCE4(v13, a1, 2 * v3, 2 * v3 + 2);
*(_BYTE *)++v5 = sub_12594(v13[0], 0, 16);
++v3;
sub_DABA(v13);
}
sub_DB50(v11, 85326);
for ( i = 0; i < v2; i += 8 )
{
if ( i + 7 < v2 )
{
v13[0] = (*(unsigned __int8 *)(v4 + i + 2) << 16)
| (*(unsigned __int8 *)(v4 + i + 1) << 8)
| *(unsigned __int8 *)(v4 + i)
| (*(unsigned __int8 *)(v4 + i + 3) << 24);
v13[1] = (*(unsigned __int8 *)(v4 + i + 6) << 16)
| (*(unsigned __int8 *)(v4 + i + 5) << 8)
| *(unsigned __int8 *)(v4 + i + 4)
| (*(unsigned __int8 *)(v4 + i + 7) << 24);
sub_4594(v13, &unk_14D20);
for ( n8 = 0; n8 != 8; ++n8 )
{
if ( (unsigned __int8)(*((_BYTE *)v13 + n8) - 32) <= 0x5Eu )
sub_DC42(v11);
}
}
}
sub_10920(v4);
if ( v12 )
{
sub_DD24(v11);
sub_DC8A(v11, aSatellite, 100, 0); // "SATELLITE"
dword_200003BC = v12;
dword_2000031C = 0;
dword_20000320 = 0;
sub_42F4(&dword_20000358);
sub_DB50(v13, "Decrypted message: ");
v8 = sub_DC58(v13, v11);
sub_D9B0(&unk_20000854, v8);
sub_DABA(v13);
p_Failed_to_decrypt_data_or_no_printable_characters_found. = "Displaying on matrix...";
}
else
{
p_Failed_to_decrypt_data_or_no_printable_characters_found. = "Failed to decrypt data or no printable characters found.";
}
sub_D9C4(&unk_20000854, p_Failed_to_decrypt_data_or_no_printable_characters_found.);
return sub_DABA(v11);
}

sub_4594是一个tea解密函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 *__fastcall sub_4594(__int64 *result, __int64 *a2)
{
__int64 v2; // r2
__int64 v3; // kr00_8
__int64 v4; // kr08_8
int v5; // r1
int v6; // r4

v2 = *result;
v3 = a2[1];
v4 = *a2;
v5 = 0xC6EF3720;
do
{
HIDWORD(v2) -= (v3 + 16 * v2) ^ (HIDWORD(v3) + ((unsigned int)v2 >> 5)) ^ (v2 + v5);
v6 = (v4 + 16 * HIDWORD(v2)) ^ (HIDWORD(v4) + (HIDWORD(v2) >> 5)) ^ (HIDWORD(v2) + v5);
v5 += 0x61C88647;
LODWORD(v2) = v2 - v6;
}
while ( v5 );
*result = v2;
return result;
}

密钥为:

1
.text:00014D20 dword_14D20     DCD 0x12345678,0x9ABCDEF0,0x11111111,0x22222222

编写脚本解密:

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
def b2dle(byte_array):
return [int.from_bytes(byte_array[i:i+4], byteorder='little', signed=False) for i in range(0, len(byte_array), 4)]

def d2ble(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='little'))
return byte_list

encflag = bytearray.fromhex("5771D410CFFE844D24B50FCBBBDC1973A7A935E5C3468242950DFCCE94794B067F876A215D96EE09")
dwordenc = b2dle(encflag)
key = [0x12345678, 0x9ABCDEF0, 0x11111111, 0x22222222]

delta = 0x9E3779B9
result = []
for j in range(0, len(dwordenc)//2):
m1 = dwordenc[2*j]
m2 = dwordenc[2*j+1]
ttl = (delta * 0x20) & 0xFFFFFFFF
for j in range(0x20):
m2 = (m2 - ((key[3] + (m1 >> 5)) ^ (ttl + m1) ^ (key[2] + (m1 << 4)))) & 0xFFFFFFFF
m1 = (m1 - ((key[1] + (m2 >> 5)) ^ (ttl + m2) ^ (key[0] + (m2 << 4)))) & 0xFFFFFFFF
ttl = (ttl - delta) & 0xFFFFFFFF
result.append(m1)
result.append(m2)
flag = d2ble(result)
flagbytes = bytearray(flag)
print(flagbytes.decode())

justCTF{TheConnection_w4s_interrupted}

slowrun

程序主函数:

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
__int64 __fastcall main(int n2, char **n100, char **a3)
{
void **inp; // [rsp+18h] [rbp-18h]
void **out; // [rsp+20h] [rbp-10h]
char *ptr; // [rsp+28h] [rbp-8h]

if ( n2 == 2 )
{
inp = (void **)Load_BigNum(n100[1], (__int64)n100);
if ( inp )
{
if ( (int)BigNumValue((__int64)inp, 0) >= 0 )
{
puts("allocating memory... lots... of... memory...");
sleep(3u);
puts("warming up the CPU...");
sleep(3u);
puts("increasing fan speed...");
sleep(3u);
puts("calculating...");
out = PorcessBigNums((__int64)inp);
ptr = TransformToChar((__int64)out);
printf("flag: %s\n", ptr);
frees(out);
frees(inp);
free(ptr);
return 0;
}
else
{
fprintf(stderr, "Invalid number: %s\n", n100[1]);
frees(inp);
return 1;
}
}
else
{
fprintf(stderr, "Invalid number: %s\n", n100[1]);
return 1;
}
}
else
{
fprintf(stderr, "Usage: %s <number>\n", *n100);
return 1;
}
}

带入一个整数(docker中给出是13337)进行运算,查看运算方式:

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
void **__fastcall PorcessBigNums(__int64 inp)
{
void **ptr; // [rsp+10h] [rbp-20h]
void **num1; // [rsp+18h] [rbp-18h]
void **num2; // [rsp+20h] [rbp-10h]
__int64 ptr_1; // [rsp+28h] [rbp-8h]

ptr = (void **)CalcResultFuncA(inp);
if ( (int)mpz_NumValue(inp, 100) <= 0 )
return ptr;
num1 = (void **)Load_BigNum((char *)BigNum1, 100);
num2 = (void **)Load_BigNum((char *)BigNum2, 100);
ptr_1 = mpz_modadd((__int64)ptr, (__int64)num1);
mpz_add(ptr_1, (__int64)num2);
frees(num1);
frees(num2);
frees(ptr);
return (void **)ptr_1;
}

_BYTE *__fastcall CalcResultFuncA(__int64 ptr)
{
int v2; // eax
_BYTE *ptr_1; // [rsp+18h] [rbp-28h]
void **ptr_2; // [rsp+20h] [rbp-20h]
void **ptr_3; // [rsp+28h] [rbp-18h]
void **ptr_4; // [rsp+30h] [rbp-10h]
void **ptra; // [rsp+38h] [rbp-8h]

if ( !(unsigned int)mpz_NumValue(ptr, 0) )
return mpz_num(2);
if ( (int)mpz_NumValue(ptr, 1) <= 0 )
return mpz_num(1);
ptr_1 = mpz_num(0);
ptr_2 = (void **)mpz_num(73);
mpz_mul_((__int64)ptr_2, ptr);
mpz_mul_((__int64)ptr_2, ptr);
mpz_mul_((__int64)ptr_2, ptr);
mpz_mul_((__int64)ptr_2, ptr);
mpz_mul_((__int64)ptr_2, ptr);
ptr_4 = (void **)mpz_num(8);
mpz_mul_((__int64)ptr_4, ptr);
mpz_mul_((__int64)ptr_4, ptr);
mpz_mul_((__int64)ptr_4, ptr);
v2 = mpz_SignalLength(ptr);
ptra = (void **)mpz_ReAllocateSize(ptr, v2 + 1);
mpz_sub((__int64)ptra, 1);
ptr_3 = (void **)CalcResultFuncB((__int64)ptra);
mpz_add((__int64)ptr_1, ptr);
mpz_sub((__int64)ptr_1, 4);
mpz_add((__int64)ptr_1, (__int64)ptr_2);
mpz_add((__int64)ptr_1, (__int64)ptr_3);
mpz_add((__int64)ptr_1, (__int64)ptr_4);
frees(ptr_2);
frees(ptr_3);
frees(ptr_4);
frees(ptra);
return ptr_1;
}

_BYTE *__fastcall CalcResultFuncB(__int64 ptr)
{
int v2; // eax
int v3; // eax
int v4; // eax
_BYTE *ptr_3; // [rsp+10h] [rbp-40h]
_BYTE *ptr_4; // [rsp+18h] [rbp-38h]
void **ptr_5; // [rsp+20h] [rbp-30h]
void **ptr_6; // [rsp+28h] [rbp-28h]
void **ptr_7; // [rsp+30h] [rbp-20h]
void **ptra; // [rsp+38h] [rbp-18h]
void **ptr_1; // [rsp+40h] [rbp-10h]
void **ptr_2; // [rsp+48h] [rbp-8h]

if ( (int)mpz_NumValue(ptr, 1) <= 0 )
return mpz_num(1);
ptr_3 = mpz_num(0);
v2 = mpz_SignalLength(ptr);
ptra = (void **)mpz_ReAllocateSize(ptr, v2 + 1);
mpz_sub((__int64)ptra, 1);
ptr_4 = CalcResult((__int64)ptra);
v3 = mpz_SignalLength(ptr);
ptr_1 = (void **)mpz_ReAllocateSize(ptr, v3 + 1);
mpz_sub((__int64)ptr_1, 2);
ptr_5 = (void **)CalcResult((__int64)ptr_1);
mpz_mul((__int64)ptr_5, 3);
v4 = mpz_SignalLength(ptr);
ptr_2 = (void **)mpz_ReAllocateSize(ptr, v4 + 1);
mpz_sub((__int64)ptr_2, 3);
ptr_6 = (void **)CalcResult((__int64)ptr_2);
mpz_mul((__int64)ptr_6, 5);
ptr_7 = (void **)mpz_num(3);
mpz_mul_((__int64)ptr_7, ptr);
mpz_mul_((__int64)ptr_7, ptr);
mpz_mul_((__int64)ptr_7, ptr);
mpz_mul_((__int64)ptr_7, ptr);
mpz_add((__int64)ptr_3, (__int64)ptr_4);
mpz_add((__int64)ptr_3, (__int64)ptr_5);
mpz_sub_((__int64)ptr_3, (__int64)ptr_6);
mpz_add((__int64)ptr_3, (__int64)ptr_7);
frees(ptr_5);
frees(ptr_6);
frees(ptr_7);
frees(ptra);
frees(ptr_1);
frees(ptr_2);
return ptr_3;
}

其中CalcResultFuncACalcResultFuncB是一个递归的多项式计算,可以简写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
def A(n):
if n == 0:
return 2
elif n <= 1:
return 1
else:
return 73 * pow(n, 5) + 8 * pow(n, 3) + n - 4 + B(n - 1)

def B(n):
if n <= 1:
return 1
else:
return 3 * pow(n, 4) + A(n - 1) + 3 * A(n - 2) - 5 * A(n - 3)

由于13337极大,因此不可能用递归来求解,应当从0开始向上递推关系式直到13337,编写脚本:

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
from Crypto.Util.number import *

DictA = {}
DictB = {}

def getValue(DictChoice, name):
Dict = DictA if DictChoice == "A" else DictB
if name in Dict:
val = Dict[name]
else:
val = A(int(name[1:])) if DictChoice == "A" else B(int(name[1:]))
return val

def A(n):
if n == 0:
return 2
elif n <= 1:
return 1
else:
res = 73 * pow(n, 5) + 8 * pow(n, 3) + n - 4 + getValue("B", f"B{n-1}")
DictA[f"A{n}"] = res
return res

def B(n):
if n <= 1:
return 1
else:
res = 3 * pow(n, 4) + getValue("A", f"A{n-1}") + 3 * getValue("A", f"A{n-2}") - 5 * getValue("A", f"A{n-3}")
DictB[f"B{n}"] = res
return res

for i in range(13337): A(i)

num1 = 12871709638832864416674237492708808074465131233250468097567609804146306910998417223517320307084142930385333755674444057095681119233485961920941215894136808839080569675919567597231
num2 = 805129649450289111374098215345043938348341847793365469885914570440914675704049341968773123354333661444680237475120349087680072042981825910641377252873686258216120616639500404381

print(long_to_bytes(A(13337) % num1 + num2).decode())

justCTF{1n_0rd3r_70_und3r574nd_r3cur510n_y0u_h4v3_t0_und3r574nd_r3cur510n}

6pack

sub_4C9E00 main.main

sub_4C9D20 初始化,监听ipv6

sub_4C9EE0 关闭网络连接

sub_4C91C0 主逻辑

sub_4C9CC0 sub_4C8EC0 sub_4C8A60 输入解析

sub_4C9C60 sub_4CA820 网络接收循环

sub_4CA640 sub_4CA0E0 网络发送循环

sub_4C8820 (main_mainlogic_generaterandomnum) 被用于构造一个随机的ipv6结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
result = runtime_newobject(&RTYPE_IPv6Header);
*result = 6; // Version = 6
result[1] = math_rand_v2_UintN(32); // Traffic Class (random)
*((_DWORD *)result + 1) = flowLabel; // Flow Label
*((_WORD *)result + 4) = math_rand_v2_UintN(4096); // Payload Len (random)
result[10] = math_rand_v2_UintN(64); // Next Header (random)
result[11] = math_rand_v2_UintN(32) + 10; // Hop Limit (random + 10)
*((_QWORD *)result + 2) = srcIp_ptr; // Source IP
*((_QWORD *)result + 3) = srcIp_len; // Source IP len
*((_QWORD *)result + 4) = srcIp_cap; // Source IP cap
*((_QWORD *)result + 5) = dstAddr_ptr; // Destination IP
*((_QWORD *)result + 6) = dstAddr_len;
*((_QWORD *)result + 7) = dstAddr_cap;

之后程序会解析flowlabel,65536为数据,65537时开始解压缩

wireshark过滤器ipv6.flow == 0x10000、ipv6.flow == 0x10001

对于给出的流量,经过分析发现长度均为94,消息应该全部在 flow label 中,编写脚本提取

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

capture = pyshark.FileCapture(input_file='dump.pcapng', use_json=True, include_raw=True)

state = False
for packet in capture:
data = packet.get_raw_packet()
if len(data) != 94: continue

chunk = data[54:62]
assert (chunk[0] >> 4) == 6
assert 0 <= (((chunk[0] & 0xF) << 4) | (chunk[1] >> 4)) < 32
flow_label = ((chunk[1] & 0xF) << 16) | (chunk[2] << 8) | chunk[3]
assert 0 <= chunk[4] * 256 + chunk[5] < 4096
assert 0 <= chunk[6] < 64
assert 10 <= chunk[7] < 10 + 32

if data[53] in [2]:
if state == False:
assert flow_label == 0x10000
state = True
message = b""
elif flow_label == 0x10001:
state = False
print(message.hex())
else:
message += flow_label.to_bytes(2, "little")

提取后发现可以raw inflate,双方发送的信息相同

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
hey man how's it going
yo all good just chilling at home
same here been a long day at work
had some pizza and just vibing
nice that sounds chill
i just got done with some coding stuff
cool check this out
i discovered heroes of might and magic 3 and its epic
no way that classic game is so lit
yeah its totally dope and got mad retro vibes
spent the whole night playing and having a blast
omg that is insane i love that nostalgia
remember the days of pixel art and epic battles
exactly its like stepping back in time
the gameplay is tight and the music is fire
for real i get major chill vibes from that game
havent played in ages but now im hyped
we should totally squad up for a game night
thinking of a weekend marathon with the crew
we can invite a few friends and just game all night
hell yeah and we can talk about our coding wins
plus share some dank memes too
omg memes are life
btw did you catch that new tech release
nah been too busy gaming and chilling
caught a bit of it on social tho
same here just skimming news while gaming
love how we can code and game at the same time
preach that its all about balance
work hard play harder right
exactly man its all about good vibes
letting loose when the day is done is the move
totally get that its a lifestyle
gotta run some errands but chatting is fun
no worries catch you later
im off to try a new mod for heroes of might and magic 3
lol that sounds rad have fun
def gonna flex my strategy moves
better check out my brand new flag checker
going to send it in chunks
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
uncompressed file checksum crc32: 0xdc38757d
ok, will let you know once I find matching input
cool
brb

base64编码的内容是一个exe文件,有UPX壳,脱壳后

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v3; // ebx
void *lpAddress; // rsi
const char *v7; // rbp
int v8; // eax
void *lpAddress_1; // rax
int v10; // eax
const char *p_nope; // rcx
unsigned __int16 v13; // [rsp+2Eh] [rbp-4Ah] BYREF
SIZE_T dwSize; // [rsp+30h] [rbp-48h] BYREF
void *Src; // [rsp+38h] [rbp-40h]
_QWORD p_n2[7]; // [rsp+40h] [rbp-38h] BYREF

init();
if ( argc != 3 )
{
LABEL_9:
VirtualFree(lpAddress, 0, 0x8000u);
goto LABEL_11;
}
v7 = argv[1];
v13 = atoi(argv[2]);
dwSize = 0;
Src = 0;
v3 = open6pack((__int64)&dwSize);
if ( !v3 )
{
LODWORD(p_n2[0]) = 2;
p_n2[1] = &v13;
if ( v13 >> 11 == 15 )
{
v8 = extrfunc((__int64)&dwSize, (__int64)p_n2);
if ( !v8 )
{
lpAddress_1 = VirtualAlloc(0, (unsigned int)dwSize, 0x3000u, 0x40u);
lpAddress = lpAddress_1;
if ( !lpAddress_1 )
goto LABEL_11;
memcpy(lpAddress_1, Src, (unsigned int)dwSize);
v10 = ((__int64 (__fastcall *)(const char *))lpAddress)(v7);
p_nope = "correct";
v3 = v10;
if ( v10 )
p_nope = "nope";
puts(p_nope);
goto LABEL_9;
}
v3 = v8;
}
}
LABEL_11:
if ( Src )
free(Src);
return v3;
}

要提供两个参数,第一个是flag,第二个是一个整数,满足右移11之后是15(30720~32767),open6pack:

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
__int64 __fastcall open6pack(__int64 p_dwSize)
{
Stream *Stream; // rax
Stream *Stream_1; // rbx
void *Buffer_1; // rsi
int v5; // r14d
void *v6; // rax
int Offset_1; // edx
_BYTE Buffer[40]; // [rsp+20h] [rbp-F0h] BYREF
int v10; // [rsp+48h] [rbp-C8h]
unsigned __int16 v11; // [rsp+5Ah] [rbp-B6h]
unsigned __int16 v12; // [rsp+5Ch] [rbp-B4h]
unsigned __int16 v13; // [rsp+5Eh] [rbp-B2h]
_BYTE Buffer_[24]; // [rsp+60h] [rbp-B0h] BYREF
int Offset; // [rsp+78h] [rbp-98h]
size_t Size; // [rsp+80h] [rbp-90h]
_DWORD Buffer__1[8]; // [rsp+A0h] [rbp-70h] BYREF
size_t ElementSize; // [rsp+C0h] [rbp-50h]

Stream = fopen("./6-pack", "rb");
if ( !Stream )
return 1;
Stream_1 = Stream;
fread(Buffer, 0x40u, 1u, Stream);
if ( fseek(Stream_1, v10 + v11 * v13, 0) < 0 )
goto LABEL_3;
fread(Buffer_, 0x40u, 1u, Stream_1);
Buffer_1 = malloc(Size);
if ( fseek(Stream_1, Offset, 0) < 0 )
goto LABEL_3;
v5 = 0;
fread(Buffer_1, Size, 1u, Stream_1);
while ( 1 )
{
if ( v12 <= v5 )
goto LABEL_12;
if ( fseek(Stream_1, v10 + v5 * v11, 0) < 0 )
goto LABEL_3;
fread(Buffer__1, 0x40u, 1u, Stream_1);
if ( !strcmp((const char *)Buffer_1 + Buffer__1[0], ".go.runtimeinfo") )
break;
++v5;
}
v6 = malloc(ElementSize);
Offset_1 = Buffer__1[6];
*(_QWORD *)(p_dwSize + 8) = v6;
if ( fseek(Stream_1, Offset_1, 0) < 0 )
LABEL_3:
exit(1);
fread(*(void **)(p_dwSize + 8), ElementSize, 1u, Stream_1);
*(_DWORD *)p_dwSize = ElementSize;
LABEL_12:
free(Buffer_1);
fclose(Stream_1);
return 0;
}

之后会进入下方的extrfunc:

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 extrfunc(__int64 p_dwSize, __int64 p_n2)
{
HMODULE hModule; // rax
FARPROC SystemFunction033; // rax

hModule = LoadLibraryA("advapi32.dll");
if ( !hModule )
return 1;
SystemFunction033 = GetProcAddress(hModule, "SystemFunction033");
if ( !SystemFunction033 )
return 1;
((void (__fastcall *)(__int64, __int64))SystemFunction033)(p_dwSize, p_n2);
return 0;
}

__int64 __fastcall cryptsp_SystemFunction032(unsigned int *a1, unsigned int *a2)
{
__int64 buf; // [rsp+30h] [rbp+8h] BYREF

buf = 0;
if ( *a1 )
{
sub_7FFA86A17858(&buf);
sub_7FFA86A177F8(&buf, *a1, *((_QWORD *)a1 + 1));
sub_7FFA86A170FF();
memset(&buf, 0, sizeof(buf));
}
return 0;
}

在这里用系统库函数解密shellcode,并在之后主函数中执行,尝试爆破30720~32767:

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
import subprocess
import threading
import queue
import time

EXE_PATH = "unupx.exe"
ARG1 = "flag{test}"
START_KEY = 30720
END_KEY = 32767
NUM_THREADS = 32
TIMEOUT = 2 # 超时时间(秒)

result_queue = queue.Queue()

def run_exe_with_key(key):
try:
cmd = [EXE_PATH, ARG1, str(key)]
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=TIMEOUT
)
output = result.stdout + result.stderr
if "nope" in output:
status = "nope"
elif "correct" in output:
status = "correct"
else:
status = "no_output"
except Exception:
status = "timeout_or_error"

result_queue.put((key, status))

def worker(key_queue):
while True:
try:
key = key_queue.get_nowait()
print(f"Testing key: {key}")
run_exe_with_key(key)
key_queue.task_done()
except queue.Empty:
break

def main():
key_queue = queue.Queue()
for key in range(START_KEY, END_KEY + 1):
key_queue.put(key)

threads = []
for _ in range(NUM_THREADS):
t = threading.Thread(target=worker, args=(key_queue,))
t.start()
threads.append(t)

for t in threads:
t.join()

results = []
while not result_queue.empty():
results.append(result_queue.get())

results.sort()

for key, status in results:
if status == "nope":
print(f"Key {key}: nope")

nope_keys = [k for k, s in results if s == "nope"]
if nope_keys:
print("nope keys:", ','.join(map(str, nope_keys)))
else:
print("no nope keys found")

if __name__ == "__main__":
main()

找到合适的密钥:31057,31076,31114,31118,31299,31337,31545,31749,31855,32031,32041,32530,32566,32683

这里面有一些仍然是不对的,需要筛选(因为解密有可能会随机解出来一个return,但其实还是不正确的)

经过测试发现31337是正确的密钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 __fastcall sub_1FCDB8F0000(_BYTE *a1)
{
if ( sub_1FCDB8F002D(a1) != 36 )
return 1;
sub_1FCDB8F0046(); //将输入的字符串倒过来
sub_1FCDB8F006C();
return (*(__int64 (__fastcall **)(_BYTE *, __int64))SMC_func)(a1, 36);
}

void sub_1FCDB8F006C()
{
__int64 n1792; // rcx

n1792 = 1792;
do
SMC_func[--n1792] ^= 0x17u;
while ( n1792 );
}

flag长度为36,输入会被倒过来带入一个SMC自解密函数:

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
_BOOL8 __fastcall sub_1FDC4660100(__int64 index_1, unsigned __int64 length_1)
{
void (__fastcall *kernelbase_LoadLibraryA)(char *); // rax
__int64 inp; // rcx
__int64 cmpres; // rax
__int64 cmpresult; // [rsp+28h] [rbp-48h]
__int64 out[4]; // [rsp+30h] [rbp-40h] BYREF
unsigned __int64 rounds; // [rsp+50h] [rbp-20h]
__int64 tmp; // [rsp+58h] [rbp-18h]
unsigned __int64 length; // [rsp+60h] [rbp-10h]
__int64 index; // [rsp+68h] [rbp-8h]

index = index_1;
length = length_1;
tmp = 0;
rounds = 0;
memset(out, 0, sizeof(out));
cmpresult = 0;
LOBYTE(kernelbase_LoadLibraryA) = FindFunctionHashTable(
NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink[2].Flink,
dword_1FDC4660253);
kernelbase_LoadLibraryA(aBcryptDll); // "bcrypt.dll"
rounds = length / 3;
do
{
inp = index;
index += 3;
sub_1FDC466026F(inp, (__int64)out);
cmpres = tmp;
LOBYTE(cmpres) = memcmp(&qword_1FDC46603D0[tmp], out, 0x20u) == 0;
cmpresult += cmpres;
tmp += 4;
--rounds;
}
while ( rounds );
return cmpresult != 12;
}

3字节带入一次循环,然后进行sha256,再和密文比较,密文:

1
2
3
4
5
6
7
8
9
10
11
12
204001DE61EE2B59F771F53FEB640F350932C5CDE699E23B27662995D4BD0416
73FE4A5E486AE1F4D033C595C5AAC79E6AFEAE7A28B7829C3EA97F0C7475FF30
A38DF01B42FE1009F1DA3C3BF7787BEACD5CE1743E6D19F6D46D096FD0BBA90A
677562C4990949FB8E8C2F8166F80D8151A4A1C0E0679AC24D454633C1709C29
16DB9AE9D8A6B5DC7616FB3B0B74E287E492E7D295961A0A14B9EE935C825D59
AAC513EA306B59B9AA9AF987D390E38DF0928918B25656C180C235B9AF7BE296
B7CF5A8D7EB93D4D825A400FA173020A0D5CD34A8C2F4F818D01CA2BD991C215
487D3CFD82C3915466513E097C6B00593D8C677CE54B2A292FDE8B14F88CCB89
933130B10131093C4463F7C372BA435C091A87A9B77803853F2C5AB5A3F33CAA
547D5E2A740B5D7D7C94C9B9333C8183E13A858A528C8188FDDD2FE187A07CC0
7C5CD81D84046CAAC335C2AFD9673729BA0FBA09FB7CB225FEDB6499BE5EFC29
EE340EA3ECF77D89247E264015C071D8BBD66B30FB49479A55C9225282B19287

justCTF{ipv6_ch4t___h0w_b4d_1s_th4t}