SECCON 2022 Write up
Baby cmp [rev]
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
int __cdecl main(int argc, const char **argv, const char **envp)
{
const char *v5; // r12
size_t v10; // rax
size_t v11; // rdi
unsigned __int64 v12; // rcx
const char *v13; // rsi
__int64 v14; // rax
unsigned __int64 v15; // rdx
int v16; // er12
__m128i v18; // [rsp+0h] [rbp-68h]
char v19[8]; // [rsp+10h] [rbp-58h] BYREF
__m128i v20; // [rsp+20h] [rbp-48h]
__m128i v21; // [rsp+30h] [rbp-38h]
int v22; // [rsp+40h] [rbp-28h]
unsigned __int64 v23; // [rsp+48h] [rbp-20h]
v23 = __readfsqword(0x28u);
_RAX = 0LL;
if ( argc <= 1 )
{
v16 = 1;
__printf_chk(1LL, "Usage: %s FLAG\n", *argv);
}
else
{
v5 = argv[1];
__asm { cpuid }
v22 = 3672641;
strcpy(v19, "N 2022");
v20 = _mm_load_si128((const __m128i *)&xmmword_3140);
v21 = _mm_load_si128((const __m128i *)&xmmword_3150);
v18 = _mm_load_si128((const __m128i *)&xmmword_3160);
v10 = strlen(v5);
v11 = v10;
if ( v10 )
{
*v5 ^= 0x57u;
v12 = 1LL;
if ( v10 != 1 )
{
do
{
v13 = &argv[1][v12];
v14 = v12 / 0x16
+ 2 * (v12 / 0x16 + (((0x2E8BA2E8BA2E8BA3LL * (unsigned __int128)v12) >> 64) & 0xFFFFFFFFFFFFFFFCLL));
v15 = v12++;
*v13 ^= v18.m128i_u8[v15 - 2 * v14];
}
while ( v11 != v12 );
}
v5 = argv[1];
}
if ( *(_OWORD *)&v20 == *(_OWORD *)v5 && *(_OWORD *)&v21 == *((_OWORD *)v5 + 1) && *((_DWORD *)v5 + 8) == v22 )
{
v16 = 0;
puts("Correct!");
}
else
{
v16 = 0;
puts("Wrong...");
}
}
return v16;
}
비교해주는 부분의 코드는 실제로 아래와 같이 값을 비교해주기 때문에 사용자의 입력값을 연산한 값과 binary에 미리 저장되어 있는 v20, v21, v22와 비교해서 같으면 Correct, 아니면 Wrong을 출력합니다.
1
2
3
4
5
6
mov rax, [r12]
mov rdx, [r12+8]
xor rax, qword ptr [rsp+68h+var_48]
xor rdx, qword ptr [rsp+68h+var_48+8]
or rdx, rax
jz short loc_129E
역 연산도 가능하겠지만 저 같은 경우는 z3 모듈을 이용해서 간단히 풀었습니다.
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
from z3 import *
v18 = [0x57,0x65,0x6C,0x63,0x6F,0x6D,0x65,0x20,0x74,0x6F,0x20,0x53,0x45,0x43,0x43,0x4F, 0x4e, 0x20, 0x32, 0x30, 0x32, 0x32]
v20 = [0x04,0x20,0x2F,0x20,0x20,0x23,0x1E,0x59,0x44,0x1A,0x7F,0x35,0x75,0x36,0x2D,0x2B]
v21 = [0x11,0x17,0x5A,0x03,0x6D,0x50,0x36,0x07,0x15,0x3C,0x09,0x01,0x04,0x47,0x2B,0x36]
v22 = [0x41, 0x0a, 0x38]
table = v20 + v21 + v22
value_1 = 0x2E8BA2E8BA2E8BA3
value_2 = 0xFFFFFFFFFFFFFFFC
length = len(table)
def solve_rev(cmp, len):
x = [BitVec(f'x{i}', 8) for i in range(len)]
s = Solver()
s.insert(x[0] ^ 0x57 == cmp[0])
v12 = 1
while v12 != len:
v14 = v12 // 0x16 + 2 * (v12 // 0x16 + (((value_1 * v12) >> 64) & value_2))
fn1 = x[v12] ^ v18[v12 - 2 * v14] == cmp[v12]
s.add(fn1)
v12 += 1
s.check()
m = s.model()
ret = [m[i].as_long() for i in x]
for i in ret:
print(chr(i), end='')
solve_rev(table, length)
#solve_rev(v21, length)
flag
SECCON{y0u_f0und_7h3_baby_flag_YaY}
This post is licensed under CC BY 4.0 by the author.