문제 화면입니다.

서버에 접속해서 문제를 확인해봅니다.


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

소스코드는 이렇습니다. uaf 취약점에 대한 문제라고 생각이 됩니다.

uaf란 Use After Free의 약어로 Heap 영역에서 발생하는 취약점 입니다.

동적할당시 free하고 전에 free한 메모리와 같은 크기의 메모리를 재할당을 할때 그 메모리 주소에 재할당을 합니다. 

이를 이용해 문제를 풀어보도록 하겠습니다.


gdb로 디버깅을 하면 아래와 같습니다.



uaf.cpp를 보면 case라는 분기문이 있습니다. 

op라는 값으로 1,2,3을 확인하는데 main+241 부분부터 보면 2,3,1 순서로 비교하는 코드가 있습니다.




1을 비교하는 부분을 살펴보기 위해 아래와 같이 bp를 걸어봤습니다.




그리고 run 후 1을 선택합니다.



$rax에는 1이 입력되었습니다. 이제 비교를 하겠네요.


비교하기전에 먼저 main함수를 다시 살펴보겠습니다.



rax가 1이니 main+258에서 main+265로 jump 할 것 입니다. 



이를 분석해보면 $rbp-0x38에는 0x0199d040이라는 주소가 들어있고 이주소는 0x401570을 가리킵니다. 

이 값은 Human클래스에 있는 give_shell()이란 함수입니다.

이를 통해 쉘을 따도록 해야하는것 같습니다.

다시 내용으로 돌아와서 main함수를 해석해보면 rbp-0x38이 가리키는 주소(0x0199d040)를 $rax에 mov합니다. 

그리고 다시 $rax가 가리키는 주소(0x401570)를 $rax에 mov합니다. 그리고 $rax+8(0x401578)을 $rax에 mov합니다. 

그리고 rdx에 함수 주소를 mov한 후 호출합니다. 이 함수는 introduce()입니다. 

저 rdx 값이 give_shell()이란 함수로 바뀌면 쉘을 딸 수 있습니다. 이제 어떻게 저기에 원하는 값을 입력할지 고민을 해봅시다.

분기문에서 2를 입력하면 argv[2]로 들어온 인자의 값을 이름으로 하는 파일을 read하고 data에 argv[1]의 인자로 들어온 값만큼 값을 읽어옵니다.


[$rbp-0x38]+0x8 = 0x401578

X + 0x8 = 0x401570

따라서 X = 0x401568 입니다.


/tmp/sso/file 이라는 파일을 생성합니다.



그리고 인자를 주어 실행합니다.



m과 w의 동적할당을 free로 해제하고 after로 give_shell()의 주소를 재할당한 후 쉘을 딸 수 있습니다.


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

[pwnable.kr] shellshock 1p  (0) 2016.08.20
[pwnable.kr] mistake 1p  (0) 2016.08.19
[pnwable.kr] random 1pt  (0) 2016.07.29
[pwnable.kr] passcode 10p  (0) 2016.07.29
[pwnable.kr 2번] collision 3pt  (0) 2016.07.27

+ Recent posts