Pwnstar

Pwnable.kr uaf 본문

Pwnable.kr/Toddler's Bottle

Pwnable.kr uaf

포너블처돌이 2020. 3. 17. 01:23

이번에는 앞의 두 문제를 건너뛰고 uaf와 unlink 순서대로 라이트업을 쓰려고 한다.

 

이유는 이 두 문제 모두 스스로의 힘으로 풀지 못하고 룸메 도움을 많이 받았는데, heap 문제를 처음 접해봐서 그런지 이해가 잘 가지 않았기 때문이다.

 

더 확실히 이해하기 위해 천천히 다시 풀어보면서 라업을 써보려고 한다.

 

 

우선은 64비트 바이너리이고, 보호기법은 NX밖에 걸려있지 않다.

 

소스코드

#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번을 입력하면 Man으로 동적할당된 구조체와 Woman으로 동적할당된 구조체의 introduce 함수가 실행되고 아마 heap 영역에 데이터가 할당될 것이다.

 

2번 메뉴는 첫번째 인자를 atoi 함수로 숫자로 바꿔 len 변수에 저장되고 두번째 인자를 열어서 data에 첫번째 인자의 길이만큼 동적할당한다. 즉 두 번째 인자로 할당해 줄 것은 아마 파일이 되겠지

 

3번 메뉴는 동적할당해준 구조체를 삭제한다.

 

우선 1번으로 동적할당해준 객체들이 heap에 어떻게 저장되는 지 확인해보자.

 

 

1번 메뉴를 누르고 메뉴가 출력되기 직전 브레이크포인트를 걸고 실행해보았다.

 

 

위에는 Jack이라는 문자열, 아래 0x19 = 25로 나이인듯 하고, 0x0000000000401570 이 주소와 0x0000000000401550 이 주소는 모두 0x000000000040117a라는 주소를 가리키고 있었는데

 

 

give shell 이라는 쉘을 획득할 수 있는 주소였다.

 

 

이 부분을 잘 이용해야할 것 같다.

 

3번 메뉴에도 브레이크 포인트를 걸고 다시 실행해보았다.

 

그 전에 1번 메뉴의 asm을 잘 보면

 

rax에 rab-0x38의 값을 넣고, 그 값에 +0x8을 해준 다음 그 값을 rdx에 넣고 call rdx를 한다. 때문에 결과적으로는 0x401578에 있는 introduce 함수가 실행되는데, 이 점을 유의해야한다.

 

3번 메뉴를 누른 후의 모습인데 0x0000000000401570 이 주소만 사라지고 전부 그대로 남아있는 모습이다. 그럼 이제 2번 메뉴를 확인해보기 위해 2번에도 break point를 걸고 실행하는데, ex.txt라는 파일을 만들어 A를 4개 입력해주었다.

 

첫 네바이트(0x0000000000401550) 주소가 있던 부분이 AAAA로 덮여있었다. 이 부분을 give shell 주소 - 0x8로 써주고 입력값을 잘 주면 될 것 같다.

 

아 우선 테스트하기 앞서 서버에서는 속도가 너무 느려서 로컬로 바이너리를 먼저 가져와 테스트를 진행하였다.

 

시나리오는 다음과 같다.

 

우선 3번으로 heap 부분의 영역을 delete해준 다음, 2번메뉴로 원하는 부분과 원하는 사이즈만큼을 덮어써주기 위해 두번 실행한다 그 이후 1번 메뉴를 실행하면 give shell이 실행될 것이다.

 

 

로컬에서 성공적으로 쉘을 획득했으니 이제 서버에서 진행해보도록 하겠다.

 

 

'Pwnable.kr > Toddler's Bottle' 카테고리의 다른 글

Pwnable.kr blukat  (0) 2020.03.21
Pwnable.kr unlink  (0) 2020.03.20
Pwnable.kr lotto  (0) 2020.03.16
Pwnable.kr blackjack  (0) 2020.03.16
Pwnable.kr coin1  (0) 2020.03.16
Comments