2022 Whitehat Contest Final
OTP Gen
이 문제는 master_key 파일에 써있는 값을 불러와서 연산을 한 값이 Description에 나타나 있다. 즉, master_key에는 우리가 얻어야할 flag 값이 써있다.
적국이 사용하는 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들을
1signed __int64 __fastcall main(int a1, char **a2, char **a3)
2{
3 __int64 times; // r13
4 __int64 backflag; // r12
5 __int64 frontflag; // rbx
6 char *time; // rax
7 __pid_t v8; // [rsp+14h] [rbp-7Ch] BYREF
8 int i; // [rsp+18h] [rbp-78h]
9 int j; // [rsp+1Ch] [rbp-74h]
10 int fd; // [rsp+20h] [rbp-70h]
11 int v12; // [rsp+24h] [rbp-6Ch]
12 __int64 masterkey; // [rsp+28h] [rbp-68h] BYREF
13 __int64 masterkey2; // [rsp+30h] [rbp-60h] BYREF
14 __int64 v15; // [rsp+38h] [rbp-58h] BYREF
15 time_t timer; // [rsp+40h] [rbp-50h] BYREF
16 FILE *infotxtfd; // [rsp+48h] [rbp-48h]
17 _QWORD buf[8]; // [rsp+50h] [rbp-40h] BYREF
18
19 buf[3] = __readfsqword(0x28u);
20 if ( a1 == 2 )
21 {
22 unlink(infotxt);
23 unlink(infolock);
24 fd = open(a2[1], 0);
25 if ( fd == -1 )
26 exit(1);
27 if ( read(fd, buf, 0x10uLL) != 16 )
28 exit(1);
29 close(fd);
30 masterkey = buf[0];
31 masterkey2 = buf[1];
32 for ( i = 0; i <= 32; ++i )
33 {
34 v8 = fork();
35 if ( !v8 )
36 create_thread(i);
37 thread[i] = v8;
38 }
39 timer = ::time(0LL);
40 while ( 1 )
41 {
42 v12 = open(infolock, 194, 292LL);
43 if ( v12 != -1 )
44 break;
45 sched_yield();
46 }
47 infotxtfd = fopen(infotxt, "w");
48 fwrite("0 0\n", 1uLL, 4uLL, infotxtfd);
49 fprintf(infotxtfd, "%llx %llx %llx\n", masterkey, masterkey2, timer);
50 fclose(infotxtfd);
51 close(v12);
52 unlink(infolock);
53 for ( j = 0; j <= 32; ++j )
54 waitpid(thread[j], 0LL, 0);
55 infotxtfd = fopen(infotxt, "r");
56 fscanf(infotxtfd, "%d %d\n", &v8, &v8);
57 fscanf(infotxtfd, "%llx %llx %llx\n", &masterkey, &masterkey2, &v15);
58 times = v15;
59 backflag = masterkey2;
60 frontflag = masterkey;
61 time = ctime(&timer);
62 printf("%.24s: %llu-%llu-%llu\n", time, frontflag, backflag, times);
63 unlink(infotxt);
64 unlink(infolock);
65 return 0LL;
66 }
67 else
68 {
69 printf("Usage: %s [seed file]\n", *a2);
70 return 1LL;
71 }
72}
1void __fastcall __noreturn create_thread(int idx)
2{
3 int highidx; // [rsp+18h] [rbp-48h] BYREF
4 int lowidx_; // [rsp+1Ch] [rbp-44h] BYREF
5 int fd; // [rsp+20h] [rbp-40h]
6 unsigned int lowidx; // [rsp+24h] [rbp-3Ch]
7 __int64 front; // [rsp+28h] [rbp-38h] BYREF
8 __int64 back; // [rsp+30h] [rbp-30h] BYREF
9 __int64 timestamp; // [rsp+38h] [rbp-28h] BYREF
10 FILE *stream; // [rsp+40h] [rbp-20h]
11 _DWORD *v9; // [rsp+48h] [rbp-18h]
12 __int64 v10; // [rsp+50h] [rbp-10h]
13 unsigned __int64 v11; // [rsp+58h] [rbp-8h]
14
15 v11 = __readfsqword(0x28u);
16 mprotect((exchange & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 7);// rwx
17 v9 = memmem(exchange, 0x1000uLL, &nops, 4uLL);// \x90\x90\x90\x90 find ; return start address
18 *v9 += table[idx];
19 mprotect((exchange & 0xFFFFFFFFFFFFF000LL), 0x1000uLL, 5);// rx
20 while ( 1 )
21 {
22 while ( 1 )
23 {
24 fd = open(infolock, 194, 292LL);
25 if ( fd != -1 )
26 break;
27 sched_yield();
28 }
29 stream = fopen(infotxt, "a+");
30 if ( stream )
31 {
32 fseek(stream, 0LL, 0);
33 if ( fscanf(stream, "%d %d\n", &highidx, &lowidx_) == 2 )
34 {
35 if ( highidx == 131 )
36 {
37 fclose(stream);
38 close(fd);
39 unlink(infolock);
40 exit(1);
41 }
42 if ( idx == lowidx_ && fscanf(stream, "%llx %llx %llx\n", &front, &back, ×tamp) == 3 )
43 {
44 exchange(&front, &back, ×tamp);
45 v10 = idx;
46 lowidx = (34LL * idx + 17) % 0x21uLL;
47 fclose(stream);
48 stream = fopen(infotxt, "w");
49 fprintf(stream, "%d %d\n", (highidx + 1), lowidx);
50 fprintf(stream, "%llx %llx %llx\n", front, back, timestamp);
51 }
52 }
53 }
54 fclose(stream);
55 close(fd);
56 unlink(infolock);
57 sleep(1u);
58 }
59}
1from pwn import *
2context.arch = 'amd64'
3table = [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]
4
5nop = 0x90909090
6
7for i in range(len(table)):
8 table[i] += nop
9 print(disasm(p32(table[i] & 0xffffffff)))
10: 48 f7 d1 not rcx
23: 90 nop
30: 48 c1 c1 11 rol rcx, 0x11
40: 48 c1 c9 17 ror rcx, 0x17
50: 49 f7 d0 not r8
63: 90 nop
70: 49 c1 c0 11 rol r8, 0x11
80: 49 c1 c8 17 ror r8, 0x17
90: 49 f7 d1 not r9
103: 90 nop
110: 49 c1 c1 11 rol r9, 0x11
120: 49 c1 c9 17 ror r9, 0x17
130: 4c 01 c1 add rcx, r8
143: 90 nop
150: 4c 29 c1 sub rcx, r8
163: 90 nop
170: 4c 31 c1 xor rcx, r8
183: 90 nop
190: 4c 87 c1 xchg rcx, r8
203: 90 nop
210: 4c 01 c9 add rcx, r9
223: 90 nop
230: 4c 29 c9 sub rcx, r9
243: 90 nop
250: 4c 31 c9 xor rcx, r9
263: 90 nop
270: 4c 87 c9 xchg rcx, r9
283: 90 nop
290: 49 01 c8 add r8, rcx
303: 90 nop
310: 49 29 c8 sub r8, rcx
323: 90 nop
330: 49 31 c8 xor r8, rcx
343: 90 nop
350: 49 87 c8 xchg r8, rcx
363: 90 nop
370: 4d 01 c8 add r8, r9
383: 90 nop
390: 4d 29 c8 sub r8, r9
403: 90 nop
410: 4d 31 c8 xor r8, r9
423: 90 nop
430: 4d 87 c8 xchg r8, r9
443: 90 nop
450: 49 01 c9 add r9, rcx
463: 90 nop
470: 49 29 c9 sub r9, rcx
483: 90 nop
490: 49 31 c9 xor r9, rcx
503: 90 nop
510: 49 87 c9 xchg r9, rcx
523: 90 nop
530: 4d 01 c1 add r9, r8
543: 90 nop
550: 4d 29 c1 sub r9, r8
563: 90 nop
570: 4d 31 c1 xor r9, r8
583: 90 nop
590: 4d 87 c1 xchg r9, r8
603: 90 nop
1import binascii
2
3def _rol(val, bits, bit_size):
4 return (val << bits % bit_size) & (2 ** bit_size - 1) | \
5 ((val & (2 ** bit_size - 1)) >> (bit_size - (bits % bit_size)))
6
7def _ror(val, bits, bit_size):
8 return ((val & (2 ** bit_size - 1)) >> bits % bit_size) | \
9 (val << (bit_size - (bits % bit_size)) & (2 ** bit_size - 1))
10
11__ROR4__ = lambda val, bits: _ror(val, bits, 32)
12__ROR8__ = lambda val, bits: _ror(val, bits, 64)
13__ROL4__ = lambda val, bits: _rol(val, bits, 32)
14__ROL8__ = lambda val, bits: _rol(val, bits, 64)
15
16def f0(rcx, r9, r8):
17 rcx = ((1 << 64) - rcx - 1)
18 return (rcx, r9, r8)
19
20def f1(rcx, r9, r8):
21 rcx = __ROR8__(rcx, 0x11)
22 return (rcx, r9, r8)
23
24def f2(rcx, r9, r8):
25 rcx = __ROL8__(rcx, 0x17)
26 return (rcx, r9, r8)
27
28def f3(rcx, r9, r8):
29 r8 = ((1 << 64) - r8 - 1)
30 return (rcx, r9, r8)
31
32def f4(rcx, r9, r8):
33 r8 = __ROR8__(r8, 0x11)
34 return (rcx, r9, r8)
35
36def f5(rcx, r9, r8):
37 r8 = __ROL8__(r8, 0x17)
38 return (rcx, r9, r8)
39
40def f6(rcx, r9, r8):
41 r9 = ((1 << 64) - r9 - 1)
42 return (rcx, r9, r8)
43
44def f7(rcx, r9, r8):
45 r9 = __ROR8__(r9, 0x11)
46 return (rcx, r9, r8)
47
48def f8(rcx, r9, r8):
49 r9 = __ROL8__(r9, 0x17)
50 return (rcx, r9, r8)
51
52def f9(rcx, r9, r8):
53 rcx -= r8
54 rcx &= 0xffffffffffffffff
55 return (rcx, r9, r8)
56
57def f10(rcx, r9, r8):
58 rcx += r8
59 rcx &= 0xffffffffffffffff
60 return (rcx, r9, r8)
61
62def f11(rcx, r9, r8):
63 rcx ^= r8
64 return (rcx, r9, r8)
65
66def f12(rcx, r9, r8):
67 r8, rcx = rcx, r8
68 return (rcx, r9, r8)
69
70def f13(rcx, r9, r8):
71 rcx -= r9
72 rcx &= 0xffffffffffffffff
73 return (rcx, r9, r8)
74
75def f14(rcx, r9, r8):
76 rcx += r9
77 rcx &= 0xffffffffffffffff
78 return (rcx, r9, r8)
79
80def f15(rcx, r9, r8):
81 rcx ^= r9
82 return (rcx, r9, r8)
83
84def f16(rcx, r9, r8):
85 rcx, r9 = r9, rcx
86 return (rcx, r9, r8)
87
88def f17(rcx, r9, r8):
89 r8 -= rcx
90 r8 &= 0xffffffffffffffff
91 return (rcx, r9, r8)
92
93def f18(rcx, r9, r8):
94 r8 += rcx
95 r8 &= 0xffffffffffffffff
96 return (rcx, r9, r8)
97
98def f19(rcx, r9, r8):
99 r8 ^= rcx
100 return (rcx, r9, r8)
101
102def f20(rcx, r9, r8):
103 r8, rcx = rcx, r8
104 return (rcx, r9, r8)
105
106def f21(rcx, r9, r8):
107 r8 -= r9
108 r8 &= 0xffffffffffffffff
109 return (rcx, r9, r8)
110
111def f22(rcx, r9, r8):
112 r8 += r9
113 r8 &= 0xffffffffffffffff
114 return (rcx, r9, r8)
115
116def f23(rcx, r9, r8):
117 r8 ^= r9
118 return (rcx, r9, r8)
119
120def f24(rcx, r9, r8):
121 r8, r9 = r9, r8
122 return (rcx, r9, r8)
123
124def f25(rcx, r9, r8):
125 r9 -= rcx
126 r9 &= 0xffffffffffffffff
127 return (rcx, r9, r8)
128
129def f26(rcx, r9, r8):
130 r9 += rcx
131 r9 &= 0xffffffffffffffff
132 return (rcx, r9, r8)
133
134def f27(rcx, r9, r8):
135 r9 ^= rcx
136 return (rcx, r9, r8)
137
138def f28(rcx, r9, r8):
139 r9, rcx = rcx, r9
140 return (rcx, r9, r8)
141
142def f29(rcx, r9, r8):
143 r9 -= r8
144 r9 &= 0xffffffffffffffff
145 return (rcx, r9, r8)
146
147def f30(rcx, r9, r8):
148 r9 += r8
149 r9 &= 0xffffffffffffffff
150 return (rcx, r9, r8)
151
152def f31(rcx, r9, r8):
153 r9 ^= r8
154 return (rcx, r9, r8)
155
156def f32(rcx, r9, r8):
157 r9, r8 = r8, r9
158 return (rcx, r9, r8)
159
160func = [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]
161
162i = 0
163j = 0
164dic = {}
165dic[0] = 0
166while True:
167 i += 1
168 j = 34 * j + 17 - 33 * ((0xF83E0F83E0F83E1 * (34 * j + 17) >> 64) >> 1)
169 dic[i] = j
170 if(i == 130): break
171
172# test : 1234567890abcdef
173# a = 16837421865713898823
174# b = 10413852426347822448
175# c = 3864992439499163626
176
177# flag
178a = 13154447211896390350
179b = 13431592849769172675
180c = 8204214703590688774
181
182i = 130
183j = 32
184while True:
185 (a, c, b) = func[dic[i]](a, c, b)
186 if(i == 0): break
187 i-=1
188
189print(a, c, b)
190print(binascii.unhexlify(hex(a)[2:])[::-1])
191print(binascii.unhexlify(hex(b)[2:])[::-1])
192# FLAG{35ddf36349a0b310}
empty
_init_array
,_fini_array
Section
main함수에는 별게 없다. 그런데 실행해보면 ‘>’ 문자열과 함께 출력이 되는걸 보아 init, fini section을 이용한다는 점을 알 수 있었다.
init section을 보면 약간의 난독화가 걸려있는거 같은데 실질적으로 flag를 얻는데 전혀 필요가 없다는 것을 알게 됐다.
1unsigned __int64 inittt()
2{
3 unsigned __int64 v0; // rax
4 void *v1; // rsp
5 int v2; // eax
6 unsigned int v3; // eax
7 unsigned int v5; // [rsp+8h] [rbp-60h] BYREF
8 unsigned int v6; // [rsp+Ch] [rbp-5Ch]
9 int i; // [rsp+10h] [rbp-58h]
10 int v8; // [rsp+14h] [rbp-54h]
11 unsigned int v9; // [rsp+18h] [rbp-50h]
12 int v10; // [rsp+1Ch] [rbp-4Ch]
13 __int64 v11; // [rsp+20h] [rbp-48h]
14 unsigned int *stack; // [rsp+28h] [rbp-40h]
15 unsigned __int64 v13; // [rsp+30h] [rbp-38h]
16
17 v13 = __readfsqword(0x28u);
18 printf("> ");
19 __isoc99_scanf("%s", input);
20 if ( !check )
21 exit(1);
22 v10 = (unsigned int)val1 >> 3;
23 v11 = (unsigned int)val4 - 1LL;
24 v0 = 16 * ((4 * (unsigned __int64)(unsigned int)val4 + 15) / 0x10);
25 while ( &v5 != (unsigned int *)((char *)&v5 - (v0 & 0xFFFFFFFFFFFFF000LL)) )
26 ;
27 v1 = alloca(v0 & 0xFFF);
28 if ( (v0 & 0xFFF) != 0 )
29 *(_QWORD *)((char *)&v5 + (v0 & 0xFFF) - 8) = *(_QWORD *)((char *)&v5 + (v0 & 0xFFF) - 8);
30 stack = &v5;
31 i = val3 - 1;
32 *(&v5 + (unsigned int)(val4 - 1)) = 0;
33 while ( i != -1 )
34 {
35 stack[i / v10] = (stack[i / v10] << 8) + (unsigned __int8)aTemptemptempte[i];
36 --i;
37 }
38 table[0] = val6;
39 for ( i = 1; i < (unsigned int)val5; ++i )
40 table[i] = table[i - 1] + val7;
41 v5 = 0;
42 v6 = 0;
43 i = 0;
44 v9 = 0;
45 v8 = 0;
46 while ( 3 * val5 > v5 )
47 {
48 v2 = rol4(v8 + v9 + table[i], 3);
49 table[i] = v2;
50 v8 = table[i];
51 v3 = rol4(v8 + v9 + stack[v6], (unsigned __int8)v8 + (unsigned __int8)v9);
52 stack[v6] = v3;
53 v9 = stack[v6];
54 ++v5;
55 i = (i + 1) % (unsigned int)val5;
56 v6 = (v6 + 1) % val4;
57 }
58 return __readfsqword(0x28u) ^ v13;
59}
fini 영역만 분석하면 되는데 이 부분이 핵심이다.
1unsigned __int64 finittt()
2{
3 int i; // [rsp+Ch] [rbp-34h]
4 int j; // [rsp+Ch] [rbp-34h]
5 int v3[10]; // [rsp+10h] [rbp-30h] BYREF
6 unsigned __int64 v4; // [rsp+38h] [rbp-8h]
7
8 v4 = __readfsqword(0x28u);
9 for ( i = 0; i <= 3; ++i )
10 finifunc(&input[8 * i], (unsigned int *)&v3[2 * i]);
11 for ( j = 0; j <= 7; ++j )
12 {
13 if ( cipher[j] != v3[j] )
14 {
15 puts("WRONG!");
16 return __readfsqword(0x28u) ^ v4;
17 }
18 }
19 puts("CORRECT!");
20 return __readfsqword(0x28u) ^ v4;
21}
1__int64 __fastcall finifunc(_DWORD *a1, unsigned int *v3)
2{
3 __int64 result; // rax
4 unsigned int i; // [rsp+14h] [rbp-Ch]
5 int v4; // [rsp+18h] [rbp-8h]
6 unsigned int v5; // [rsp+1Ch] [rbp-4h]
7
8 v4 = *a1 + table[0];
9 v5 = a1[1] + table1;
10 for ( i = 1; i <= val2; ++i )
11 {
12 v4 = table[2 * i] + rol4(v5 ^ v4, v5);
13 v5 = table[2 * i + 1] + rol4(v4 ^ v5, v4);
14 }
15 *v3 = v4;
16 result = v5;
17 v3[1] = v5;
18 return result;
19}
1from z3 import *
2
3cipher = [0x0A8FE257A, 0x84A65091, 0x0D0808947, 0x2232E444, 0x0C7B5C9D0, 0x72857510, 0x0F9FF5512, 0x431D6EE0]
4table = [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]
5
6s = Solver()
7
8x = [BitVec('x%i'%i,32)for i in range(4)]
9y = [BitVec('y%i'%i,32)for i in range(4)]
10
11for i in range(4):
12 x[i] = x[i] + table[0]
13 y[i] = y[i] + table[1]
14
15 for j in range(1, 0xd):
16 x[i] = (table[2 * j] + RotateLeft(x[i] ^ y[i], y[i])) & 0xffffffff
17 y[i] = (table[2 * j + 1] + RotateLeft(x[i] ^ y[i], x[i])) & 0xffffffff
18
19 s.add(x[i] == cipher[i * 2])
20 s.add(y[i] == cipher[i * 2 + 1])
21
22print(s.check())
23
24m = s.model()
25m = sorted([(d, m[d]) for d in m], key = lambda x: str(x[0]))
26
27val = []
28for c in m:
29 val.append(c[1])
30
31flag = b''
32for i in range(4):
33 flag += bytes.fromhex(hex(val[i].as_long())[2:])[::-1]
34 flag += bytes.fromhex(hex(val[i+4].as_long())[2:])[::-1]
35
36print(flag)
37# flag{you_know_what_init_&_fini!}