Rabu, 06 Desember 2017

Write Up Resqua - Reversing. Hacktoday.

Write Up CTF - Resqua

Write Up CTF Hacktoday - Reversing : Resqua

Binary dapat didownload disini https://gist.github.com/alfakatsuki/2544d829b8e0782802cd1e20fa153b75

Diberikan binary dengan spesifikasi berikut

alfakatsuki@Ubuntu ~/C/H/2/r/resqua> file resqua
resqua: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=9b1245d862484ea5086e05c670d9ef40e312a05e, not stripped

alfakatsuki@Ubuntu ~/C/H/2/r/resqua> ./resqua 
Enter a valid serial number: 1 1 1 1 1
Wrong format!

Binary tersebut meminta input berbentuk serial number. Lebih jelas mari kita coba lihat decompile dari binary tersebut di IDA.

Berikut adalah decompile dari fungsi main

// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  unlock(*(_QWORD *)&argc, argv, envp);
  ((void (*)(void))run)();
  return 0;
}

Berikut adalah pseudocode dari fungsi unlock

int unlock()
{
  signed __int64 v0; // ST20_8
  __int64 v1; // ST30_8
  signed int i; // [rsp+4h] [rbp-3Ch]
  int v4; // [rsp+14h] [rbp-2Ch]
  void *addr; // [rsp+38h] [rbp-8h]

  v0 = *(signed int *)byte_400009 + 0x400000LL;
  v1 = sysconf(30);
  addr = (void *)(-v1 & v0);
  v4 = *(_DWORD *)byte_400009 + *(signed __int16 *)&byte_400009[4] + 0x400000 - (-(signed int)v1 & v0);
  mprotect(addr, v4, 7);
  for ( i = 0; i < *(signed __int16 *)&byte_400009[4]; ++i )
    *((_BYTE *)&dword_400000 + *(signed int *)byte_400009 + i) ^= *((_BYTE *)&dword_400000 + i % 3 + 1);
  return mprotect(addr, v4, 5);
}

Yang menarik dari program ini adalah program tersebut adalah opcode dari fungsi run yang terenkripsi dan akan di dekripsi saat fungsi unlock dijalankan. Ketika fungsi unlock sudah dijalankan binary baru akan mengeksekusi pertintah yang sebenarnya pada fungsi run.

Kita coba lihat di IDA terdapat segment .lock dimana terdapat fungsi run() dan fungsi c() yang assembly code nya terenkripsi.

.lock:0x0000000000400992 ; ------------------
.
.
.lock:0x0000000000400BA0 ; ----------------
.

Dengan menggunakan GDB. Kita coba break program setelah program menjalankan fungsi unlock(). Kita coba break pada 0x0000000000400903. Sebelumnya kita coba break di main untuk melihat opcode yang masih terenkripsi.

gdb-peda$ x/100gx 0x400992
0x400992 <c>: 0xcda93bc5a0cf0410 0x42394c45421aa93b
0x4009a2 <c+16>: 0x816fae464c4546f4 0x46ae464c4547b000
0x4009b2 <c+32>: 0x00c5a0006fb000cd 0xb63345aa31c644b0
0x4009c2 <c+48>: 0x4385d24345aa31c6 0xa0cf0410851185f0
0x4009d2 <run+4>: 0x42c70d22cc81c504 0x09cc0e4c45466460
0x4009e2 <run+20>: 0x450647fdf98c74be 0xba30ad464c4546f4
0x4009f2 <run+36>: 0xc50da609c80eb3ba 0xc80eb3babaecad81
0x400a02 <run+52>: 0xba08ad81c50da609 0x543055b4c60eb3ba
0x400a12 <run+68>: 0x55336179a209f349 0x4e39687aa500f043
0x400a22 <run+84>: 0x43316b70ab03fa4a 0xb94aa445064793f9
0x400a32 <run+100>: 0x814c45471facb9b3 0x6aae464c4546c000
0x400a42 <run+116>: 0x01f043dd0ec000cd 0xb6f351337c79a649
0x400a52 <run+132>: 0xb3babdaaad460c4e 0xb93aa445464c44f9
.
.
.

Berikut adalah opcode dari binary yang telah di dekripsi dan telah dijalankan.

