Step 62. 실험: 버퍼 오버플로우 — 넘친 입력의 행선지

Step 62. 실험: 버퍼 오버플로우 — 넘친 입력의 행선지

Level 1 — 프로그래밍과 컴퓨터 내부 | 난이도 ★★★★☆ | 예상 소요 시간 4시간

전제: Step 56~61을 마쳤다. 배열이 붙어 있고, C가 경계를 검사하지 않음을 안다. WSL 우분투의 bash와 gcc를 쓸 수 있다.

⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.

  • 준비물: WSL 우분투 터미널, gcc. 실측 환경은 Ubuntu 24.04, gcc 13.3.0, x86-64입니다.
  • 주의: 오늘 실험의 대상은 전부 여러분이 방금 직접 쓴 프로그램입니다. 같은 원리라도 남의 프로그램이나 서비스에 적용하면 범죄입니다.

해킹 역사상 가장 유명한 취약점, 버퍼 오버플로우(buffer overflow)를 직접 재현합니다. 지금까지의 C 수업이 사실 이 날을 위한 것이었습니다. 배열이 붙어 있다는 것(Step 59), C가 경계를 검사하지 않는다는 것(Step 59, 61), 스택에 지역 변수들이 나란히 산다는 것(Step 60) — 이 셋을 합치면 하나의 사고가 자동으로 도출됩니다. "칸보다 긴 입력을 받으면, 옆 변수가 덮인다." 오늘의 목표는 공격이 아니라 관찰입니다. 여덟 칸짜리 상자에 긴 입력을 쏟아붓고, 옆 변수가 어떻게 되는지 눈으로 봅니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • 버퍼가 무엇이며, 왜 넘치는지 설명한다
  • 넘친 데이터가 이웃 변수를 덮는 장면을 직접 관찰하고 기록한다
  • 두 변수의 주소 거리로 "몇 번째 글자부터 닿는지"를 계산한다
  • 컴파일러의 스택 보호 장치가 오버플로우를 탐지하는 것을 확인한다
  • 상한 있는 입력(%7s, fgets)으로 같은 입력을 막는다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 C 언어, WSL 우분투 bash, gcc 13.x (실측: 13.3.0, x86-64)
오늘의 명령어·옵션 gcc -Wall -O0 -fno-stack-protector(관찰용 컴파일), python3 -c "print('A'*16)" | ./프로그램(긴 입력 만들기)
필요한 개념 버퍼, 경계 검사 부재, 스택 변수 배치, \0(문자열 끝 표식), 스택 보호(stack protector)

2-1. 버퍼 — 임시 저장 상자

버퍼(buffer)는 데이터를 잠시 담아 두는 메모리 상자입니다. char buf[8]은 "글자 여덟 개를 담는 버퍼". 키보드 입력을 받거나, 파일을 읽거나, 네트워크 데이터를 받을 때 이런 상자에 먼저 담습니다.

문제는 이 상자들이 스택에서 다른 변수들과 어깨를 나란히 한다는 것입니다.

2-2. 경계 검사 부재 — C의 계약

C는 칸 밖을 읽어도, 칸 밖에 써도 혼내지 않습니다. "프로그래머를 믿는다"는 계약입니다. 여덟 칸 상자에 서른 글자를 넣는 코드도 컴파일러는 통과시킵니다.

검사는 프로그래머의 몫입니다. 그리고 역사적으로, 프로그래머들은 계속 그 검사를 잊었습니다.

2-3. 덮어쓰기 — 넘침의 행선지

여덟 칸 상자에 열여섯 글자를 쓰면, 남은 여덟 글자는 어디로 갈까요? 사라지지 않습니다. 상자 바로 옆의 메모리를 순서대로 덮어씁니다. 칸들은 붙어 있고, 쓰기는 끝을 모르니까요.

그 옆 칸에 만약 "비밀번호 일치 여부"나 "관리자 권한 플래그"가 살고 있다면? 이것이 버퍼 오버플로우가 단순한 버그가 아니라 취약점인 이유입니다.

2-4. \0 — 문자열의 끝 표식

C의 문자열은 끝에 널 문자(null character) \0(값 0)가 붙어 문자열의 끝을 표시합니다. scanf의 %s는 입력을 받은 뒤 이 \0을 마지막에 하나 더 씁니다.

