ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • pwnable.kr - uaf
    pwnable.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
Designed by Tistory.