Guessing Game

The program asks us to input 10 numbers, then it do calculation with these 10 numbers, check the result with bytes stored in match. Decompile the program with r2, we are able to take a look into how the calculation works.

      ...
      0x56383f24c108      488d3df90e00.  lea rdi, str.Enter_10_numbers_to_check_your_luck ; 0x56383f24d008 ; "Enter 10 numbers to check your luck"
      0x56383f24c10f      488d2d160f00.  lea rbp, [0x56383f24d02c] ; " %u"
      0x56383f24c116      e815ffffff     call sym.imp.puts
      0x56383f24c11b      488b3dc62f00.  mov rdi, qword [reloc.stdout] ; [0x56383f24f0e8:8]=0
      0x56383f24c122      e849ffffff     call sym.imp.fflush
      0x56383f24c127      660f1f840000.  nop word [rax + rax]
  ┌─> 0x56383f24c130      4889de         mov rsi, rbx
   0x56383f24c133      4889ef         mov rdi, rbp
   0x56383f24c136      31c0           xor eax, eax
   0x56383f24c138      4883c304       add rbx, 4
   0x56383f24c13c      e83fffffff     call sym.imp.__isoc99_scanf
   0x56383f24c141      4939dc         cmp r12, rbx
  └─< 0x56383f24c144      75ea           jne 0x56383f24c130
      0x56383f24c146      660fefc0       pxor xmm0, xmm0
      0x56383f24c14a      488d6c2430     lea rbp, [rsp + 0x30]
      0x56383f24c14f      4c8d642458     lea r12, [rsp + 0x58]
      0x56383f24c154      48c744245000.  mov qword [rsp + 0x50], 0
      0x56383f24c15d      0f29442430     movaps xmmword [rsp + 0x30], xmm0
      0x56383f24c162      0f29442440     movaps xmmword [rsp + 0x40], xmm0
      0x56383f24c167      660f1f840000.  nop word [rax + rax]
  ┌─> 0x56383f24c170      bb05000000     mov ebx, 5
 ┌──> 0x56383f24c175      e816ffffff     call sym.imp.rand
 ╎╎   0x56383f24c17a      314500         xor dword [rbp], eax
 ╎╎   0x56383f24c17d      83eb01         sub ebx, 1
 └──< 0x56383f24c180      75f3           jne 0x56383f24c175
   0x56383f24c182      4883c504       add rbp, 4
   0x56383f24c186      4c39e5         cmp rbp, r12
  └─< 0x56383f24c189      75e5           jne 0x56383f24c170
      0x56383f24c18b      f30f7e542450   movq xmm2, qword [rsp + 0x50]
      0x56383f24c191      f30f7e5c2420   movq xmm3, qword [rsp + 0x20]
      0x56383f24c197      660f6f442430   movdqa xmm0, xmmword [rsp + 0x30]
      0x56383f24c19d      660fef0424     pxor xmm0, xmmword [rsp]
      0x56383f24c1a2      660fefd3       pxor xmm2, xmm3
      0x56383f24c1a6      660fef05122f.  pxor xmm0, xmmword [obj.arr]
      0x56383f24c1ae      f30f7e1d2a2f.  movq xmm3, qword [0x56383f24f0e0] ; [0x56383f24f0e0:8]=0
      0x56383f24c1b6      660f6f4c2410   movdqa xmm1, xmmword [rsp + 0x10]
      0x56383f24c1bc      660fef4c2440   pxor xmm1, xmmword [rsp + 0x40]
      0x56383f24c1c2      660fef0d062f.  pxor xmm1, xmmword [0x56383f24f0d0]
      0x56383f24c1ca      660fefd3       pxor xmm2, xmm3
      0x56383f24c1ce      660f7ec0       movd eax, xmm0
      0x56383f24c1d2      3905a82e0000   cmp dword [obj.match], eax ; [0x56383f24f080:4]=0x3653a908
      ...

Translate it to psudocode, it gets clearer.

