System&Write up/Pwnable.kr

[pwnable.kr] Toddler's Bottle - uaf

Jubil 2018. 1. 31. 00:17
반응형

[pwnable.kr] Toddler’s Bottle - uaf

 


 


 

UAF (Use After Free) 문제입니다. 코드가 cpp이여서 c++ 공부와 UAF 공부를 했습니다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

#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;    

}

Colored by Color Scripter

cs

 

 

Man, Woman ClassHuman Class를 상속 받습니다.

introduce() 함수와 age, name 멤버 변수를 가지고 있습니다.

 

각 생성자는 이름과 나이를 입력 받아서 멤버 변수에 저장해주는 기능을 가지고 있습니다.

 

궁극적 목표는 give_shell 함수를 실행하는 것이겠죠.

 

메뉴 1. use, 2. after, 3. free 가 있습니다.

 

1use 메뉴는 m 객체의 introduce, w 객체의 introduce를 순서대로 실행합니다.

2after 메뉴는 argv[1]을 정수로 변환해서 그만큼 동적 할당을 하고 argv[2] 경로의 파일을 ReadOnly로 열어서 argv[1]만큼 동적 할당한 data에 읽어옵니다.

3free 메뉴는 mw 객체를 해제하는 기능을 합니다.

 

 

그럼 give_shell 함수는 어디에 있을까요?

 


 

아까 64bit라고 말하지 않았지만, 여기서 알 수 있습니다! ..

 

 


 

main을 분석해보면, 핸드레이를 연습한 덕분에 저 부분이 switch-case문이라는 걸 알 수 있습니다.

 

1번 메뉴를 보면 rbp-0x38m 객체이고, 거기에 *를 해서 다시 rax에 담습니다.

그리고 그 값에 8을 더하고, 다시 *를 해서 rdx에 담습니다.

그리고 rdx call합니다.

 

마찬가지로, w 객체는 rbp-0x30입니다.

 

 

일단 m만 따라가보면서 레지스터를 보겠습니다.

 


 

rax = *(rbp-0x38) 합니다.

 


 

rax = *rax 합니다.

잘 보면 *raxgive_shell 주소입니다.

 


 

rax += 8 합니다.

 


 

rdx = *rax 합니다.

아까 그 주소는 Manintroduce 함수입니다.

rax8을 더하고 *하니까, 원래 rax 값이 8만큼 작다면 8을 더했을 때 가리키고 있는 값은 give_shell의 주소일 것입니다.

 

그렇다면 일단 free하고 use해보겠습니다.

 

 


 

보시면 rax에 처음 들어온 주소가 가진 값이 NULL입니다.

그 값을 rax에 넣고, 8을 더했습니다.

그리고 그 주소에 접근해서 값을 가져오려 하는데 펑. 세폴이 납니다.

 

 

그렇다면 m 객체와 w 객체가 가리키고 있는 힙 메모리를 관찰해봅시다.

일단 값의 변화를 관찰하기 위해서 /tmp/jaemin1/input 이라는 파일에 AAAA를 저장해 놓았습니다.

 

(gdb) r 16 /tmp/jaemin1/input

 

다시 use로 가겠습니다.

 

 


 

힙을 정말 모르지만, 빨간색 1번이 chunk이고, 파란색 1번이 저 주소에 더하고 *하고 호출하는 걸 보니 함수의 Base 같은 느낌..?

빨간색 2번이 age, 파란색 2번이 name입니다.

 

보이는 *0x401570, *0x401550give_shell 함수이고, *(0x401570+8), *(0x401550+8)introduce 함수일 것입니다. 확인해보겠습니다.

 



 

정답입니다. 그렇다면 free하고 2번으로 재할당 할 때 어떻게 값이 변하는지 보겠습니다.

 

일단 확인을 위해서 0x1395c50을 기억합시다.

 

 

freeafter한 상태입니다.

 


 

나중에 freew객체에 값이 써졌습니다. 하지만 나중에 use해서 UAF를 터뜨릴 때 m->introduce가 먼저 호출되니 m객체에 값을 쓰기 위해서 한번 더 after 하겠습니다.

 


 

생각대로 잘 변조되었습니다. 저 주소에 +8해서 *하기 때문에 8 작은 값으로 변조해야 합니다.

 


 

0x401570-80x401568을 넣어주면 give_shell 함수가 실행될 것입니다.

 

 

64bit 바이너리이기 때문에,

python ‘print “\x68\x15\x40\x00\x00\x00\x00\x00”’ > /tmp/jaemin1/input

8바이트 주소로 해줘야 합니다. (개행 문자 \x0a 때문에 안될 수 있음)

 

 


flag를 얻었습니다.

반응형