-
pwnable.kr - uafpwnable.kr 2023. 4. 24. 17:29
* ASLR이 적용되어있는 환경에서 여러번 디버깅했기때문에 스크린샷마다 주소가 조금씩 다를 수 있습니다.
#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; }
checksec 적용된 보호기법을 확인한다.
switch statement switch문에 break point를 걸어 각각의 분기 로직을 확인한다.
1. use 생성된 각 객체의 introduce() 메소드는 [[rbp-0x38]]+8 을 통해 호출됨을 확인할 수 있다.
* 참고
0x401578 => 0x4012d2 (introduce)
0x401570 => 0x40117a (give_shell)heap new man(name, age)을 통해 객체 생성 시 청크를 하나만 할당할 줄 알았는데 실제로는 age의 경우 chunk의 size가 0x20byte (0x18d2e90 = Man 객체의 age), name의 경우 chunk의 size가 0x30byte (0x18d2eb0 = Woman 객체의 name)으로 각각 할당되어있다. (64비트이기때문에 0x10바이트씩 증감, +0x1은 prev in use flag)
"3. free" 후 "2. after"를 통해 데이터를 할당한 다음 heap 영역을 확인한다(w 객체의 name을 관리하던 chunk를 reallocate함)
두 번째 실행인자를 통해 가져온 파일의 데이터가 [rbp-0x38]에 쓰여있는것을 확인할 수 있다. 따라서 파일에서 가져온 데이터가 0x401568이라면 give_shell이 호출될 수 있다. ([[rbp-0x38]]+8 = 0x401570 (give_shell)) (pie 미적용)
Segmentation fault debugging 다만 메모리 할당을 한 번만 할 경우에는 위처럼 Segmentation fault 에러가 발생한다. tcache의 경우 LIFO를 사용하고 있는데 "3. free"에서 m 객체 해제 이후 w 객체를 해제하기 때문에 "2. after"에서 메모리 할당 시 우선적으로 w 객체가 사용하던 name 청크가 재사용된다. 반면에 "1. use"에서는 m 객체의 introduce가 먼저 호출되기 때문에 free상태의 m->name 청크가 재사용되면서 segmentation fault가 발생한다. 따라서 free 된 m, w의 name 청크에 값을 모두 넣어주기 위해서 "2. after"를 두 번 호출해야 한다.
free 상태의 m객체의 name 청크와 reallocate된 w객체의 name 청크 flag 728x90'pwnable.kr' 카테고리의 다른 글
pwnable.kr - asm (0) 2023.05.05 pwnable.kr - memcpy (0) 2023.05.04 pwnable.kr - cmd2 (0) 2023.04.20 pwnable.kr - cmd1 (0) 2023.04.20 pwnable.kr - lotto (0) 2023.04.15