문제 화면입니다.
서버에 접속해서 문제를 확인해봅니다.
#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 |