kaka09
pwntools을 이용한 간단한 ROP 문제풀이 본문
여태까지 포너블 공부를 함에 있어서 local환경에서 메모리 보호기법 없이 단순히 공격기법들의 원리만 파악하기 위한 공부를 했었다.(깊이가 없는..)따라서 이번에는 메모리 보호기법이 적용된 상태에서 원격(로컬을 원격처럼)으로 익스플로잇 하는 방법들을 공부해볼 생각으로 본 포스팅을 작성할 예정이다.
[사진]1-1
위 사진은 해당 바이너리를 IDA의 Hex-Rays를 통해 소스코드를 복원한 내용이다. 내용을 살펴보면 1~4번까지 메뉴를 선택할 수 있다. 랜덤으로 설정된 가격을 기준으로 코인을 사고 파는 형태의 프로그램으로 판단 할 수 있다. 취약점 벡터는 46번째 줄에서 발생한다. 사진에는 안안나와 있지만 v6 변수는 bp-14h 영역에 할당된다. 하지만 46번째 줄에서 scanf로 입력 받을때 50 바이트 만큼 v6에 값이 입력된다. 따라서 memory corruption이 발생된다.
이제 어떤 메모리 보호기법이 적용되어 있는지 살펴보자
[사진]1-2
NX를 제외하면 비활성화 되어 있다. 해당 바이너리에 NX가 걸려 있기 때문에 스택상에서 쉘코드를 입력하는 행위는 불가능하다. 또한 운영체제에서 ASLR 또한 적용되어 있다. 따라서 Memory Leak 기법을 이용한 RTL&ROP 공격을 수행해야 한다. pwntools를 이용하여 Memory Leak을 수행해보자
[사진]1-3
context() 메소드를 호출하여 해당 바이너리의 아키텍쳐,OS, 오더링을 지정한다.
그다음 원격의 경우 remote('binary',port) 로컬의 경우 process('binary') 형태로 지정할 수 있다.
이 다음의 경우 send와 recv를 이용하여 값을 전달하고 넘겨 받는 과정을 진행한다.
[사진]1-4
s.send를 통해 "A"*4 와 "3"을 전송하는 이유는 바이너리를 실행하면 맨처음 이름을 입력받고, Memory Corruption 취약점이 3번 메뉴에서 터지기 때문이다. 무튼 send를 통하여 3번 이름변경 루틴에 접근 한 다음 EBP 영역까지 오버플로우 시켜 RET에서 printf 함수를 호출하여 Memory Leak을 시도하는 과정이다.
[사진]1-5
printf 함수의 주소는 .text영역에 존재한다. aslr은 프로그램 실행시 스택,힙,라이브러리영역의 주소를 랜덤하게 매핑하는 보안모듈이다. 하지만 코드영역의 주소까지 랜덤하게 매핑시키지 않는다는 특징이 존재한다. 따라서 .text영역에 존재하는 함수의 주소를 이용하면 aslr을 신경 쓰지 않아도 된다.
[사진]1-6
payload+=p32(0x804871d)를 입력한 이유는 프로그램을 종료시키지 않게 하기 위해서다. 보통 RTL 방식으로 함수를 호출할때,
함수명 |
리턴 |
인자값 |
형태로 실행이된다. 즉, 호출될 함수+4바이트가 ret이기 때문에 Memory Leak 이후 쉘을 획득 하기 위해 프로그램이 종료되면 안된다. 따라서 메인문의 최상위은 0x0804871d를 입력하여 Memory Leak 수행 후 프로그램을 다시 실행되게 흐름을 조작하기 위함이다.
printf로 Memory Leak을 시도한 대상은 __libc_start_main의 주소이다. (got 주소를 입력한 이유는 생략하겠다.) __libc_start_main의 주소를 왜 Leak을 했냐? 라는 질문 이전에 우리는 ASLR이 어떠한 원리로 주소를 랜덤하게 매핑하는지 알아야 한다. c언어로 작성된 프로그램은 main문 이전에 __libc_start_main문을 거친 다음에 main문을 수행한다.
__libc_start_main문은 환경변수,malloc,라이브러리 영역들을 초기화 하는 역할을 담당한다고 어느 블로그에서 본 기억이 있다. gdb를 이용하여 동적 분석을 통해 __libc_start_main의 위치를 살펴보자
[사진]1-7
gdb 동적 분석으로 __libc_start_main의 주소를 확인한 결과 0xf7e20540으로 매핑되어 있다. 이번에는 system 함수의 주소를 확인해보자
[사진]1-8
system 함수의 주소는 0xf7e42da0으로 매핑되어 있다. system-__libc_start_main(0xf7e42da0-f7e20540) 을 계산해보자 0x22860(141,408) 이라는 값이 나왔다. 함수와 함수간 거리의 차이를 오프셋이라 하는데, 이는
__libc_start_main+141,408(오프셋)=system() 이 존재 한다는 말과 같다. ASLR 기법이 주소를 랜덤하게 매핑시킨다고는 하나, 이 오프셋 값은 항상 일정하기 때문에 Memory Leak 으로 특정 함수의 주소를 파악 할 수 있으면 다른 함수의 주소를 쉽게 구할 수 있다. 이제부터는 실제로 Memory Leak을 통하여 과연 오프셋 값이 항상 일정한지 검증하도록 살펴보겠다.
[사진]1-9
사진을 살펴보면 __libc_start_main 의 끝 주소는 540 으로 끝나고, __isoc99_scanf의 끝 주소는 0c0 으로 끝난다. 이전 설명에서 ASLR은 주소값이 변해도 라이브러리의 오프셋 위치는 변하지 않는다는 설명을 하였다. 여기서 한가지 사실을 더 알수 있는데, 주소의 끝 3자리 또한 변하지 않음을 알 수 있다. 직접 GDB로 동적 분석을 통해 매핑되는 주소의 끝자리를 살펴보자
[사진]1-10
실제 실행되는 프로그램과 끝 3자리(12bit)가 일치함을 확인 할 수 있다. 하지만 몇몇의 함수는 GDB상,실제상에서 차이가 남을 확인하였다. 그래서 libc 파일을 뒤져봐도 libc 파일 또한 몇몇은 일치하지 않음을 확인하였다. 이건 왜 이렇게 되는건지 추후에 연구를 해봐야 할 부분..
아무튼 문제를 풀기위해 팩트만 짚고 넘어가자
첫째, ASLR은 주소가 변해도 함수와 함수간의 거리 즉, 오프셋은 항상 일정하다.
둘째, ASLR은 끝의 12BIT는 항상? 동일하다.
이 두가지 팩트를 통해 알 수있는 한 가지 사실은 오프셋을 통하여 실행되지 않는 함수의 주소를 구할 수 있다는 점이다. [사진 1-8,1-9 과정에서 설명 한 것 처럼]
따라서 다음과 같은 페이로드를 작성하여 익스플로잇을 시도해 보았다.
[사진]1-11
system 함수와 /bin/sh의 가젯을 찾기 위해 오프셋을 이용하였다.
system 함수는 __libc_start_main + 141408 위치에 존재하고
/bin/sh의 주소는 __libc_start_main + 1324139 위치에 존재한다.
만약 해당 오프셋이 정확하다면 프로그램이 실행되면서 RET 부분에서 system("/bin/sh")가 정상적으로 실행 될 것이다. 제대로 쉘이 획득 되는지 실행을 시켜보자
[사진]1-12
정상적으로 쉘을 획득함을 확인 할 수 있다. 다음에는 아까 위에서 해결못한 주소의 끝 12bit가 서로 다른 이유에 대해 연구를 진행해야겠다.
'Pwnable' 카테고리의 다른 글
pickle 취약점을 이용한 Nebula level17 풀이 (0) | 2017.08.07 |
---|---|
pickle 취약점 으로 Nebula level 17 풀이 (0) | 2017.08.07 |
문제풀이로 알아보는 TICTOU 취약점 (0) | 2017.08.06 |