2022 Whitehat Contest Final Writeup

OTP Gen

이 문제는 master_key 파일에 써있는 값을 불러와서 연산을 한 값이 Description에 나타나 있다. 즉, master_key에는 우리가 얻어야할 flag 값이 써있다.

1
2
3
4
5
적국이 사용하는 OTP 생성기를 입수하는데 성공하였다.
주어진 OTP 생성기를 분석하여 마스터 키를 복구하라.
root@ubuntu:/# ./otpgen ./master_key
Mon Oct 24 10:37:39 2022: 13154447211896390350-13431592849769172675-8204214703590688774
root@ubuntu:/# echo "FLAG{`cat master_key`}" > ./flag

main함수를 분석해보면 info.txt, infolock.txt가 파일이 존하면 삭제해주고, master_key 8바이트, 8바이트씩 읽어온다.
child process들을

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
signed __int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 times; // r13
__int64 backflag; // r12
__int64 frontflag; // rbx
char *time; // rax
__pid_t v8; // [rsp+14h] [rbp-7Ch] BYREF
int i; // [rsp+18h] [rbp-78h]
int j; // [rsp+1Ch] [rbp-74h]
int fd; // [rsp+20h] [rbp-70h]
int v12; // [rsp+24h] [rbp-6Ch]
__int64 masterkey; // [rsp+28h] [rbp-68h] BYREF
__int64 masterkey2; // [rsp+30h] [rbp-60h] BYREF
__int64 v15; // [rsp+38h] [rbp-58h] BYREF
time_t timer; // [rsp+40h] [rbp-50h] BYREF
FILE *infotxtfd; // [rsp+48h] [rbp-48h]
_QWORD buf[8]; // [rsp+50h] [rbp-40h] BYREF