그래서 여덟 글자를 입력하면 실제로는 아홉 바이트가 쓰입니다. 이 한 바이트가 오늘 실험에서 예상 밖의 관찰을 만들어 냅니다.

2-5. 관찰 설계 — 실험실 만들기

오늘의 실험 장치는 이렇습니다. 여덟 칸짜리 버퍼 옆에 secret이라는 변수를 두고, 버퍼에 긴 입력을 밀어 넣은 뒤 secret이 바뀌는지 봅니다.

안전한 내 코드, 내 컴퓨터의 랩 안에서, 관찰만 합니다. 이것이 오늘의 전부입니다.


3. 따라 하기

3-1. 실험 장치 — 이웃한 두 변수

입력 (bof1.c)

#include <stdio.h>
#include <string.h>

int main(void) {
    char secret[8] = "SAFE";
    char buf[8];

    printf("실험 전 secret: %s (주소 %p)\n", secret, (void *)secret);
    printf("buf의 주소: %p\n", (void *)buf);
    printf("두 변수의 거리: %ld 바이트\n", (long)(secret - buf));

    printf("buf에 넣을 문자열: ");
    scanf("%s", buf);

    printf("실험 후 secret: %s\n", secret);
    return 0;
}

컴파일과 실행

gcc -Wall -O0 -fno-stack-protector bof1.c -o bof1
python3 -c "print('A'*8)" | ./bof1
실험 전 secret: SAFE (주소 0x7ffe17063a08)
buf의 주소: 0x7ffe17063a00
두 변수의 거리: 8 바이트
buf에 넣을 문자열: 실험 후 secret: 

(2026-09-09 실측. 주소값은 실행할 때마다 다릅니다. 거리는 컴파일러와 옵션에 따라 달라질 수 있습니다.)

새 명령과 옵션:

  • -O0 — 최적화 끄기. 배운 구조 그대로 변수를 배치하게 합니다.
  • -fno-stack-protector — 최신 gcc의 넘침 감지 장치를 끕니다. 오늘은 관찰이 목적이니 일부러 끕니다. 이 장치가 뭔지는 3-4에서 직접 만납니다.
  • python3 -c "print('A'*8)" | ./bof1 — 긴 입력을 손으로 치는 대신 파이썬으로 만들어 파이프로 넣습니다. 입력을 코드로 만드는 이 습관은 계속 씁니다.

출력 읽는 법: 거리 8바이트 — 버퍼에서 여덟 걸음 가면 secret입니다. 그런데 여덟 글자(A×8)를 넣었을 뿐인데 secret이 빈 문자열이 되었습니다. 이유는 2-4의 \0 때문입니다. 입력 여덟 글자 + scanf가 붙이는 \0 = 아홉 바이트가 쓰였고, 아홉 번째 칸은 secret의 첫 글자 자리였던 것입니다. 문자열은 첫 \0에서 끝나니, "S"가 \0으로 덮인 secret은 텅 빈 문자열로 읽힙니다.

예측: A를 열여섯 개(A×8 + B×8) 넣으면 secret은 어떻게 될까요? 거리 숫자로 계산해 보고, 다음 실험에서 확인하세요.

: 주소를 먼저 보는 이유는 "몇 글자면 닿는지"를 예측하기 위해서입니다. 예측 → 확인. 이 순서가 분석의 기본 자세입니다.

3-2. 넘쳐라 — 관찰의 순간

같은 프로그램을 다시 실행하고 이번에는 길게 입력합니다.

python3 -c "print('A'*8 + 'B'*8)" | ./bof1
실험 전 secret: SAFE (주소 0x7fff6e675938)
buf의 주소: 0x7fff6e675930
두 변수의 거리: 8 바이트
buf에 넣을 문자열: 실험 후 secret: BBBBBBBB

(2026-09-09 실측.)

출력 읽는 법: 열여섯 글자가 들어갔습니다. 앞의 여덟(A×8)은 buf를 채웠고, 뒤의 여덟(B×8)은 경계를 넘어 secret을 통째로 덮었습니다. "SAFE"가 사라지고 B가 들어앉았습니다.

우리는 secret을 건드리는 코드를 한 줄도 쓰지 않았습니다. 버퍼에 쓴 것이 경계를 넘어 이웃을 바꾼 것입니다. 이것이 버퍼 오버플로우의 실물입니다.