0x400992 <c>: 0x81ec7d89e5894855 0x077f00000456ec7d
0x4009a2 <c+16>: 0xc723eb00000000b8 0x0aeb00000001fc45
0x4009b2 <c+32>: 0x4583ec4529fc458b 0xf07f00ec7d8302fc
0x4009c2 <c+48>: 0x0fc0940f00ec7d83 0xe5894855c35dc0b6
0x4009d2 <run+4>: 0x048b486480c48348 0x4589480000002825
0x4009e2 <run+20>: 0x00400bb8bfc031f8 0xfc7ce800000000b8
0x4009f2 <run+36>: 0x8948e0458d48ffff 0x8d48fffffca0e8c7
0x400a02 <run+52>: 0xfc44e8c78948e045 0x187513f88348ffff
0x400a12 <run+68>: 0x10752d3ce445b60f 0x08752d3ce945b60f
0x400a22 <run+84>: 0x0f742d3cee45b60f 0xfc0ce800400bd6bf
0x400a32 <run+100>: 0xc700000153e9ffff 0x26eb000000008c45
0x400a42 <run+116>: 0x44b60f98488c458b 0xf0bf1475303ce005
0x400a52 <run+132>: 0xfffffbe6e800400b 0xfc7ce800000001bf
0x400a62 <run+148>: 0x7d83018c4583ffff 0xe04d8d48d47e138c
0x400a72 <run+164>: 0x000004baa0458d48 0xe8c78948ce894800
0x400a82 <run+180>: 0x00a445c6fffffbaa 0x05488d48e0458d48
0x400a92 <run+196>: 0x000004bab0458d48 0xe8c78948ce894800
0x400aa2 <run+212>: 0x00b445c6fffffb8a 0x0a488d48e0458d48
0x400ab2 <run+228>: 0x000004bac0458d48 0xe8c78948ce894800
0x400ac2 <run+244>: 0x00c445c6fffffb6a 0x0f488d48e0458d48
0x400ad2 <run+260>: 0x000004bad0458d48 0xe8c78948ce894800
0x400ae2 <run+276>: 0x00d445c6fffffb4a 0xe8c78948a0458d48
0x400af2 <run+292>: 0x48904589fffffbda 0xcbe8c78948b0458d
0x400b02 <run+308>: 0x8d48944589fffffb 0xfbbce8c78948c045
0x400b12 <run+324>: 0x458d48984589ffff 0xfffbade8c78948d0
0x400b22 <run+340>: 0x8990458b9c4589ff 0xc085fffffe62e8c7
0x400b32 <run+356>: 0xe8c78994458b4e74 0x4074c085fffffe54
0x400b42 <run+372>: 0xfe46e8c78998458b 0x458b3274c085ffff
0x400b52 <run+388>: 0xfffffe38e8c7899c 0x3b90458b2474c085
0x400b62 <run+404>: 0x3b94458b1c7d9445 0x3b98458b147d9845
0x400b72 <run+420>: 0x400c10bf0c7d9c45 0x0aebfffffac0e800
0x400b82 <run+436>: 0xfab4e800400bf0bf 0x4864f8458b48ffff
0x400b92 <run+452>: 0x7400000028250433 0xc3c9fffffac0e805

Binary ini dapat dianalisis langsung melalui disassemblynya. Namun kita coba cara lain yaitu dengan melakukan patch pada binary agar binary dapat di decompile dengan opcode yang benar.

Berikut adalah skrip yang digunakan untuk melakukan patching pada binary

from pwn import *
from sys import *

binary = open('resqua').read()

# Cari opcode ini untuk dipatch
cari = p64(0xcda93bc5a0cf0410)

realopcode = [0x81ec7d89e5894855 ,0x077f00000456ec7d ,0xc723eb00000000b8 ,0x0aeb00000001fc45 ,0x4583ec4529fc458b ,0xf07f00ec7d8302fc ,0x0fc0940f00ec7d83 ,0xe5894855c35dc0b6 ,0x048b486480c48348 ,0x4589480000002825 ,0x00400bb8bfc031f8 ,0xfc7ce800000000b8 ,0x8948e0458d48ffff ,0x8d48fffffca0e8c7 ,0xfc44e8c78948e045 ,0x187513f88348ffff ,0x10752d3ce445b60f ,0x08752d3ce945b60f ,0x0f742d3cee45b60f ,0xfc0ce800400bd6bf ,0xc700000153e9ffff ,0x26eb000000008c45 ,0x44b60f98488c458b ,0xf0bf1475303ce005 ,0xfffffbe6e800400b ,0xfc7ce800000001bf ,0x7d83018c4583ffff ,0xe04d8d48d47e138c ,0x000004baa0458d48 ,0xe8c78948ce894800 ,0x00a445c6fffffbaa ,0x05488d48e0458d48 ,0x000004bab0458d48 ,0xe8c78948ce894800 ,0x00b445c6fffffb8a ,0x0a488d48e0458d48 ,0x000004bac0458d48 ,0xe8c78948ce894800 ,0x00c445c6fffffb6a ,0x0f488d48e0458d48 ,0x000004bad0458d48 ,0xe8c78948ce894800 ,0x00d445c6fffffb4a ,0xe8c78948a0458d48 ,0x48904589fffffbda ,0xcbe8c78948b0458d ,0x8d48944589fffffb ,0xfbbce8c78948c045 ,0x458d48984589ffff ,0xfffbade8c78948d0 ,0x8990458b9c4589ff ,0xc085fffffe62e8c7 ,0xe8c78994458b4e74 ,0x4074c085fffffe54 ,0xfe46e8c78998458b ,0x458b3274c085ffff ,0xfffffe38e8c7899c ,0x3b90458b2474c085 ,0x3b94458b1c7d9445 ,0x3b98458b147d9845 ,0x400c10bf0c7d9c45 ,0x0aebfffffac0e800 ,0xfab4e800400bf0bf ,0x4864f8458b48ffff ,0x7400000028250433 ,0xc3c9fffffac0e805 ]

