// 컴파일: gcc -o uaf_overwrite uaf_overwrite.c
#include
#include
#include <문자열.h>
#include
구조체 인간 {
문자 이름[16];
정수 무게;
나이가 많다;
};
로봇 구조체 {
문자 이름[16];
정수 무게;
무효 (*fptr)();
};
구조체 인간 *인간;
구조체 로봇 *robot;
char *맞춤[10];
int c_idx;
void print_name() { printf("이름: %s\n", 로봇->이름); }
무효 메뉴() {
printf("1. 인간\n");
printf("2. 로봇\n");
printf("3. 사용자 정의\n");
printf("> ");
}
무효 human_func() {
int 셀;
human = (struct Human *)malloc(sizeof(struct Human));
strcpy(사람->이름, "사람");
printf("인체 체중: ");
scanf("%d", &사람->체중);
printf("인간나이: ");
scanf("%ld", &사람->나이);
자유(인간);
}
무효 로봇_펑크() {
int 셀;
로봇 = (로봇 구조 *)malloc(sizeof(로봇 구조));
strcpy(로봇->이름, "로봇");
printf("로봇 무게: ");
scanf("%d", &robot->체중);
if (로봇->fptr)
로봇->fptr();
또 다른
로봇->fptr = print_name;
로봇->fptr(로봇);
무료(로봇);
}
int custom_func() {
부호 없는 정수 크기;
부호 없는 int idx;
if (c_idx > 9) {
printf("사용자 정의 전체!!\n");
0을 반환합니다.
}
printf("크기: ");
scanf("%d", &size);
if (크기 >= 0x100) {
custom[c_idx] = malloc(크기);
printf("데이터: ");
read(0, custom[c_idx], 크기 - 1);
printf("데이터: %s\n", custom[c_idx]);
printf("무료 IDx: ");
scanf("%d", &idx);
if (idx < 10 && 사용자 정의[idx]) {
free(custom[idx]);
사용자 정의[idx] = NULL;
}
}
c_idx++;
}
정수 메인() {
정수 idx;
char *ptr;
setvbuf(표준입력, 0, 2, 0);
setvbuf(표준출력, 0, 2, 0);
동안 (1) {
메뉴();
scanf("%d", &idx);
스위치(idx) {
사례 1:
인간_펑크();
부서지다;
사례 2:
로봇_펑크();
부서지다;
사례 3:
custom_func();
부서지다;
}
}
}
UAF 는 free 하고 그걸 쓰는 겁니다.
Use After Free 는 메모리 참조에 사용한 포인터를 메모리 해제 후에 적절히 초기화하지 않아서 , 또는 해제한 메모리를 초기화하지 않고 다음 청크에 재할당 해주면서 발생하는 취약점이다.
Dangling Pointer 는 유효하지 않은 메모리 영역을 가리키는 포인터를 말햔다.
Free 를 호출한 후에 포인터를 초기화하지 않으면 이 포인터는 해제된 청크를 가리키는 Dangling pointer 가 된다.
또 해제된 메모리에 접근할 수 있을때도 발생할 수 있는 취약점이다.
할당 후 사용한 메모리를 free 하고 메모리의 데이터를 초기화 하지 않으면 그 안에 데이터가 그대로 남아있어 데이터가 유출 되거나 사용될 수 있다.
다음은 UAF 의 예제 코드이다.
// 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;
}
}
}
- human_func
strcpy로 human→ name 에 “Human” 을 복사함
human→weight 에 입력을 받음 ( % d )
human→age 에 입력을 받음 ( %ld )
그러고 free 를 하는데 초기화가 없음 - robot_func
strcpy로 robot→name 에 “Robot” 을 복사함
robot→weight 에 입력을 받음
robot→fptr 이 NULL이 아니면 robot→fptr(); 실행
아니면 robot→fptr = print_name
robot→fptr(robot);
이후 free 하는데 초기화가 없음 - custom_func
c_idx > 9
size를 입력 받음
size가 0x100 이상
custom[c_idx] 에 size만큼 malloc
custom[c_idx] 에 size-1 만큼 입력 받음
custom[c_idx] 값을 출력
idx를 입력 받아서 ( idx < 10 && custom[idx])
free(custom[idx])
custom[idx] = NULL 로 초기화해줌.
Human과 Robot 의 구조체 크기가 같고
메모리를 초기화 하지 않기 때문에 UAF가 발생해 age 에 남긴 값이 Robot의 fptr에서 그대로 쓸 수 있음
따라서 원하는 함수를 실행할 수 있음
custom_func 함수도 메모리 영역을 초기화 하지 않기 때문에 UAF가 발생
0x500 (1280) 크기만큼을
custom(0x500, aaaa, -1)
custom(0x500, aaaa, -1)
custom(0x500, aaaa, 0)
custom(0x500, b, -1)
를 했을 때
heap chunks 상황이다
이렇게 하면 해제된 청크의 fd, bk 가 정리되지 않고 b만 덮여 출력되어 leak 이 된다.
leak되는 주소는 main_arena+96 이다. (unsorted bin)
libc 가 매핑되어있는 주소를 vmmap으로 찾고 이를 빼면 offset이 나온다
여기서는 libc 차이때문에 다르게 나왔다.
정상적으로 나오면 0x3ebc42가 offset이 된다. (42 는 data 에 B를 입력했기 때문에 하위 바이트가 덮였다. C를 입력하면 0x3ebc43으로 나온다.)
그냥 free 하고 data를 정리 안해줘서 fd 에 쓰인 main_arena 주소가 leak 되고 (uaf)
이로 인해 one gadget 주소를 만들 수 있음
Robot func_pointer overwrite
libc_base를 구했으면 one_shot 가젯의 주소를 알 수 있다.
Robot 구조체에는 함수 포인터가 있으므로 이를 overwrite 하면 이를 호출할 수 있다.
human과 robot 구조체의 크기가 같으므로
human을 해제하고 robot을 할당하면 robot은 human이 사용했던 영역을 그대로 사용한다.
human 구조체의 age 와 fptr 의 위치가 같기 때문에 age 에 one_gadget의 주소를 넣고 robot을 호출하면 one_gadget이 실행될 것이다.
이 Dangling pointer 를 이용하면 해제된 청크의 주소를 가지고 다시 해제할 수 있다.
이를 Double Free Bug 라고 한다.
Double Free
연결리스트에 중복된 힙 주소를 추가하기 때문에 두 개의 객체가 동일한 메모리를 사용할 수 있다.
ptmalloc2 할당자는 Double Free 를 방지하기 위해 동일한 힙 청크를 연속으로 여러 번 해제할 수 없도록 하는 코드가 존재한다.
if (__builtin_expect (old == p, 0)
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
이전에 해제한 힙 청크의 포인터인 old
와 현재 해제할 힙 청크의 포인터인 p
가 같다면 여러 메시지를 출력하고 비정상 종료하게 된다.
힙을 할당하고 해제하면 할당된 힙 청크 주소가 크기에 맞는 bin
에 들어가고 같은 bin
의 크기로 재할당 요청이 오면 순차적으로 할당이 된다.
Double Free 가 발생해 중복된 주소가 bin
에 들어가 있으면, 같은 크기로 할당 요청이 여러 번 들어왔을 때 동일한 메모리에 두 개의 객체가 할당될 수 있다.
이때 입력이 가능한 경우 free된 힙 청크의 메타데이터를 조작할 수 있다.
우회 코드
old와 p포인터를 다르게 하여 검증을 우회하고 Double Free를 발생 시키는 코드
// gcc -o dfb2 dfb2.c
#include <stdlib.h>
int main()
{
char *ptr = malloc(32); // 0x602010
char *ptr2 = malloc(32); // 0x602030
free(ptr);
free(ptr2);
free(ptr);
return 0;
}
마지막 ptr
이 해제될 때 ptr2
의 주소가 저장되고, p
포인터에는 ptr
의 주소가 저장되면서 old
와 p
는 다른 주소를 가지기 때문에 검증을 우회하여 Double Free 가 발생한다.
중복된 주소가 fastbin에 있다.
fstbin에서 Double Free 가 발생하고 같은 bin의 크기로 할당 요청이 들어온다면
0xd69010, 0xd69040, 0xd69010 주소에 순차적으로 힙을 할당하여 두 개의 객체가 하나의 메모리를 사용할 수 있게 된다.
이 글은 옵시디언을 이용해서 작성되었습니다.
'TOOR' 카테고리의 다른 글
[TOOR] 19.1 Stack Pivot (0) | 2023.12.06 |
---|---|
[TOOR] 24.2 cpp_smart_pointer_1 write up (0) | 2023.11.17 |
[TOOR] 23. Heap chunk 구조 (2) | 2023.10.17 |
[TOOR] 22. FSOP (2) | 2023.10.17 |
[TOOR] 13.2 pwnable.xyz badayum write up (1) | 2023.10.16 |