예측: A×8 + "HACK"(열두 글자)를 넣으면 secret은 어떻게 될까요? 실측에서는 HACK이 되었습니다 — secret의 앞 네 바이트만 덮이고 다섯 번째 칸에 \0이 들어간 모습입니다. 직접 확인해 보세요.

: 이 한 화면이 해킹 역사의 수많은 사건의 축소 모형입니다. 입력이 데이터를 넘어 이웃 데이터를 바꾸는 순간을, 여러분은 지금 눈으로 본 것입니다.

3-3. 경계까지의 거리 재기 — 계산하는 공부

입력 (bof2.c)

#include <stdio.h>

int main(void) {
    int pin = 1234;
    char buf[8];

    printf("pin = %d (주소 %p)\n", pin, (void *)&pin);
    printf("buf 주소: %p\n", (void *)buf);
    printf("거리: %ld 바이트\n", (long)((char *)&pin - buf));
    return 0;
}

컴파일과 실행

gcc -Wall -O0 -fno-stack-protector bof2.c -o bof2
./bof2
pin = 1234 (주소 0x7ffdb8290f6c)
buf 주소: 0x7ffdb8290f64
거리: 8 바이트

(2026-09-09 실측. 주소와 거리는 환경마다 다릅니다.)

출력 읽는 법: 목표 변수와 버퍼 사이의 거리를 미리 재는 프로그램입니다. 거리가 8이면 아홉 번째 바이트부터 pin을 덮기 시작합니다. "몇 바이트를 넣어야 목표에 닿는가"를 계산하는 것 — 이것이 바로 분석의 첫 걸음입니다. 거리가 음수로 나오는 환경(목표가 버퍼보다 낮은 주소에 배치된 경우)도 있습니다. 그때는 순서를 바꿔 다시 관찰하면 됩니다. 어느 쪽이든 원리는 같습니다.

3-4. 최신 컴파일러의 보호 장치 — stack smashing detected

3-1과 같은 소스를, 이번에는 보호 장치를 끄지 않고 기본 옵션으로만 컴파일해 봅니다.

gcc -Wall bof1.c -o bof1_prot
python3 -c "print('A'*8 + 'B'*8)" | ./bof1_prot
*** stack smashing detected ***: terminated
Aborted (core dumped)

(2026-09-09 실측. 한글 로캘 환경에서는 마지막 줄이 "중단됨 (core dumped)"로 보일 수 있습니다.)

출력 읽는 법: 우분투의 gcc는 스택 보호(stack protector)를 기본으로 켭니다. 버퍼 뒤에 감시용 값(카나리, canary)을 숨겨 두었다가, 함수가 끝날 때 그 값이 덮였으면 "스택이 부숴졌다(stack smashing)"며 프로그램을 스스로 중단시킵니다. 3-2에서는 성공했던 덮어쓰기가 여기서는 탐지됐습니다.

: 이 장치가 있다는 것 자체가 배움입니다. 실제 프로그램들은 대부분 이 방어막을 두르고 배포됩니다. 관찰 실험을 위해 -fno-stack-protector로 껐던 것이고, 장치의 이름과 끄는 법, 둘 다 기억해 두세요.

3-5. 안전한 버전으로 고치기

입력 (safe.c)

#include <stdio.h>

int main(void) {
    char secret[8] = "SAFE";
    char buf[8];

    printf("buf에 넣을 문자열 (최대 7글자): ");
    scanf("%7s", buf);

    printf("secret: %s\n", secret);
    printf("buf: %s\n", buf);
    return 0;
}

컴파일과 실행

gcc -Wall -O0 safe.c -o safe
python3 -c "print('A'*8 + 'B'*8)" | ./safe
buf에 넣을 문자열 (최대 7글자): secret: SAFE
buf: AAAAAAA

(2026-09-09 실측.)

출력 읽는 법: 같은 공격 입력을 넣어도 %7s가 일곱 글자에서 잘랐습니다. 일곱 글자 + \0 = 여덟 바이트, 버퍼에 딱 맞습니다. secret은 무사합니다. 여덟 칸이면 %7s — 상한은 "칸 수 빼기 1"입니다. 더 표준적인 안전 관례는 fgets(buf, sizeof(buf), stdin)입니다.

방어는 거창한 기술이 아니라 "상자 크기를 아는 코드"였습니다.