def forward():

    for i in range(16):
      xmm0[i] ^= buf[i]

    for i in range(16):
      xmm2[i] ^= xmm3[i]

    for i in range(16):
      xmm0[i] ^= arr[i]

    xmm3 = arr[-8:]
    xmm1 = buf[16:32]

    for i in range(17):
      xmm1[i] ^= ran[16+i]

    for i in range(17):
      xmm1[i] ^= arr[16+i]
     
    for i in range(8):
      xmm2[i] ^= xmm3[i]

buf stores the 10 numbers from input, arr above is transformation result from original one, the transformation duplicates the string DeltaForce three times, so it becomes a string of four DeltaForce, here is the decompiled code snipe from ghidra.

  ...
  i = 10;
  local_20 = *(long *)(in_FS_OFFSET + 0x28);
  do {
    arr[i] = arr[(int)i + (int)((i & 0xffffffff) / 10) * -10];
    i = i + 1;
  } while (i != 0x28);
  ...

ran is a list of random numbers generated from constant seeded rand.

  srand(0xffffff);
  ...
  do {
    j = 5;
    do {
      random = rand();
      *(uint *)p2 = *(uint *)p2 ^ random;
      j = j + -1;
    } while (j != 0);
    p2 = (long *)((long)p2 + 4);
  } while (p2 != &local_20);
  ...

Now we need to do reverse calculation to find out what number should we input. All calculation is byte level, so buf need to be an array of size 40.

ran = [36, 184, 75, 50, 106, 222, 33, 64, 75, 253, 75, 85, 118, 114, 201, 92, 121, 55, 219, 18, 48, 67, 22, 5, 184, 96, 219, 113, 158, 97, 171, 102, 131, 244, 199, 55, 173, 40, 184, 46]
arr = [ord(c) for c in list(("DeltaForce" * 4))]
match = [8, 169, 83, 54, 120, 162, 97, 29, 81, 247, 122, 68, 111, 40, 202, 127, 57, 33, 233, 0, 64, 81, 67, 38, 190, 126, 215, 82, 253, 4, 239, 3, 49, 11, 209, 71, 226, 13, 147, 78]
buf = [0]*40
xmm1 = buf[16:32]
xmm2 = ran[32:40]
xmm3 = ran[0:16]

Use bytes_to_array function to conver byte string of match to integer array that can be used for calculation.

def bytes_to_array(dat, sz):
    dat = dat.split()
    arr = []
    for i in range(0, len(dat), sz):
        arr.append(int(''.join(reversed(dat[i:i+sz])), 16))
    return arr

match = "08 a9 53 36 78 a2 61 1d 51 f7 7a 44 6f 28 ca 7f 39 21 e9 00 40 51 43 26 be 7e d7 52 fd 04 ef 03 31 0b d1 47 e2 0d 93 4e"
match = bytes_to_array(match, 1)

The random byte list can be generated from rand in glibc with seed 0xffffff, or copy from r2.

- offset -       0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x7ffd22229560  24b8 4b32 6ade 2140 4bfd 4b55 7672 c95c  $.K2j.!@K.KUvr.\
0x7ffd22229570  7937 db12 3043 1605 b860 db71 9e61 ab66  y7..0C...`.q.a.f
0x7ffd22229580  83f4 c737 ad28 b82e 

According to forward operations above, here is the reverse operations.

def reverse():
    xmm0 = [0] * 40

    for i in range(40):
        xmm0[i] = match[i] ^ arr[i]
    for i in range(40):
        buf[i] = xmm0[i] ^ ran[i]

    for i in range(4):
        xmm1[i] = match[16+i] ^ arr[16+i]

    for i in range(4):
        xmm1[i] ^= ran[16+i]

    buf[16:20] = xmm1[:4]

reverse()
print(' '.join([str(int.from_bytes(bytes(buf[i:i+4]),'little')) for i in range(0, len(buf), 4)]))

Concatenate the numbers with space as delimiter and send to the server, the flag is in response.

TOP