buf[3] = __readfsqword(0x28u);
if ( a1 == 2 )
{
unlink(infotxt);
unlink(infolock);
fd = open(a2[1], 0);
if ( fd == -1 )
exit(1);
if ( read(fd, buf, 0x10uLL) != 16 )
exit(1);
close(fd);
masterkey = buf[0];
masterkey2 = buf[1];
for ( i = 0; i <= 32; ++i )
{
v8 = fork();
if ( !v8 )
create_thread(i);
thread[i] = v8;
}
timer = ::time(0LL);
while ( 1 )
{
v12 = open(infolock, 194, 292LL);
if ( v12 != -1 )
break;
sched_yield();
}
infotxtfd = fopen(infotxt, "w");
fwrite("0 0\n", 1uLL, 4uLL, infotxtfd);
fprintf(infotxtfd, "%llx %llx %llx\n", masterkey, masterkey2, timer);
fclose(infotxtfd);
close(v12);
unlink(infolock);
for ( j = 0; j <= 32; ++j )
waitpid(thread[j], 0LL, 0);
infotxtfd = fopen(infotxt, "r");
fscanf(infotxtfd, "%d %d\n", &v8, &v8);
fscanf(infotxtfd, "%llx %llx %llx\n", &masterkey, &masterkey2, &v15);
times = v15;
backflag = masterkey2;
frontflag = masterkey;
time = ctime(&timer);
printf("%.24s: %llu-%llu-%llu\n", time, frontflag, backflag, times);
unlink(infotxt);
unlink(infolock);
return 0LL;
}
else
{
printf("Usage: %s [seed file]\n", *a2);
return 1LL;
}
}
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
void __fastcall __noreturn create_thread(int idx)
{
int highidx; // [rsp+18h] [rbp-48h] BYREF
int lowidx_; // [rsp+1Ch] [rbp-44h] BYREF
int fd; // [rsp+20h] [rbp-40h]
unsigned int lowidx; // [rsp+24h] [rbp-3Ch]
__int64 front; // [rsp+28h] [rbp-38h] BYREF
__int64 back; // [rsp+30h] [rbp-30h] BYREF
__int64 timestamp; // [rsp+38h] [rbp-28h] BYREF
FILE *stream; // [rsp+40h] [rbp-20h]
_DWORD *v9; // [rsp+48h] [rbp-18h]
__int64 v10; // [rsp+50h] [rbp-10h]
unsigned __int64 v11; // [rsp+58h] [rbp-8h]

v11 = __readfsqword(0x28u);
mprotect((exchange & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7);// rwx
v9 = memmem(exchange, 0x1000uLL, &nops, 4uLL);// \x90\x90\x90\x90 find ; return start address
*v9 += table[idx];
mprotect((exchange & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 5);// rx
while ( 1 )
{
while ( 1 )
{
fd = open(infolock, 194, 292LL);
if ( fd != -1 )
break;
sched_yield();
}
stream = fopen(infotxt, "a+");
if ( stream )
{
fseek(stream, 0LL, 0);
if ( fscanf(stream, "%d %d\n", &highidx, &lowidx_) == 2 )
{
if ( highidx == 131 )
{
fclose(stream);
close(fd);
unlink(infolock);
exit(1);
}
if ( idx == lowidx_ && fscanf(stream, "%llx %llx %llx\n", &front, &back, &timestamp) == 3 )
{
exchange(&front, &back, &timestamp);
v10 = idx;
lowidx = (34LL * idx + 17) % 0x21uLL;
fclose(stream);
stream = fopen(infotxt, "w");
fprintf(stream, "%d %d\n", (highidx + 1), lowidx);
fprintf(stream, "%llx %llx %llx\n", front, back, timestamp);
}
}
}
fclose(stream);
close(fd);
unlink(infolock);
sleep(1u);
}
}
1
2
3
4
5
6
7
8
9
from pwn import *
context.arch = 'amd64'
table = [0x004166B8, 0x813130B8, 0x873930B8, 0x004066B9, 0x813030B9, 0x873830B9, 0x004166B9, 0x813130B9, 0x873930B9, 0x003070BC, 0x003098BC, 0x0030A0BC, 0x0030F6BC, 0x003870BC, 0x003898BC, 0x0038A0BC, 0x0038F6BC, 0x003770B9, 0x003798B9, 0x0037A0B9, 0x0037F6B9, 0x003770BD, 0x003798BD, 0x0037A0BD, 0x0037F6BD, 0x003870B9, 0x003898B9, 0x0038A0B9, 0x0038F6B9, 0x003070BD, 0x003098BD, 0x0030A0BD, 0x0030F6BD]

nop = 0x90909090

for i in range(len(table)):
table[i] += nop
print(disasm(p32(table[i] & 0xffffffff)))
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
0:   48 f7 d1                not    rcx
3: 90 nop
0: 48 c1 c1 11 rol rcx, 0x11
0: 48 c1 c9 17 ror rcx, 0x17
0: 49 f7 d0 not r8
3: 90 nop
0: 49 c1 c0 11 rol r8, 0x11
0: 49 c1 c8 17 ror r8, 0x17
0: 49 f7 d1 not r9
3: 90 nop
0: 49 c1 c1 11 rol r9, 0x11
0: 49 c1 c9 17 ror r9, 0x17
0: 4c 01 c1 add rcx, r8
3: 90 nop
0: 4c 29 c1 sub rcx, r8
3: 90 nop
0: 4c 31 c1 xor rcx, r8
3: 90 nop
0: 4c 87 c1 xchg rcx, r8
3: 90 nop
0: 4c 01 c9 add rcx, r9
3: 90 nop
0: 4c 29 c9 sub rcx, r9
3: 90 nop
0: 4c 31 c9 xor rcx, r9
3: 90 nop
0: 4c 87 c9 xchg rcx, r9
3: 90 nop
0: 49 01 c8 add r8, rcx
3: 90 nop
0: 49 29 c8 sub r8, rcx
3: 90 nop
0: 49 31 c8 xor r8, rcx
3: 90 nop
0: 49 87 c8 xchg r8, rcx
3: 90 nop
0: 4d 01 c8 add r8, r9
3: 90 nop
0: 4d 29 c8 sub r8, r9
3: 90 nop
0: 4d 31 c8 xor r8, r9
3: 90 nop
0: 4d 87 c8 xchg r8, r9
3: 90 nop
0: 49 01 c9 add r9, rcx
3: 90 nop
0: 49 29 c9 sub r9, rcx
3: 90 nop
0: 49 31 c9 xor r9, rcx
3: 90 nop
0: 49 87 c9 xchg r9, rcx
3: 90 nop
0: 4d 01 c1 add r9, r8
3: 90 nop
0: 4d 29 c1 sub r9, r8
3: 90 nop
0: 4d 31 c1 xor r9, r8
3: 90 nop
0: 4d 87 c1 xchg r9, r8
3: 90 nop
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import binascii

def _rol(val, bits, bit_size):
return (val << bits % bit_size) & (2 ** bit_size - 1) | \
((val & (2 ** bit_size - 1)) >> (bit_size - (bits % bit_size)))

def _ror(val, bits, bit_size):
return ((val & (2 ** bit_size - 1)) >> bits % bit_size) | \
(val << (bit_size - (bits % bit_size)) & (2 ** bit_size - 1))

__ROR4__ = lambda val, bits: _ror(val, bits, 32)
__ROR8__ = lambda val, bits: _ror(val, bits, 64)
__ROL4__ = lambda val, bits: _rol(val, bits, 32)
__ROL8__ = lambda val, bits: _rol(val, bits, 64)

def f0(rcx, r9, r8):
rcx = ((1 << 64) - rcx - 1)
return (rcx, r9, r8)

def f1(rcx, r9, r8):
rcx = __ROR8__(rcx, 0x11)
return (rcx, r9, r8)

def f2(rcx, r9, r8):
rcx = __ROL8__(rcx, 0x17)
return (rcx, r9, r8)

def f3(rcx, r9, r8):
r8 = ((1 << 64) - r8 - 1)
return (rcx, r9, r8)

def f4(rcx, r9, r8):
r8 = __ROR8__(r8, 0x11)
return (rcx, r9, r8)

def f5(rcx, r9, r8):
r8 = __ROL8__(r8, 0x17)
return (rcx, r9, r8)

def f6(rcx, r9, r8):
r9 = ((1 << 64) - r9 - 1)
return (rcx, r9, r8)

def f7(rcx, r9, r8):
r9 = __ROR8__(r9, 0x11)
return (rcx, r9, r8)

def f8(rcx, r9, r8):
r9 = __ROL8__(r9, 0x17)
return (rcx, r9, r8)

def f9(rcx, r9, r8):
rcx -= r8
rcx &= 0xffffffffffffffff
return (rcx, r9, r8)

def f10(rcx, r9, r8):
rcx += r8
rcx &= 0xffffffffffffffff
return (rcx, r9, r8)

def f11(rcx, r9, r8):
rcx ^= r8
return (rcx, r9, r8)

def f12(rcx, r9, r8):
r8, rcx = rcx, r8
return (rcx, r9, r8)

def f13(rcx, r9, r8):
rcx -= r9
rcx &= 0xffffffffffffffff
return (rcx, r9, r8)

def f14(rcx, r9, r8):
rcx += r9
rcx &= 0xffffffffffffffff
return (rcx, r9, r8)

def f15(rcx, r9, r8):
rcx ^= r9
return (rcx, r9, r8)

def f16(rcx, r9, r8):
rcx, r9 = r9, rcx
return (rcx, r9, r8)

def f17(rcx, r9, r8):
r8 -= rcx
r8 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f18(rcx, r9, r8):
r8 += rcx
r8 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f19(rcx, r9, r8):
r8 ^= rcx
return (rcx, r9, r8)

def f20(rcx, r9, r8):
r8, rcx = rcx, r8
return (rcx, r9, r8)

def f21(rcx, r9, r8):
r8 -= r9
r8 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f22(rcx, r9, r8):
r8 += r9
r8 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f23(rcx, r9, r8):
r8 ^= r9
return (rcx, r9, r8)

def f24(rcx, r9, r8):
r8, r9 = r9, r8
return (rcx, r9, r8)

def f25(rcx, r9, r8):
r9 -= rcx
r9 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f26(rcx, r9, r8):
r9 += rcx
r9 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f27(rcx, r9, r8):
r9 ^= rcx
return (rcx, r9, r8)

def f28(rcx, r9, r8):
r9, rcx = rcx, r9
return (rcx, r9, r8)

def f29(rcx, r9, r8):
r9 -= r8
r9 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f30(rcx, r9, r8):
r9 += r8
r9 &= 0xffffffffffffffff
return (rcx, r9, r8)

def f31(rcx, r9, r8):
r9 ^= r8
return (rcx, r9, r8)

def f32(rcx, r9, r8):
r9, r8 = r8, r9
return (rcx, r9, r8)

func = [f0 ,f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, f22, f23, f24, f25, f26, f27, f28, f29, f30, f31, f32]

i = 0
j = 0
dic = {}
dic[0] = 0
while True:
i += 1
j = 34 * j + 17 - 33 * ((0xF83E0F83E0F83E1 * (34 * j + 17) >> 64) >> 1)
dic[i] = j
if(i == 130): break

# test : 1234567890abcdef
# a = 16837421865713898823
# b = 10413852426347822448
# c = 3864992439499163626

# flag
a = 13154447211896390350
b = 13431592849769172675
c = 8204214703590688774

i = 130
j = 32
while True:
(a, c, b) = func[dic[i]](a, c, b)
if(i == 0): break
i-=1

print(a, c, b)
print(binascii.unhexlify(hex(a)[2:])[::-1])
print(binascii.unhexlify(hex(b)[2:])[::-1])
# FLAG{35ddf36349a0b310}

empty

  • _init_array, _fini_array Section

main함수에는 별게 없다. 그런데 실행해보면 ‘>’ 문자열과 함께 출력이 되는걸 보아 init, fini section을 이용한다는 점을 알 수 있었다.

init section을 보면 약간의 난독화가 걸려있는거 같은데 실질적으로 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
unsigned __int64 inittt()
{
unsigned __int64 v0; // rax
void *v1; // rsp
int v2; // eax
unsigned int v3; // eax
unsigned int v5; // [rsp+8h] [rbp-60h] BYREF
unsigned int v6; // [rsp+Ch] [rbp-5Ch]
int i; // [rsp+10h] [rbp-58h]
int v8; // [rsp+14h] [rbp-54h]
unsigned int v9; // [rsp+18h] [rbp-50h]
int v10; // [rsp+1Ch] [rbp-4Ch]
__int64 v11; // [rsp+20h] [rbp-48h]
unsigned int *stack; // [rsp+28h] [rbp-40h]
unsigned __int64 v13; // [rsp+30h] [rbp-38h]

v13 = __readfsqword(0x28u);
printf("> ");
__isoc99_scanf("%s", input);
if ( !check )
exit(1);
v10 = (unsigned int)val1 >> 3;
v11 = (unsigned int)val4 - 1LL;
v0 = 16 * ((4 * (unsigned __int64)(unsigned int)val4 + 15) / 0x10);
while ( &v5 != (unsigned int *)((char *)&v5 - (v0 & 0xFFFFFFFFFFFFF000LL)) )
;
v1 = alloca(v0 & 0xFFF);
if ( (v0 & 0xFFF) != 0 )
*(_QWORD *)((char *)&v5 + (v0 & 0xFFF) - 8) = *(_QWORD *)((char *)&v5 + (v0 & 0xFFF) - 8);
stack = &v5;
i = val3 - 1;
*(&v5 + (unsigned int)(val4 - 1)) = 0;
while ( i != -1 )
{
stack[i / v10] = (stack[i / v10] << 8) + (unsigned __int8)aTemptemptempte[i];
--i;
}
table[0] = val6;
for ( i = 1; i < (unsigned int)val5; ++i )
table[i] = table[i - 1] + val7;
v5 = 0;
v6 = 0;
i = 0;
v9 = 0;
v8 = 0;
while ( 3 * val5 > v5 )
{
v2 = rol4(v8 + v9 + table[i], 3);
table[i] = v2;
v8 = table[i];
v3 = rol4(v8 + v9 + stack[v6], (unsigned __int8)v8 + (unsigned __int8)v9);
stack[v6] = v3;
v9 = stack[v6];
++v5;
i = (i + 1) % (unsigned int)val5;
v6 = (v6 + 1) % val4;
}
return __readfsqword(0x28u) ^ v13;
}

fini 영역만 분석하면 되는데 이 부분이 핵심이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned __int64 finittt()
{
int i; // [rsp+Ch] [rbp-34h]
int j; // [rsp+Ch] [rbp-34h]
int v3[10]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v4; // [rsp+38h] [rbp-8h]

v4 = __readfsqword(0x28u);
for ( i = 0; i <= 3; ++i )
finifunc(&input[8 * i], (unsigned int *)&v3[2 * i]);
for ( j = 0; j <= 7; ++j )
{
if ( cipher[j] != v3[j] )
{
puts("WRONG!");
return __readfsqword(0x28u) ^ v4;
}
}
puts("CORRECT!");
return __readfsqword(0x28u) ^ v4;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __fastcall finifunc(_DWORD *a1, unsigned int *v3)
{
__int64 result; // rax
unsigned int i; // [rsp+14h] [rbp-Ch]
int v4; // [rsp+18h] [rbp-8h]
unsigned int v5; // [rsp+1Ch] [rbp-4h]

v4 = *a1 + table[0];
v5 = a1[1] + table1;
for ( i = 1; i <= val2; ++i )
{
v4 = table[2 * i] + rol4(v5 ^ v4, v5);
v5 = table[2 * i + 1] + rol4(v4 ^ v5, v4);
}
*v3 = v4;
result = v5;
v3[1] = v5;
return result;
}
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 z3 import *

cipher = [0x0A8FE257A, 0x84A65091, 0x0D0808947, 0x2232E444, 0x0C7B5C9D0, 0x72857510, 0x0F9FF5512, 0x431D6EE0]
table = [0x91149D39, 0x1C4A9696, 0x1DC0B015, 0x63F95BF7, 0x04524B02, 0x6D22D4E3, 0x0F174BA3, 0x5E7655D7, 0xA2957F5A, 0x15C5B171, 0xF3E8ACF2, 0x7B323109, 0xFC662B93, 0x7252BD32, 0x7A10719D, 0xF69FA11D, 0xAF7435D9, 0xDC5B9B41, 0xE073F55C, 0xC8042C55, 0xEF4E930A, 0xD97EFC1C, 0x2CA2E0CA, 0xFB0C4508, 0xB871D67F, 0xA615FDA8, 0x6F8B7F2D, 0x7F0004B9, 0xA8C243AE, 0x9A4DB16C, 0x700E5459, 0xD11F3BA5, 0x6EEEBF3E, 0xFD00235A, 0x63C3CD3F, 0xF267AD4A, 0x97E1CC7A, 0x9A61990B, 0xFB6AC7AA, 0x02601442, 0x1FDD5D29, 0x5B203EC7, 0x785953D3, 0x82E77D12, 0xE8357FC5, 0x89CF80BF, 0x8E6E0D25, 0x2A855DF7, 0x5B9B431C, 0x8AFEA3F2]

s = Solver()

x = [BitVec('x%i'%i,32)for i in range(4)]
y = [BitVec('y%i'%i,32)for i in range(4)]

for i in range(4):
x[i] = x[i] + table[0]
y[i] = y[i] + table[1]

for j in range(1, 0xd):
x[i] = (table[2 * j] + RotateLeft(x[i] ^ y[i], y[i])) & 0xffffffff
y[i] = (table[2 * j + 1] + RotateLeft(x[i] ^ y[i], x[i])) & 0xffffffff

s.add(x[i] == cipher[i * 2])
s.add(y[i] == cipher[i * 2 + 1])

print(s.check())

m = s.model()
m = sorted([(d, m[d]) for d in m], key = lambda x: str(x[0]))

val = []
for c in m:
val.append(c[1])

flag = b''
for i in range(4):
flag += bytes.fromhex(hex(val[i].as_long())[2:])[::-1]
flag += bytes.fromhex(hex(val[i+4].as_long())[2:])[::-1]

print(flag)
# flag{you_know_what_init_&_fini!}