4. Security Engineering · 실제 시험 Abschnitt 4.2
4.2. Fehlerhafter Programmcode
시험지의 하나의 C listing 문제에서 요구한 두 오류를 가짜 소문제로 만들지 않고 두 답안 항목으로 분석합니다.
이 페이지는 비슷한 주제를 임의로 다시 묶지 않고 실제 시험지의 Chapter → subsection → 소문제 순서를 그대로 따릅니다.
ACTUAL EXAM · VERBATIM TRANSCRIPT
시험지 원문 1:1 전사
아래 내용은 해설자가 바꿔 쓴 요약이 아닙니다. 실제 시험 전사본의 문장·순서·수치·배점·코드·표를 그대로 두고, Markdown 기호만 읽기 쉬운 제목·표·코드 모양으로 표시했습니다.
4.2. Fehlerhafter Programmcode (8 Punkte)
In Listing 1 ist ein C-Programm dargestellt, das Benutzereingaben verarbeitet und externe Befehle ausführt. Identifizieren Sie zwei sicherheitskritische Fehler im Programmcode. Geben Sie für jeden Fehler die betroffene Zeile an. Erläutern Sie jeweils kurz welche Auswirkungen der Fehler haben kann und wie der Fehler behoben werden kann.
Listing 1: Unsicherer C-Programmcode
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void build_message(char *name, char *dest) {
5 sprintf(dest, "echo Willkommen %s", name);
6 }
7
8 int main(void) {
9 char name[16];
10 char cmd[64];
11 unsigned int age;
12
13 printf("Name eingeben: ");
14 fgets(name, 15, stdin);
15
16 printf("Alter eingeben: ");
17 scanf("%u", &age);
18
19 if (age - 21 < 0) {
20 printf("Zugriff verweigert\n");
21 return 1;
22 }
23
24 build_message(name, cmd);
25 system(cmd);
26
27 return 0;
28 }근거: CSS_Altklausur_WiSe_2526.pdf 및 computersystemsicherheit_wise25-26_questions_only.md · Abschnitt 4.2
VISUAL MAP
한 listing의 두 독립 오류 왼쪽에서 오른쪽으로 읽은 뒤 아래 실제 소문제에서 같은 순서를 반복합니다.- 01 C 입력
- 02 Unsigned 계산
- 03 Command 문자열
- 04 Shell parser
- 05 우회·명령 실행
FIXED SOLVING METHOD
이 묶음의 고정 풀이 순서
- 변수 type, 배열 크기, 함수 호출을 먼저 읽습니다.
- Zeile 19에 age=18을 실제로 대입하고 unsigned 결과를 추적합니다.
- Zeile 24에서 name이 cmd 문자열에 어떻게 합쳐지는지 씁니다.
- Zeile 25에서 shell이 세미콜론 등을 어떻게 다시 해석하는지 봅니다.
- 각 오류마다 line, 원인, 구체 입력, 영향, 근본 수정을 별도로 작성합니다.
ZERO-BASE CONCEPT LESSONS
이 묶음을 풀기 전에 필요한 개념
카드를 열고 닫는 방식 대신 한 방향으로 이어지는 글로 구성했습니다. 비유 → 용어의 쉬운 뜻 → 실제 작동 → 시험에서의 경계 순서로 천천히 읽으세요.
기초 개념 01
C의 buffer와 length 없는 sprintf
먼저 장면으로 이해해 봅시다. 10칸짜리 서랍에 30개 물건을 밀어 넣으면 옆 서랍의 물건까지 밀어내는 것과 같다. C는 자동으로 벽을 만들어 막아 주지 않는다.
이제 전문 용어를 붙이면 다음과 같습니다. Buffer은(는) 프로그램이 byte나 문자를 잠시 저장하도록 확보한 연속 memory 공간입니다. Boundary / Bounds은(는) Buffer가 합법적으로 사용할 수 있는 시작과 끝 범위입니다. Null terminator은(는) C 문자열의 끝을 표시하는 값 `\0`으로, 저장 공간 1바이트를 차지합니다. Stack frame은(는) 한 함수 호출의 local variable, 저장된 register, return 관련 정보가 놓이는 stack 영역입니다. Buffer overflow은(는) 확보한 buffer 범위를 넘어 write하여 인접 memory를 손상시키는 오류입니다.
실제 시스템에서는 이렇게 작동합니다. C의 char array는 정해진 크기의 연속된 memory 공간이다. Buffer에 들어갈 문자열 길이를 확인하지 않고 sprintf로 쓰면 경계를 넘어 인접 memory를 덮을 수 있다. 이를 buffer overflow라고 하며 crash, data corruption, 경우에 따라 code execution으로 이어질 수 있다. snprintf는 최대 길이를 받지만 반환값과 null termination 조건도 확인해야 한다.
- 목적지 buffer 크기를 확인한다.
- 공격자가 제어하는 문자열의 최대 길이를 확인한다.
- Format 후 필요한 길이가 buffer보다 큰지 계산한다.
- Bounded API와 반환값 검사, 명시적 길이 검증을 적용한다.
여기서 넘지 말아야 할 경계: sprintf의 format string이 고정되어 있어도 출력 전체 길이가 제한되지 않으면 overflow가 생길 수 있다.
21. C memory·Buffer boundary 독립 강의 →기초 개념 02
system()과 shell command injection
먼저 장면으로 이해해 봅시다. 배달 주소 칸에 ‘서울; 창고 문도 열어라’라고 썼는데 프로그램이 세미콜론 뒤 문장까지 새 업무 지시로 처리하는 상황이다.
이제 전문 용어를 붙이면 다음과 같습니다. Shell은(는) 명령 문자열을 해석해 program을 실행하고 pipe·redirection 같은 문법을 처리하는 command interpreter입니다. `system()`은(는) 문자열을 shell에 넘겨 command로 실행하는 C library 함수입니다. Metacharacter은(는) `;`, `|`, `&&`처럼 shell에서 단순 글자가 아니라 문법 역할을 하는 문자입니다. Command injection은(는) 공격자 data가 shell syntax로 해석되어 의도하지 않은 command를 실행하게 되는 취약점입니다.
실제 시스템에서는 이렇게 작동합니다. system()은 문자열을 운영체제 shell에 명령으로 전달한다. 사용자 입력을 그 문자열에 직접 붙이면 세미콜론, 파이프, 명령 치환 같은 shell metacharacter가 데이터가 아니라 추가 명령으로 해석될 수 있다. 이것이 command injection이다. 가능하면 shell을 거치지 않고 허용된 프로그램과 argument를 분리해 실행해야 한다.
- 외부 입력 source를 찾는다.
- 입력이 system, popen, shell=True 같은 command sink에 도달하는지 본다.
- Shell 문법 문자가 명령 구조를 바꿀 수 있는지 확인한다.
- Shell 없는 process API와 고정 allowlist argument를 사용한다.
- 실행 계정 권한을 최소화한다.
여기서 넘지 말아야 할 경계: 일부 문자만 escape하는 방식은 shell 종류와 인코딩에 따라 우회될 수 있으므로 구조적으로 shell을 제거하는 편이 안전하다.
23. system()·Command injection 독립 강의 →ACTIVE RECALL
이 페이지를 닫기 전 확인
- `unsigned int`는 음수를 어떻게 표현하나요?
- `build_message(name, cmd)`에서 dest는 무엇을 가리키나요?
- `system(cmd)`가 위험한 핵심 이유는?
QUESTION-BY-QUESTION COMMENTARY
실제 시험 소문제별 해설
시험지의 번호와 순서를 그대로 유지했습니다. 각 항목을 열어 원문 → 쉬운 개념 설명 → 이번 문제의 단계별 풀이 → 답안 → 함정 순서로 읽으세요.
ACTUAL EXAM SUBSECTION 4.2
4.2. Fehlerhafter Programmcode
2개 학습 항목 · 8점
답안 항목 1 · Zeile 19 19행 `if (age - 21 < 0)`의 오류·영향·수정을 설명하시오. 기존 71문항 학습 번호 65 · 4점 Code 감사
답안 항목 1 · Zeile 19 · 실제 시험 원문
이 항목은 시험지에 독립된 소문제로 적혀 있지 않습니다. 4.2의 하나의 지시문이 요구한 두 개의 sicherheitskritische Fehler 중 한 답안 항목입니다. 위 ‘시험지 원문 1:1 전사’를 기준으로 읽으세요.
한국어로 요구사항만 풀어 읽기
19행 `if (age - 21 < 0)`의 오류·영향·수정을 설명하시오.
TERMS FOR 답안 항목 1 · Zeile 19
이 소문제의 중요한 용어부터 이해하기
전문 용어를 알고 있다고 가정하지 않습니다. 아래 정의의 굵은 용어를 문제 문장 속 같은 단어와 바꾸어 읽은 뒤 풀이로 넘어가세요.
정수 계산 결과가 type의 최소 범위보다 작아져 다른 큰 값으로 돌아가는 현상입니다.
작은 예: 나이 검사를 unsigned 뺄셈 뒤 `< 0`으로 하면 미성년자 차단이 실패할 수 있습니다.
프로그램이 byte나 문자를 잠시 저장하도록 확보한 연속 memory 공간입니다.
Buffer가 합법적으로 사용할 수 있는 시작과 끝 범위입니다.
C 문자열의 끝을 표시하는 값 `\0`으로, 저장 공간 1바이트를 차지합니다.
한 함수 호출의 local variable, 저장된 register, return 관련 정보가 놓이는 stack 영역입니다.
확보한 buffer 범위를 넘어 write하여 인접 memory를 손상시키는 오류입니다.
이 소문제만을 위한 0부터 시작하는 미니 강의
아래 설명은 이 과목의 선행 지식을 가정하지 않습니다. 개념 → 비유의 대응 → 눈으로 읽는 구조 → 작은 예제 순서로 읽은 뒤 실제 시험 풀이로 넘어가세요.
1 · 먼저 알아야 할 개념
C의 unsigned integer는 음수를 표현하지 않고 0부터 정해진 최댓값까지의 값만 가집니다. 연산 결과가 0 아래로 내려가면 수학의 음수가 되는 대신 정수 범위의 큰 값으로 modulo wraparound합니다.
예를 들어 32-bit unsigned에서 0-1은 4,294,967,295가 됩니다.
age < 21이면 뺄셈 결과가 큰 unsigned 값으로 감기고,age >= 21이면 원래부터 0 이상입니다. 따라서 unsigned 식age - 21 < 0은 이 코드에서 모든age값에 대해 항상 false입니다.접근 제어 조건은 위험한 연산을 하기 전에 원래 값을 직접 비교해야 합니다. 이 문항에서는
if (age < 21)가 의도를 가장 분명하게 표현합니다. 또한scanf의 반환값이 1인지 확인하지 않으면 숫자 변환 실패 뒤 초기화되지 않은age를 사용할 수 있으므로 입력 성공 여부와 허용 범위도 검사해야 합니다.2 · 일상 장면으로 먼저 잡기
0에서 9999까지만 표시하는 네 자리 주행거리계에서 0000을 한 칸 뒤로 돌리면 -1이 아니라 9999가 나타납니다.
age - 21 < 0가 false비유의 경계 실제 C unsigned의 범위는 type bit width에 따라 다르며, wrap 결과는 2^N을 modulus로 계산합니다.
age = 20 (unsigned)
20 - 21 = -1
-1 mod 2^32 = 4294967295
4294967295 < 0 → false
age < 21 → true
뺄셈 뒤에는 이미 wrap되므로 음수 검사가 늦습니다. 빼기 전에 원래 값을 비교합니다.
20세가 검사에 통과하는 이유
주어진 것과 목표
age가 32-bit unsigned이고 입력이 20입니다. 조건age - 21 < 0을 평가합니다.뺄셈의 수학 결과를 먼저 봅니다.
왜? 개발자의 의도를 확인하기 위해서입니다.
중간 결과 수학에서는 20-21=-1입니다.
unsigned 표현 규칙을 적용합니다.
왜? C가 실제로 저장하는 값을 구하기 위해서입니다.
중간 결과 -1은 2^32-1 = 4,294,967,295로 wrap합니다.
비교식을 평가합니다.
왜? 분기 결과를 결정하기 위해서입니다.
중간 결과 큰 unsigned 값은 0보다 작지 않으므로 조건은 false입니다.
안전한 비교로 바꿉니다.
왜? underflow 전에 나이 범위를 판정하기 위해서입니다.
중간 결과
if (age < 21)이면 20이 올바르게 거부됩니다.예제 결론 20세 입력은 대표 예시이며, 실제 조건은 unsigned
age의 모든 값에서 false입니다. 뺄셈 전 직접 비교로 수정합니다.실제 시험으로 옮기기 답안에는 line 11의 unsigned type과 line 19의 subtraction을 연결하고 underflow, 영향, 수정을 모두 써야 합니다.
이번 문제는 이 단계로 풀어야 했습니다
먼저 문제에서 직접 주어진 값과 최종적으로 구할 값을 분리합니다. 그다음 각 값·용어·경로를 왜 선택했는지 확인하며, 앞 단계의 중간 결과를 다음 단계의 입력으로 사용합니다.
먼저 문제를 식과 조건으로 정리하기
계산을 시작하기 전에 주어진 것과 구할 것을 분리합니다. 그다음 아래 관계식을 위에서 아래로 사용하면, 숫자가 어디서 왔는지 놓치지 않을 수 있습니다.
강사가 문제의 요구사항을 쉬운 말로 바꾸면
시험 문장이 요구하는 것: 19행 `if (age - 21 < 0)`의 오류·영향·수정을 설명하시오.
이 문제의 풀이 전략: 이 소문제에서는 변수 type과 문제 행을 연결한다 → 경계 입력으로 실제 값을 계산한다 → 조건이 항상 false임을 일반화한다 → 보안 영향을 분기까지 추적한다 → 원인에 맞는 수정을 제시한다 → 시험 답안을 세 문장으로 정리한다 순서로 진행합니다. 마지막에는 ‘위험한 unsigned 뺄셈을 하지 말고 `age < 21`로 원래 값을 직접 비교합니다.’라는 교정 기준으로 답을 다시 확인합니다.
문제에서 주어진 정보
unsigned int age
if (age - 21 < 0)
최종적으로 구해야 하는 것
Underflow로 검사가 우회되는 이유, 영향, 올바른 수정
사용할 공식·판정 관계
변수 type과 문제 행을 연결한다
구체적으로 Line 11에서
age가 unsigned임을 확인하고 line 19의age - 21 < 0에 그 규칙을 적용합니다.여기서 검산 line 19만 보고 signed 정수처럼 계산하지 않습니다.
다음 단계로 여기서 확인한 내용을 다음 ‘경계 입력으로 실제 값을 계산한다’ 단계의 출발점으로 사용합니다.
경계 입력으로 실제 값을 계산한다
구체적으로 age=20이면 수학 결과 -1이 unsigned 범위에서 2^N-1의 큰 양수로 wraparound합니다.
여기서 검산 비트 폭을 모르면 정확한 최댓값 대신 ‘큰 unsigned 값’이라고 써도 원리는 유지됩니다.
다음 단계로 여기서 확인한 내용을 다음 ‘조건이 항상 false임을 일반화한다’ 단계의 출발점으로 사용합니다.
조건이 항상 false임을 일반화한다
구체적으로
age<21이면 underflow로 큰 값이고age>=21이면 정상적인 0 이상 값이므로 어떤 unsignedage에서도<0은 성립하지 않습니다.여기서 검산 미성년 입력에서만 우연히 실패한다고 축소하지 않습니다.
다음 단계로 여기서 확인한 내용을 다음 ‘보안 영향을 분기까지 추적한다’ 단계의 출발점으로 사용합니다.
보안 영향을 분기까지 추적한다
구체적으로 21세 미만 사용자를 거부해야 할 접근 제어가 실행되지 않아 제한된 기능을 우회할 수 있습니다.
여기서 검산 단순 계산 오류가 아니라 access-control bypass까지 적습니다.
다음 단계로 여기서 확인한 내용을 다음 ‘원인에 맞는 수정을 제시한다’ 단계의 출발점으로 사용합니다.
원인에 맞는 수정을 제시한다
구체적으로
scanf성공 여부를 확인하고 허용 범위를 검증한 뒤, 뺄셈 없이if (age < 21)로 직접 비교합니다.여기서 검산 cast 하나로 숨기거나 결과만 signed에 담는 불완전한 수정을 피합니다.
다음 단계로 여기서 확인한 내용을 다음 ‘시험 답안을 세 문장으로 정리한다’ 단계의 출발점으로 사용합니다.
시험 답안을 세 문장으로 정리한다
구체적으로 오류: unsigned underflow. 영향: 미성년 입력이 검사 우회. 수정: 원래
age를 21과 직접 비교.여기서 검산 오류·영향·수정 세 채점 요소가 모두 있는지 확인합니다.
다음 단계로 앞 단계의 결과를 모아 채점 가능한 최종 답안으로 정리합니다.
정답과 해설
`age`가 unsigned라 21보다 작으면 `age-21`이 큰 양수로 wraparound하고 21 이상이면 원래부터 0 이상이므로 `<0`은 모든 입력에서 항상 false다. 따라서 접근 거부가 우회된다. `scanf` 성공·범위를 확인하고 빼기 전에 `if (age < 21)`로 비교한다.
시험 답안 골격
정답이 이렇게 되는 이유
age가 unsigned이므로 21보다 작은 값에서는age-21이 큰 양수로 wraparound하고, 21 이상에서는 결과가 원래부터 0 이상입니다. 따라서age-21 < 0은 모든 입력에서 항상 false이며 나이 기반 접근 제한을 우회할 수 있습니다.scanf성공과 입력 범위를 확인하고if (age < 21)처럼 뺄셈 전에 직접 비교해야 합니다. 이 문항의 핵심은 일반적인 buffer 문제가 아니라 unsigned logic bug입니다.초보자가 가장 자주 뒤집는 지점
잘못된 생각
age - 21을 signed 변수에 저장하면 자동으로 -1이 되어 해결된다.왜 틀렸나 뺄셈은 operand의 unsigned type으로 먼저 수행되어 이미 wrap된 뒤 변환될 수 있고, 변환 결과도 구현·폭에 의존할 수 있습니다.
고쳐 말하면 위험한 unsigned 뺄셈을 하지 말고
age < 21로 원래 값을 직접 비교합니다.한 문제만 더: 개념이 정말 연결됐는지 확인
질문 32-bit unsigned
x=0에서x-1은 무엇이며x-1 < 0은 어떤 값입니까?정답 값은 2^32-1이고 unsigned 비교에서 0보다 작지 않으므로 조건은 false입니다.
이 문제의 오답 함정
이 소문제를 점수로 바꾸는 6개의 작은 훈련
해설을 닫은 상태에서 1번부터 수행하세요. 정답을 읽은 직후보다 직접 문제 지도를 만들고 답을 꺼낸 뒤 채점할 때 기억이 더 정확해집니다. 작성 내용과 복습 판정은 이 브라우저에 자동 저장됩니다.
해설을 보지 말고 주어진 정보 → 구할 것 → 사용할 공식·판정 관계를 한 줄씩 복원하세요.
막힐 때만 첫 단서 열기
0 아래로 내려갈 수 없는 주행거리계에서 0-1이 아주 큰 숫자로 돌아가는 것과 같다.
19행 `if (age - 21 < 0)`의 오류·영향·수정을 설명하시오.
정답 문장만 말하지 말고 근거·중간값·메시지 흐름 중 이 문항에 필요한 것을 빈 종이에 남기세요.
작성한 뒤에만 아래 기준을 열고, 실제로 쓴 항목만 체크하세요.
답안 작성 후 채점 기준 열기
답안 슬롯 자가 점검 — 실제로 말하거나 쓴 항목만 체크하세요.
0/3 slots
조건 변형: 제시한 수정책 하나만 적용했다고 가정하세요. 그 수정이 정확히 막는 입력 경로와 여전히 남는 별도 취약점을 각각 한 문장으로 쓰세요.
고칠 답안: 현재 구체적 입력 길이에서 line 5 sprintf만 답하고 핵심 logic bug를 놓치지 않는다.
복구 힌트: 0 아래로 내려갈 수 없는 주행거리계에서 0-1이 아주 큰 숫자로 돌아가는 것과 같다.
오답의 첫 잘못된 전제를 한 줄로 지우고, 올바른 조건과 결론을 두 줄로 다시 쓰세요.
채점 전 예상과 실제 채점 결과가 달랐는지 확인한 뒤 상태를 남기세요. ‘숙달’은 근거와 중간 과정까지 무힌트로 재현했을 때만 선택합니다.
모든 훈련을 마친 뒤 핵심 정답 다시 확인
`age`가 unsigned라 21보다 작으면 `age-21`이 큰 양수로 wraparound하고 21 이상이면 원래부터 0 이상이므로 `<0`은 모든 입력에서 항상 false다. 따라서 접근 거부가 우회된다. `scanf` 성공·범위를 확인하고 빼기 전에 `if (age < 21)`로 비교한다.
답안 항목 2 · Zeile 25 `system(cmd)` 주변의 오류·영향·수정을 설명하시오. 기존 71문항 학습 번호 66 · 4점 Code 감사
답안 항목 2 · Zeile 25 · 실제 시험 원문
이 항목은 시험지에 독립된 소문제로 적혀 있지 않습니다. 4.2의 하나의 지시문이 요구한 두 개의 sicherheitskritische Fehler 중 한 답안 항목입니다. 위 ‘시험지 원문 1:1 전사’를 기준으로 읽으세요.
한국어로 요구사항만 풀어 읽기
`system(cmd)` 주변의 오류·영향·수정을 설명하시오.
TERMS FOR 답안 항목 2 · Zeile 25
이 소문제의 중요한 용어부터 이해하기
전문 용어를 알고 있다고 가정하지 않습니다. 아래 정의의 굵은 용어를 문제 문장 속 같은 단어와 바꾸어 읽은 뒤 풀이로 넘어가세요.
문자열을 operating system의 command shell에 전달해 명령으로 실행하는 C 함수입니다.
작은 예: 문자열에 `; whoami`가 들어가면 shell이 세미콜론 뒤를 두 번째 명령으로 해석할 수 있습니다.
명령 문자열을 해석해 program을 실행하고 pipe·redirection 같은 문법을 처리하는 command interpreter입니다.
`;`, `|`, `&&`처럼 shell에서 단순 글자가 아니라 문법 역할을 하는 문자입니다.
공격자 data가 shell syntax로 해석되어 의도하지 않은 command를 실행하게 되는 취약점입니다.
이 소문제만을 위한 0부터 시작하는 미니 강의
아래 설명은 이 과목의 선행 지식을 가정하지 않습니다. 개념 → 비유의 대응 → 눈으로 읽는 구조 → 작은 예제 순서로 읽은 뒤 실제 시험 풀이로 넘어가세요.
1 · 먼저 알아야 할 개념
system(cmd)은 문자열을 운영체제 shell에 넘겨 명령으로 해석·실행하게 합니다. 문자열 안의;,&&,|,$()같은 metacharacter는 단순 글자가 아니라 명령 문법이 될 수 있습니다.사용자 입력을 명령 문자열에 이어 붙이면 data였던 입력이 shell parser에게 code로 재해석됩니다. 이를 command injection이라 하며 이 listing에서는 공격자가 local stdin을 제어할 때 process 권한으로 임의 OS command를 실행할 수 있습니다. 입력이 원격 요청에서 온다는 추가 조건이 있을 때에만 이를 RCE(remote code execution)라고 부르는 것이 정확합니다.
이 구체적인 listing에서는 line 14의
fgets가 이름을 최대 14자로 제한하므로 line 5의 출력은 prefix 16 byte + name 최대 14 byte + NUL 1 byte = 최대 31 byte로cmd[64]안에 들어갑니다. 즉 여기서 핵심은sprintfoverflow가 아니라 line 25의 shell injection입니다.Buffer 길이를 제한하는
snprintf는 memory overflow 위험을 줄일 수 있지만 shell 문법 해석은 그대로 남습니다. 가장 안전한 수정은 shell 호출을 제거하고 직접 출력하거나, 꼭 process를 실행해야 한다면 고정 executable과 분리된 argument를 사용하는 shell-free API를 쓰는 것입니다.2 · 일상 장면으로 먼저 잡기
방문객에게 이름을 적으라고 준 종이를 직원이 읽는 대신, 그 종이 전체를 건물 운영 명령서로 간주해 수행하는 상황입니다.
nameinputecho Willkommen %scommand templatesystem(); whoami같은 injected command비유의 경계 단순히 몇 문자만 지우는 blacklist는 quoting, encoding, shell 종류에 따른 우회가 많아 근본 해결이 아닙니다.
Alice; whoamiecho Willkommen+ input;를 command separator로 재해석echo 실행 후 whoami 실행
shell 없이 data로 직접 출력
공격 문자열이 위험해지는 지점은
system()이 shell 문법으로 다시 읽는 순간입니다.Alice; whoami가 두 명령이 되는 과정주어진 것과 목표 Template은
echo Willkommen %s이고 사용자 이름은Alice; whoami입니다.문자열 치환 결과를 만듭니다.
왜? shell이 받는 정확한 byte sequence를 보기 위해서입니다.
중간 결과
echo Willkommen Alice; whoami가 됩니다.Shell 문법으로 token을 나눕니다.
왜?
;가 data인지 separator인지 판단하기 위해서입니다.중간 결과
echo Willkommen Alice와whoami라는 두 command가 됩니다.system이 두 command를 실행합니다.왜? 보안 영향을 program 권한과 연결하기 위해서입니다.
중간 결과 공격자가 의도한
whoami가 application 권한으로 실행됩니다.Shell을 제거합니다.
왜? 입력이 명령 문법으로 바뀌는 interpreter 경계를 없애기 위해서입니다.
중간 결과
printf("Willkommen %s", name)처럼 data로 직접 출력합니다.예제 결론 취약점의 원인은 길이보다 untrusted data가 shell code 문맥에 들어간 것입니다.
실제 시험으로 옮기기 line 14
fgets→ line 24build_message(name, cmd)→ line 5 command 구성 → line 25system(cmd)의 흐름을 연결하고 command injection, process 권한의 명령 실행 영향, shell-free 수정을 답합니다.이번 문제는 이 단계로 풀어야 했습니다
먼저 문제에서 직접 주어진 값과 최종적으로 구할 값을 분리합니다. 그다음 각 값·용어·경로를 왜 선택했는지 확인하며, 앞 단계의 중간 결과를 다음 단계의 입력으로 사용합니다.
먼저 문제를 식과 조건으로 정리하기
계산을 시작하기 전에 주어진 것과 구할 것을 분리합니다. 그다음 아래 관계식을 위에서 아래로 사용하면, 숫자가 어디서 왔는지 놓치지 않을 수 있습니다.
강사가 문제의 요구사항을 쉬운 말로 바꾸면
시험 문장이 요구하는 것: `system(cmd)` 주변의 오류·영향·수정을 설명하시오.
이 문제의 풀이 전략: 이 소문제에서는 공격자 제어 입력의 출발점을 찾는다 → 함수 호출과 문자열 구성을 잇는다 → 취약한 sink까지 data flow를 잇는다 → 구체 payload의 parsing을 보여 준다 → 영향을 권한과 도달성에 맞춰 쓴다 → buffer 크기도 별도로 검산한다 → 근본 수정과 불충분한 수정을 구분한다 순서로 진행합니다. 마지막에는 ‘Memory safety와 command injection을 별도 취약점으로 보고 shell interpreter 경계를 제거합니다.’라는 교정 기준으로 답을 다시 확인합니다.
문제에서 주어진 정보
sprintf(cmd, "echo Willkommen %s", name)
system(cmd)
Alice; whoami
최종적으로 구해야 하는 것
Command Injection이 생기는 문자 해석 과정, 영향, shell을 제거하는 수정
사용할 공식·판정 관계
name=Alice → cmd="echo Willkommen Alice"
name="Alice; whoami" → shell이 두 command로 분리
사용자 입력을 shell command 문자열에 결합하지 않는다.
공격자 제어 입력의 출발점을 찾는다
구체적으로 Line 14의
fgets가 local stdin에서 사용자 제공name을 읽습니다.여기서 검산 공격자가 바꿀 수 있는 값과 실제 입력 line을 명시합니다.
다음 단계로 여기서 확인한 내용을 다음 ‘함수 호출과 문자열 구성을 잇는다’ 단계의 출발점으로 사용합니다.
함수 호출과 문자열 구성을 잇는다
구체적으로 Line 24가
build_message(name, cmd)를 호출하고, 그 함수의 line 5가name을echo Willkommen %s형식으로 destinationcmd에 넣습니다.여기서 검산 line 5가 입력을 읽는 곳이 아니라 command를 구성하는 곳임을 확인합니다.
다음 단계로 여기서 확인한 내용을 다음 ‘취약한 sink까지 data flow를 잇는다’ 단계의 출발점으로 사용합니다.
취약한 sink까지 data flow를 잇는다
구체적으로 구성된
cmd는 line 25의system(cmd)로 전달되어 shell 문법으로 다시 해석됩니다.여기서 검산 source→transform→sink 순서가 빠짐없이 연결되는지 봅니다.
다음 단계로 여기서 확인한 내용을 다음 ‘구체 payload의 parsing을 보여 준다’ 단계의 출발점으로 사용합니다.
구체 payload의 parsing을 보여 준다
구체적으로
Alice; whoami는 shell에서 세미콜론 기준 두 명령으로 해석됩니다.여기서 검산 취약점 이름만 쓰지 않고 interpreter 동작을 적습니다.
다음 단계로 여기서 확인한 내용을 다음 ‘영향을 권한과 도달성에 맞춰 쓴다’ 단계의 출발점으로 사용합니다.
영향을 권한과 도달성에 맞춰 쓴다
구체적으로 공격자는 application process의 권한으로 임의 OS command를 실행해 파일 읽기·변조 또는 추가 침해를 할 수 있습니다.
여기서 검산 무조건 root 또는 remote라고 과장하지 않습니다. 이 listing은 local stdin만 보여 줍니다.
다음 단계로 여기서 확인한 내용을 다음 ‘buffer 크기도 별도로 검산한다’ 단계의 출발점으로 사용합니다.
buffer 크기도 별도로 검산한다
구체적으로 현재 길이 제한에서는 16+14+1=31 byte이므로
cmd[64]를 넘지 않습니다. 이는 shell metacharacter 해석과 별개의 문제입니다.여기서 검산 공식 연습 자료의 일반적
sprintf경고를 이 구체 값에 무비판적으로 적용하지 않습니다.다음 단계로 여기서 확인한 내용을 다음 ‘근본 수정과 불충분한 수정을 구분한다’ 단계의 출발점으로 사용합니다.
근본 수정과 불충분한 수정을 구분한다
구체적으로 환영 문구는
printf로 직접 출력해system을 제거합니다.snprintf만 적용하면 길이 문제는 줄어도 command injection은 남습니다.여기서 검산 shell-free 수정이 포함되었는지 확인합니다.
다음 단계로 앞 단계의 결과를 모아 채점 가능한 최종 답안으로 정리합니다.
정답과 해설
Line 14에서 읽은 사용자 입력 name이 line 24 호출과 line 5의 cmd 구성을 거쳐 line 25의 shell로 전달되므로 `;`, `&&` 같은 metacharacter로 command injection이 가능하다. 이 listing에서는 local stdin 공격자가 process 권한으로 임의 OS command를 실행할 수 있다. `system`을 제거하고 직접 출력하거나 shell을 거치지 않는 고정 인자 API를 사용한다.
시험 답안 골격
정답이 이렇게 되는 이유
Line 14에서 읽은 사용자 입력
name이 line 24를 거쳐 line 5에서 command 문자열에 삽입되고, line 25의system()을 통해 shell이 해석하므로 command injection이 발생합니다. 예를 들어Alice; whoami는 환영 출력과 추가 명령으로 분리되어 program 권한으로 실행될 수 있습니다. 이 listing만으로 원격 입력은 보이지 않으므로 영향은 우선 arbitrary OS-command execution이라고 표현합니다. 현재cmd[64]에는 최대 31 byte만 들어가 overflow하지 않으며, shell이 필요 없는 기능이므로 직접 출력해 interpreter 경계를 제거해야 합니다.초보자가 가장 자주 뒤집는 지점
잘못된 생각
snprintf로 바꾸면 모든 문제가 해결된다.왜 틀렸나
snprintf는 destination buffer의 길이를 제한하지만;같은 입력이 shell 문법으로 해석되는 것은 막지 않습니다.고쳐 말하면 Memory safety와 command injection을 별도 취약점으로 보고 shell interpreter 경계를 제거합니다.
한 문제만 더: 개념이 정말 연결됐는지 확인
질문
execve류 API에 executable path와 argument array를 분리해 넘기면 왜 shell injection 위험이 줄어듭니까?정답 Shell이 한 문자열을 명령 문법으로 재해석하지 않고 각 argument를 data로 전달하기 때문입니다. 다만 executable과 argument의 의미에 대한 validation은 여전히 필요합니다.
이 문제의 오답 함정
이 소문제를 점수로 바꾸는 6개의 작은 훈련
해설을 닫은 상태에서 1번부터 수행하세요. 정답을 읽은 직후보다 직접 문제 지도를 만들고 답을 꺼낸 뒤 채점할 때 기억이 더 정확해집니다. 작성 내용과 복습 판정은 이 브라우저에 자동 저장됩니다.
해설을 보지 말고 주어진 정보 → 구할 것 → 사용할 공식·판정 관계를 한 줄씩 복원하세요.
막힐 때만 첫 단서 열기
사용자에게 이름을 쓰라고 종이를 줬는데 그 종이를 운영체제 명령서로 그대로 실행하는 셈이다.
`system(cmd)` 주변의 오류·영향·수정을 설명하시오.
정답 문장만 말하지 말고 근거·중간값·메시지 흐름 중 이 문항에 필요한 것을 빈 종이에 남기세요.
작성한 뒤에만 아래 기준을 열고, 실제로 쓴 항목만 체크하세요.
답안 작성 후 채점 기준 열기
답안 슬롯 자가 점검 — 실제로 말하거나 쓴 항목만 체크하세요.
0/3 slots
조건 변형: 제시한 수정책 하나만 적용했다고 가정하세요. 그 수정이 정확히 막는 입력 경로와 여전히 남는 별도 취약점을 각각 한 문장으로 쓰세요.
고칠 답안: blacklist sanitization만을 유일한 해결책으로 제시하지 않는다.
복구 힌트: 사용자에게 이름을 쓰라고 종이를 줬는데 그 종이를 운영체제 명령서로 그대로 실행하는 셈이다.
오답의 첫 잘못된 전제를 한 줄로 지우고, 올바른 조건과 결론을 두 줄로 다시 쓰세요.
채점 전 예상과 실제 채점 결과가 달랐는지 확인한 뒤 상태를 남기세요. ‘숙달’은 근거와 중간 과정까지 무힌트로 재현했을 때만 선택합니다.
모든 훈련을 마친 뒤 핵심 정답 다시 확인
Line 14에서 읽은 사용자 입력 name이 line 24 호출과 line 5의 cmd 구성을 거쳐 line 25의 shell로 전달되므로 `;`, `&&` 같은 metacharacter로 command injection이 가능하다. 이 listing에서는 local stdin 공격자가 process 권한으로 임의 OS command를 실행할 수 있다. `system`을 제거하고 직접 출력하거나 shell을 거치지 않는 고정 인자 API를 사용한다.