이번 포스트에서는 Pwnable의 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;
}
## 문제 해석 :
main() 함수 위주로 살펴보니, use, after, free 3가지의 선택지가 있고 각 선택지는 다음과 같이 동작한다.
- use : 각 개체의 introduce() 함수 실행
- after : char 객체 생성
- free : man, woman 객체 초기화
또한 Human 클래스의 give_shell() 함수를 통해 쉘을 딸 수 있는 것으로 보인다.
## 문제 풀이 :
man과 woman 객체를 free를 통해 초기화한 다음에, after를 통해 같은 크기의 새로운 char 객체를 생성한 다음에, introduce() 함수의 주소 대신 give_shell() 함수의 주소로 덮어씌우면 된다.
그럼 먼저 give_shell()의 주소를 알아보겠다.
286번째줄을 보면 call 함수를 통해 rdx가 호출된 것을 알 수 있다. 여기서 아마 rdx가 바로 introduce() 함수일 것이다. 또한 이전에 rdx는 rax에 0x8이 더해진 것을 알 수 있다. rax의 값을 확인해보겠다.
확인해본 결과 give_shell() 함수의 주소값이 0x40117a인 것을 알 수 있다. 즉, 0x401570가 가리키는 주소값이 0x40117a(give_shell())이다.
그러나 introduce() 함수가 rax+0x8 후에 실행한 것으로 보아 give_shell() 함수를 실행하려면 역시 rax+0x8 한 후의 값을 실행해야 하므로, 우리는 구해진 0x401570에다가 0x8를 빼야한다. 즉, 0x401568를 넣어야한다.
## 최종 결과 :
### 공격문 : python -c "print '\x68\x15\x40\x00'" > /tmp/jy05un_uaf/exploit
### 실행 결과 :
Flag = yay_f1ag_aft3r_pwning
## 추가 해석 :
after를 2번 실행한 이유는 free에 m과 w 객체를 동시에 delete() 했기 때문이다. 즉, after를 한 번 실행하면 LIFO(Last In First Out) 구조에 의해서 w만 할당되고 main() 함수에서 m이 w보다 먼저 실행하기 때문에 m까지 할당해주어야한다.
case 3:
delete m;
delete w;
break;
'Wargame > Pwnable' 카테고리의 다른 글
Pwnable (18. asm) (0) | 2022.09.13 |
---|---|
Pwnable (17. memcpy) (0) | 2022.07.21 |
Pwnable (15. cmd2) (0) | 2022.07.13 |
Pwnable (14. cmd1) (0) | 2022.07.12 |
Pwnable (13. lotto) (0) | 2022.07.11 |