생각해 보기: %7s로 잘렸지만 키보드로는 열여섯 글자가 들어왔습니다. 잘린 나머지 B×8은 입력 버퍼에 남아, 다음 scanf가 있으면 거기서 읽힙니다. "잘라낸 입력의 잔해"도 흐름을 예측하기 어렵게 만드는 요소라는 것, 기억해 두세요.

3-6. 덮인 정수 읽기 — 글자가 숫자가 되는 순간

버퍼 넘침으로 덮인 int 변수는 어떤 값이 될까요? 직접 봅시다. (정수 변수를 버퍼보다 먼저 선언해, 실측 환경에서 정수가 위쪽에 오게 했습니다.)

입력 (bofint.c)

#include <stdio.h>

int main(void) {
    int score = 100;
    char buf[8];

    printf("score 주소: %p, buf 주소: %p\n", (void *)&score, (void *)buf);
    printf("거리: %ld\n", (long)((char *)&score - buf));
    printf("입력: ");
    scanf("%s", buf);
    printf("score = %d\n", score);
    return 0;
}

컴파일과 실행

gcc -Wall -O0 -fno-stack-protector bofint.c -o bofint
python3 -c "print('A'*12)" | ./bofint
score 주소: 0x7ffce92f2a2c, buf 주소: 0x7ffce92f2a24
거리: 8
입력: score = 1094795585

(2026-09-09 실측.)

출력 읽는 법: 열두 글자를 넣어, 아홉~열두 번째 글자(A×4)가 score의 네 바이트를 덮었습니다. 그런데 score가 65(‘A’의 코드)가 아니라 1094795585가 되었습니다. 정수는 네 바이트가 합쳐져 하나의 수로 읽히기 때문입니다. ‘A’ 네 개(65,65,65,65)가 나란히 놓인 네 바이트를 int로 읽은 값이 저 숫자이고, 16진수로는 0x41414141입니다 — Step 50의 hex가 여기서 다시 쓰입니다.

참고로 같은 실험에서 여덟 글자만 넣으면 scanf의 \0 하나가 score의 첫 바이트를 덮어 score = 0이 되는 것도 실측으로 확인했습니다. 정수 100(16진수 0x64)의 낮은 바이트가 0으로 지워진 것입니다.

생각해 보기: ‘AAAA’ 대신 원하는 바이트들을 넣을 수 있다면 score를 원하는 값으로 만들 수 있을까요? 이 계산을 할 수 있다는 것 — "덮기가 망가뜨리기가 아니라 바꾸기가 될 수 있다" — 만 새기면 오늘은 충분합니다.

: 이 발견이 관찰을 분석으로 잇는 다리입니다. 덮이는 값도 계산할 수 있다는 것을 봤으니까요.


4. 미션과 연습문제

미션 — 버퍼 오버플로우 관찰 리포트

bof1.c를 바탕으로 관찰 리포트를 작성하세요.

  1. 세 가지 길이(8글자, 12글자, 16글자)를 입력해 각각의 secret 변화를 기록한다
  2. 두 변수의 주소 거리를 출력하고, "몇 번째 바이트부터 덮이는지"를 계산으로 맞힌 뒤 실험으로 검증한다
  3. 같은 입력 세 개를 기본 옵션(보호 장치 켜짐)으로 컴파일한 프로그램에도 넣어 보고, 어느 길이부터 stack smashing detected가 뜨는지 기록한다
  4. 안전한 버전(%7s)으로 고쳐 같은 입력이 막히는 것을 확인한다
  5. 리포트의 마지막에 답한다: "버퍼 오버플로우가 왜 위험한가 — 입력이 데이터를 넘어 무엇을 할 수 있기에?" 한 문단 이상

연습문제

문제 1. char buf[8]에 여덟 글자를 입력했는데 이웃 변수가 빈 문자열이 되었습니다(3-1 실측). 여덟 글자면 딱 맞아야 하는 것 아닌가요? 무엇이 아홉 번째 칸에 쓰였나요?

문제 2. 3-6에서 ‘A’ 네 개가 덮인 int는 왜 65가 아니라 1094795585가 되었나요? 16진수로도 표현해 보세요.

문제 3. -fno-stack-protector 없이 컴파일하니 *<strong> stack smashing detected </strong>*: terminated가 떴습니다. 이 장치는 무엇을 보고 넘침을 알아챘으며, 왜 공격을 막는 효과가 있나요?

문제 4. 여덟 칸 버퍼에 scanf를 쓸 때 안전한 형식은 %7s입니다. 왜 %8s가 아니라 %7s인가요?