real_opcode = ""
for i in realopcode:
 real_opcode += p64(i)

# cari opcode
start_patch = 0
for i in range(len(binary)):
 if(cari == binary[i:i+8]):
  # print "ketemu"

  start_patch = i
  break

# Mulai patching
binary = list(binary)

for i in real_opcode:
 binary[start_patch] = i
 start_patch += 1


# done patching
print "".join(binary)

# python patch.py > resqua_patched

Setelah dicek binary dengan file, binary masih valid sebagai file ELF. Kita coba decompile. Fungsi run() dan fungsi c() sudah dapat didecompile.

Berikut fungsi run()

unsigned __int64 run()
{
  signed int i; // [rsp+Ch] [rbp-74h]
  int v2; // [rsp+10h] [rbp-70h]
  int v3; // [rsp+14h] [rbp-6Ch]
  int v4; // [rsp+18h] [rbp-68h]
  int v5; // [rsp+1Ch] [rbp-64h]
  char dest; // [rsp+20h] [rbp-60h]
  char v7; // [rsp+24h] [rbp-5Ch]
  char nptr; // [rsp+30h] [rbp-50h]
  char v9; // [rsp+34h] [rbp-4Ch]
  char v10; // [rsp+40h] [rbp-40h]
  char v11; // [rsp+44h] [rbp-3Ch]
  char v12; // [rsp+50h] [rbp-30h]
  char v13; // [rsp+54h] [rbp-2Ch]
  char s[4]; // [rsp+60h] [rbp-20h]
  char v15; // [rsp+64h] [rbp-1Ch]
  _BYTE v16[3]; // [rsp+65h] [rbp-1Bh]
  char v17; // [rsp+69h] [rbp-17h]
  _BYTE v18[6]; // [rsp+6Ah] [rbp-16h]
  char v19; // [rsp+6Eh] [rbp-12h]
  unsigned __int64 v20; // [rsp+78h] [rbp-8h]

  v20 = __readfsqword(0x28u);
  printf("Enter a valid serial number: ");
  gets(s);
  if ( strlen(s) == 19 && v15 == 45 && v17 == 45 && v19 == 45 )
  {
    for ( i = 0; i <= 19; ++i )
    {
      if ( s[i] == 48 )
      {
        puts("\x1B[31mInvalid serial number!\x1B[0m");
        exit(1);
      }
    }
    strncpy(&dest, s, 4uLL);
    v7 = 0;
    strncpy(&nptr, v16, 4uLL);
    v9 = 0;
    strncpy(&v10, v18, 4uLL);
    v11 = 0;
    strncpy(&v12, &v18[5], 4uLL);
    v13 = 0;
    v2 = atoi(&dest);
    v3 = atoi(&nptr);
    v4 = atoi(&v10);
    v5 = atoi(&v12);
    if ( (unsigned int)c((unsigned int)v2, &v18[5])
      && (unsigned int)c((unsigned int)v3, &v18[5])
      && (unsigned int)c((unsigned int)v4, &v18[5])
      && (unsigned int)c((unsigned int)v5, &v18[5])
      && v2 < v3
      && v3 < v4
      && v4 < v5 )
    {
      puts("\x1B[32mCongrats, valid serial number!\x1B[0m");
    }
    else
    {
      puts("\x1B[31mInvalid serial number!\x1B[0m");
    }
  }
  else
  {
    puts("\x1B[31mWrong format!\x1B[0m");
  }
  return __readfsqword(0x28u) ^ v20;
}

Fungsi c()

_BOOL8 __fastcall c(signed int a1)
{
  signed int v2; // [rsp+0h] [rbp-14h]
  signed int v3; // [rsp+10h] [rbp-4h]

  v2 = a1;
  if ( a1 <= 1110 )
    return 0LL;
  v3 = 1;
  while ( v2 > 0 )
  {
    v2 -= v3;
    v3 += 2;
  }
  return v2 == 0;
}

Dari pseudocode tersebut. Didapat konstrain untuk serial number.

  1. Memiliki Panjang string 19. Dengan bentuk serial number xxxx-xxxx-xxxx-xxxx dengan x adalah angka
  2. Angka tidak boleh memiliki digit 0
  3. Angka tidak boleh <= 1110
  4. Angka1 < Angka2 < Angka3 < Angka4

Dengan skrip sederhana berikut kita dapat mendapatkan beberapa angka yang benar

arr = []

k = 1
hasil = 0
for i in range(100):
  arr.append(hasil)
  hasil += k
  k += 2

print arr

Kita misalkan pilih yang memenuhi 1681-1764-1849-1936

alfakatsuki@Ubuntu ~/Desktop> ./resqua 
Enter a valid serial number: 1681-1764-1849-1936
Congrats, valid serial number!