[DreamHack] [Reversing] basic_heap_overflow

image.png

소스코드 분석부터 진행하겠다.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

struct over {
    void (*table)();          // 함수 포인터 1개를 멤버로 가지는 구조체
};

void alarm_handler() {
    puts("TIME OUT");         // 타임아웃 시 메시지 출력
    exit(-1);                 // 즉시 종료
}

void initialize() {
    // 표준입출력 버퍼링 제거: 입출력 동작이 즉시 반영되도록 설정
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    // 30초 타임아웃 설정
    signal(SIGALRM, alarm_handler);
    alarm(30);
}

void get_shell() {
    // 쉘 획득 함수: /bin/sh 실행
    system("/bin/sh");
}

void table_func() {
    // 기본적으로 구조체의 함수 포인터가 가리키는 함수
    printf("overwrite_me!");
}

int main() {
    // 힙에 0x20(32) 바이트 크기의 버퍼 할당
    char *ptr = malloc(0x20);

    // 힙에 0x20 바이트 크기의 구조체(over) 할당
    struct over *over = malloc(0x20);

    initialize();

    // 함수 포인터를 정상 함수(table_func)로 초기화
    over->table = table_func;

    // 취약 지점: 길이 제한 없는 %s로 입력을 받아 ptr로 복사
    // ptr는 0x20 바이트 크기인데, 더 긴 문자열을 넣으면 힙 오버플로우 발생
    scanf("%s", ptr);

    // 함수 포인터가 NULL이면 종료
    if( !over->table ){
        return 0;
    }

    // 함수 포인터 호출
    over->table();
    return 0;
}

어떤 보안 정책들이 걸려있는지 확인해본다.

image.png

get_shell의 주소 또한 파악해둔다.

image.png

이제 중요한 포인트는 오프셋이 얼마인가 인데, 확인을 위해 scanf@plt 직후에 breakpoint를 걸어 확인해줄 것이다.

image.png

메인 함수 확인 결과 main+74에서 scanf@plt가 호출된다.

이 후, gdb에서 heap 명령어로 청크를 확인해보면,

image.png

두개의 작은 청크가 보인다.

Addr: 0x804b198 Size: 0x30   ← 첫 malloc(0x20): ptr
Addr: 0x804b1c8 Size: 0x30   ← 둘째 malloc(0x20): over

32비트 glibc에서 Size: 0x30이면 헤더 0x8+ 사용자영역 0x28로 정렬 되기 때문에, 주소로 확인이 가능하다.