9oat's LAB
Memory Leak 기법 본문
원격 Exploit에서 함수 주소 offset 계산과 함께 사용하는 기법인 Memory Leak에 대하여 알아보자.
Memory Leak은 말 그대로 메모리를 유출시키는 것인데 방법에 따라서 인접 메모리 영역의 값을 유출시킬 수도 있고, 특정한 주소의 값을 유출 시킬 수도 있다.
여러가지 방법들이 있겠지만 본 게시물은 Buffer Overflow를 이용한 기본적인 Memory Leak 두 가지를 다룬다. 사실 이것밖에 모름
1. Overflow를 이용해 변수와 변수 사이의 Null을 덮어씌워 인접한 메모리의 값을 유출시키는 기법
2. Return To Library를 이용해 특정 주소의 값을 유출시키는 기법
첫 번째 방법부터 보자.
변수와 변수 사이에는 Null Byte가 존재하고, 보통 출력 함수들이 값을 출력할 땐 이 Null Byte를 만날 때 까지 출력하는데, Overflow를 이용하여 Null Byte를 덮어씌움으로써 인접한 메모리 값까지 이어서 출력하게 되는 것이다.
무조건 되는 것은 아니고, 입력 값의 끝에 Null Byte가 삽입되면 안된다.
보통 로컬에서 버퍼에 입력 값을 저장할 때 사용하는 함수인 strcpy, fgets, scanf 등은 입력한 값의 끝에 Null Byte를 삽입하기 때문에 이 방법으로 Memory Leak을 할 수 없다. 하지만 recv, read 등의 함수들은 입력 값에 별다른 처리를 하지 않기 때문에 이 방법으로 Memory Leak이 가능하다.
[사진 1] 예제 Binary
예제로 쓸 Binary는 2017 HUST의 문제 중 하나인 attackme를 사용했다. 간단한 Bof문제이므로 이것저것 해보기에 좋았다.
read를 이용해 200자까지 입력받고 입력 값의 길이 만큼 다시 출력하는 프로그램으로, 별다른 보호기법도 없기 때문에 Memory Leak을 하기에 딱 적당하다. 버퍼의 크기가 100이므로 104만큼 입력하면 return address를 출력할 것이다.
[사진 2] Leak
A를 104개 넣었더니 A 뒤에 알 수 없는 값들이 함께 출력됐다. 보통 메모리에 있는 값은 ASCII로 표현되지 않는 값들이니 Leak된 게 분명하다.
다만 정확히 어떤 값인 지는 알 수 없는데, 그래서 생각해 낸 방법으로 Redirection을 이용하는 것이다.
[사진 3] 확인
파일로 출력 결과를 저장하고, 해당 파일을 Winhex와 같은 Hex Editor로 열어보면 값을 확인할 수 있다.
return address는 0xf7e1d637이다.
[사진 4] __libc_start_main+247
게시물의 주제에서 좀 벗어난 내용이지만 알아두면 좋은 정보로,
main의 return address에는 보통 __libc_start_main 이라는 함수의 +247 주소가 저장된다. 이 주소는 Libc-Database에서 offset을 제공하는 주소로, offset___libc_start_main_ret이라는 이름으로 offset 정보를 제공한다. 그러니까 이 주소를 Leak할 수 있으면 Libc-Database를 이용하여 exploit에 사용할 수 있다는 것.
다음은 두 번째 방법으로, Return To Library를 이용해 특정 주소의 값을 유출시키는 기법이다.
아무 함수로 RTL하는 건 아니고 send나 write같은 함수를 이용한다. 이 같은 함수들은 특정 값(buf 등)을 출력하거나 전송할 수 있는 함수들인데, 이 특정 값을 내가 원하는 주소로 인자를 구성한 뒤 RTL하여 값을 출력시키는 것이다.
이것도 조금은 주제에서 벗어난 이야기지만, 보통 CTF 등의 문제를 풀 때 Memory Leak의 목적은 함수 offset을 계산하기 위함이다. 그래서 이 방법으로 가장 많이 Leak하는 것은 함수 GOT의 값으로, 예를 들어
write(1,&write@GOT,4);
이런식으로 인자를 구성하여 RTL한다면 write@GOT에 저장된 write의 Library주소를 Leak할 수 있는 것이다. write는 [사진 4]에서 볼 수 있듯이 Libc-Database에서 offset 정보를 제공한다. 그래서 write의 Library 주소를 획득하면 exploit에 사용할 수 있을 것이다.
예제 Binary는 첫 번째 방법 때와 동일하다.
[사진 5] write@PLT, write@GOT
PLT와 GOT주소는 Binary 에서 쉽게 확인 가능하고 변하지 않기에 RTL하기 좋다.
Memory Leak을 위한 Payload는 다음과 같다.
| DUMMY * 104 | write@PLT | DUMMY * 4 | 0x1 | write@GOT | 0x4 |
Buf+SFP main's ret write's ret stdout size
이렇게 하면 Write의 Library 주소를 Leak할 수 있을 것이다.
[사진 6] Leak!
정상적으로 Payload가 동작한 것 같다. 끝에 주소로 보이는 0xf7edab60이 출력되었다.
[사진 7] write
확인해보니 write의 주소가 맞았다.
지금은 Memory Leak을 하는 것이 목적이므로 간단히 RTL을 이용하여 함수의 Library 주소만 획득했지만, 만약 셸을 획득해야하는 상황이고 ASLR이 동작하는 환경이라면 여기에 다른 함수를 추가한 RTL Chaining으로 Payload를 구성하면 된다.
이것으로 Memory Leak은 끝. 다른 Leak기법이 생기면 추가해야지
참고문서 : MEMORY_LEAK_TECHNIQUES_cd80
Ubuntu 16.04 / gcc 5.4.0
'Study > System' 카테고리의 다른 글
Pwnable.kr input 풀다가 궁금한 거 (0) | 2017.07.16 |
---|---|
Memory Leak 관련한 삽질(Feat. Buffering) (0) | 2017.07.02 |
Libc-Database를 이용한 함수 주소 구하기 (0) | 2017.06.28 |
Dynamic Link 시 함수 호출 과정 (0) | 2017.06.26 |
함수 주소에 대한 궁금증 (0) | 2017.05.20 |