cgy12306

[pwnable.kr] uaf 본문

Wargame/pwnable.kr

[pwnable.kr] uaf

cgy12306 2020. 2. 2. 17:30

[uaf]


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

코드를 간략하게 분석해보면 1, 2, 3을 입력을 받게 된다.

1을 입력했을 때 introduce가 시작된다.

2를 입력했을 때 첫번째 인자만큼 동적할당을 해준다.

두번째 인자를 open으로 읽어서 data 변수에 첫번째 인자만큼 값을 읽어온다.

3을 입력했을때 동적할당 해준 것을 해제한다.

하지만 해당 코드에서는 /bin/sh을 호출하는 give_shell 함수가 선언되어 있고, 이 함수를 호출하는 부분은 없다.

그러면 우리는 어떤 메모리 영역을 덮어서 give_shell 함수를 호출해야 한다는 말이다.

바이너리를 실행했을 때 use, after, free 한 후 다시 use를 하니 Segmentation fault가 떴다.

디버깅을 해보자.

gdb를 실행해보면 글자가 깨지게 출력되는데 set print asm-demangle on 명령어를 치면 깨짐현상이 해결된다.

0x400fd8에서 종료가 된다.

해당부분의 코드를 확인 해보자.

rax가 갖고 있는 주소를 따라가서 rdx에 넣는 명령어인데 rax 값을 확인해보면

rax 값은 8을 갖고있다.

rax값이 어디서 오는지 확인해보자.

rbp-0x38값을 따라가서 그 안에 있는 값을 rax에 넣고 다시 rax를 따라가 그 안의 값을 rax에 넣고 +8 을 해준다.

간단하게 표현하면 (**rbp+0x38)+8 이렇게 된다.

그리고 이 값을 다시 따라가 안의 값을 rax에 넣고, 이 값을 rdx에 넣고 호출을 한다.

일단 우리는 rdx 값을 give_shell의 주소를 넣어주면 될 듯 하다.

이 부분이 입력한 값을 비교해서 해당 함수로 점프하는 것을 볼 수 있다.

after를 호출하고 함수가 끝나기 직전에 breakpoint를 걸어서 확인해보자.

우선 바이너리 실행할 때 두번째 인자를 파일로 넘겨줘야 하기 때문에 임시파일을 생성해준다.

다시 실행 시켜보자.

r `python -c 'print "8 /tmp/uafcgy/AA"'`

rbp-0x38이 갖고 있는 값을 다시 따라가보자.

use, after, free한 후 after를 해줬을 때 0x4141414141414141이 덮여있는 것을 볼 수 있다.

다시 after를 해주자.

우리가 원하는 지점에 원하는 값을 덮을 수 있게 됐다.

0x12f9c50 지점에 give_shell의 주소를 갖고 있는 주소-8 값을 넣어줘야 한다.

give_shell의 주소를 확인해보면 0x40117a이다.

이 주소를 갖고 있는 곳을 찾아야 한다.

0x401550과 0x401570이 give_shell의 주소를 가지고 있다. 둘 중 하나를 선택하고 이 값에 -8을 계산해서 전에 만들었던 파일에 입력하자.

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

[pwnable.kr] horcruxes  (0) 2020.02.02
[pwnable.kr] memcpy  (0) 2020.01.27
[pwnable.kr] blukat  (0) 2020.01.27
[pwnable.kr] unlink  (0) 2020.01.27
[pwnable.kr] asm  (0) 2020.01.27
Comments