2022 Whitehat Contest Final

13 minute read

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, &timestamp) == 3 )
43        {
44          exchange(&front, &back, &timestamp);
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!}