5. 모범 답안과 완료 기준

미션 모범 답안

관찰 리포트의 예 (2026-09-09, Ubuntu 24.04, gcc 13.3.0, -O0 -fno-stack-protector 기준):

[거리 측정] secret - buf = 8 바이트 → 9번째 바이트부터 secret을 덮는다

[8글자 AAAAAAAA]  secret: "" (빈 문자열) — 9번째 바이트 \0이 secret[0]을 덮음
[12글자 A×8+HACK] secret: "HACK" — secret 앞 4바이트가 덮임
[16글자 A×8+B×8]  secret: "BBBBBBBB" — secret 8바이트 전체가 덮임

[보호 장치 켜짐, 기본 컴파일] 16글자 입력 시:
*** stack smashing detected ***: terminated

[안전한 버전 %7s] 같은 16글자 입력 → buf: AAAAAAA, secret: SAFE (무사)

검증하는 법: ① 계산(거리+1 = 덮이기 시작하는 바이트 번호)과 실험 결과가 일치해야 합니다. ② "왜 위험한가" 문단에는 최소한 "입력이 자기 상자를 넘어 이웃 변수를 덮을 수 있고, 덮인 값이 의미를 가질 수 있다(플래그·점수·주소)"는 내용이 있어야 합니다.

연습문제 해답

문제 1 해답. scanf의 %s는 입력 끝에 문자열 종료 표식 \0을 하나 더 씁니다. 그래서 여덟 글자 입력은 실제로 아홉 바이트가 쓰이고, 아홉 번째 바이트(값 0)가 버퍼 바로 옆 변수의 첫 바이트를 덮었습니다. 문자열은 첫 \0에서 끝나므로 그 변수는 빈 문자열로 읽힙니다. "딱 맞는 입력"도 사실은 한 바이트 넘칩니다.

문제 2 해답. int는 네 바이트가 합쳐져 하나의 수로 읽히기 때문입니다. ‘A’는 문자 코드 65(16진수 0x41)이고, 이것이 네 칸에 나란히 놓이면 네 바이트 전체가 0x41414141이 됩니다. 이를 십진수로 읽으면 1094795585입니다.

문제 3 해답. 스택 보호 장치(카나리)입니다. 컴파일러가 버퍼 뒤에 미리 숨겨 둔 감시용 값이 함수 종료 시점에 바뀌어 있으면 넘침으로 판정하고 프로그램을 중단시킵니다. 덮어쓰기가 더 중요한 것(돌아갈 주소 등)에 닿기 전에 실행을 끊어 버리므로, 공격이 성공하는 대신 프로그램이 "안전하게 죽는" 쪽이 됩니다.

문제 4 해답. 문자열 끝에는 \0이 하나 더 붙기 때문입니다. %8s는 여덟 글자 + \0 = 아홉 바이트를 써서 여덟 칸 버퍼를 한 바이트 넘칩니다. "칸 수 빼기 1"이 상한입니다.

완료 기준 체크리스트

  • [ ] 버퍼와 경계 검사 부재가 무엇인지 설명할 수 있다
  • [ ] 넘친 입력이 이웃 변수를 덮는 것을 직접 관찰하고 기록했다
  • [ ] \0 한 바이트가 "딱 맞는 입력"도 넘치게 만든다는 것을 설명할 수 있다
  • [ ] 주소 거리로 덮이는 지점을 계산하고 실험으로 검증했다
  • [ ] 스택 보호 장치의 역할과 -fno-stack-protector의 의미를 안다
  • [ ] 상한 있는 입력(%7s, fgets)으로 방어할 수 있다
  • [ ] 미션: 관찰 리포트(세 길이 기록 + 보호 장치 비교 + 한 문단 답변)를 완성했다

6. 흔한 실수와 해결

벽 1. secret이 안 바뀐다

증상: 길게 입력해도 secret이 그대로입니다.
원인: 컴파일러가 변수 배치 순서를 바꿨거나, 사이에 채움(정렬용 패딩)이 있어 거리가 예상보다 깁니다.
해결: 두 변수의 주소와 거리를 먼저 출력하세요(3-3). 거리가 16이면 입력을 24글자로 늘리는 식으로, 거리 + 목표 크기만큼 넣으면 됩니다. 관찰 장비(주소 출력)가 실험의 절반입니다.

벽 2. stack smashing detected로 죽는다

증상: 이런 메시지와 함께 프로그램이 중단됩니다 (2026-09-09 실측):

*** stack smashing detected ***: terminated

원인: 보호 장치가 켜진 채로 컴파일했습니다. 우분투 gcc의 기본값입니다.
해결: 관찰 실험용으로는 gcc -Wall -O0 -fno-stack-protector로 컴파일하세요. 단, 이것은 실험실에서만입니다. 실제로 배포하는 프로그램에서 이 장치를 끄는 것은 방어막을 벗기는 일입니다.

벽 3. 세그폴트로 죽는다

증상: 입력을 너무 길게 했더니 Segmentation fault가 납니다.
원인: 넘침이 이웃 변수를 지나 더 중요한 것(함수가 돌아갈 주소 등)까지 덮은 것입니다.
해결: 오히려 좋은 관찰입니다. "넘침이 계속되면 프로그램의 흐름 자체가 망가진다"는 증거이니까요. 입력 길이를 조금씩 늘려 가며 "어디까지가 변수, 어디서부터가 붕괴"인지 경계를 찾아 보세요.

벽 4. scanf 입력이 길 때 손으로 치다 실수한다

증상: 긴 문자열을 붙여 넣다가 길이가 어긋납니다.
원인: 손으로 치기엔 너무 깁니다.
해결: 파이썬으로 입력을 만드세요. python3 -c "print('A'*16)" | ./bof1. 입력 길이를 코드로 정확히 관리하는 것이 실험의 재현성을 만듭니다.

벽 5. 결과가 책과 다르다 (거리가 다르다)

증상: 책은 거리 8인데 내 환경은 12이거나 음수입니다.
원인: 변수 배치와 패딩은 컴파일러·버전·옵션마다 다릅니다. 정상입니다.
해결: 그래서 3-3처럼 거리를 먼저 재는 것입니다. "내 환경에서: 컴파일 옵션, 두 변수의 거리, 덮이기 시작한 입력 길이"를 노트에 적어 두세요. 이 기록이 재실험의 기준점이 됩니다.


7. 정리

오늘의 개념

개념 한 줄 설명
버퍼(buffer) 데이터를 잠시 담는 메모리 상자 — 스택에서 이웃 변수와 나란히 산다
버퍼 오버플로우 상자보다 긴 쓰기가 이웃 메모리를 덮는 사고
경계 검사 부재 C가 칸 밖 쓰기를 혼내지 않는 계약
\0(널 문자) 문자열 끝 표식 — 입력 길이 + 1 바이트가 실제로 쓰인다
스택 보호(카나리) 버퍼 뒤 감시 값으로 넘침을 탐지해 중단시키는 컴파일러 방어
치명적 한 바이트 "딱 맞는 입력"의 \0도 이웃을 덮는다 (3-1 실측)

오늘의 문법과 명령어

코드·명령 하는 일
scanf("%s", buf) 위험한 입력 — 상한 없음
scanf("%7s", buf) 상한 있는 입력 (8칸이면 7)
fgets(buf, sizeof(buf), stdin) 더 표준적인 안전 입력
gcc -Wall -O0 -fno-stack-protector 관찰용 컴파일 (실험실 전용)
python3 -c "print('A'*16)" | ./프로그램 긴 입력을 코드로 만들어 넣기

명령어보다 중요한 감각

오늘 본 것은 1988년 인터넷을 마비시킨 모리스 웜부터 수십 년간 이어진 수많은 침입의 공통 원리입니다. "상자보다 긴 입력 + 경계 검사 없음 = 이웃 덮기." 단순해서 더 오래 살아남은 취약점입니다. 오늘 우리가 덮은 것은 장난감 변수였지만, 실제로 덮이는 것은 권한 플래그, 점수, 그리고 프로그램의 흐름 자체입니다.

중요한 것은 그 장면이 마법이 아니라는 것 — 여덟 칸, 경계 없는 쓰기, 나란한 변수라는 세 가지 물리의 합작이라는 것을 여러분이 안다는 것입니다. 이해한 공격은 두렵지 않습니다. 막는 법도 같은 눈으로 보이니까요. 오늘의 관찰 리포트는 여러분의 보안 공부에서 처음으로 "취약점을 손으로 만진" 기록입니다. 잘 보관해 두세요.


전부 체크되면 Step 62 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.