Step 291. ★ CTF #7: 라이브러리 실전 검증 — 자산의 가치는 대회에서만 판명된다

Step 291. ★ CTF #7: 라이브러리 실전 검증 — 자산의 가치는 대회에서만 판명된다

Level 3 — CTF 대회 사이클 | 난이도 ★★★☆☆ | 예상 소요 시간 2일 (대회 참가 + 회고 반일)

전제: Step 290(익스플로잇 라이브러리 v1) 완료, 파이썬 3(측정 스크립트용).

  • 준비물: 팀이 참가할 CTF 대회 1개, Step 290의 라이브러리 저장소, 시간 기록 메모, 파이썬 3. 이 챕터의 대회 장면은 화면 예시, 로컬 시간 측정·경로 실패 재현은 실측(2026-09-09, Python 3.12)으로 표시했습니다.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 이 챕터는 검증 챕터입니다 — 만든 자산이 실전에서 도는지 확인하는 날입니다.

라이브러리 v1은 서랍 안에서는 완벽해 보입니다. 주석도 달렸고, 목차도 있고, 로컬 검증도 끝냈습니다. 그런데 자산의 진짜 가치는 대회의 압박 속에서만 드러납니다 — 시간에 쫓기는 손이 템플릿을 10초 안에 찾는가, 대회 서버의 생김새가 로컬 데모와 달라도 뼈대가 버티는가.

오늘의 대회는 1차 목표가 다릅니다. 점수가 아니라 라이브러리 검증입니다. 비슷한 유형이 나왔을 때 예전처럼 처음부터 짜는지, 템플릿에서 5분 만에 시작하는지 — 그 차이를 측정하는 대회입니다. 물론 점수도 딸 것입니다. 검증이 잘되면 점수는 따라옵니다.


1. 학습 목표

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

  • 대회 시작과 함께 라이브러리 목차를 펼쳐두고 유형 매칭을 10초 안에 내린다
  • 템플릿 사용 건과 미사용 건의 소요 시간을 기록해 비교한다
  • 템플릿이 실전에서 무너지는 지점(의존성·경로·입력 형식)을 분류한다
  • "5분 안에 안 맞으면 처음부터 짠다"는 판정 규칙을 적용한다
  • 대회 회고를 라이브러리 개선 패치 커밋으로 마감한다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 CTF 대회 플랫폼, Step 290의 라이브러리, 파이썬 3(측정용)
오늘의 명령 라이브러리 템플릿 실행, python step291_measure.py(로컬 측정 예시)
필요한 개념 유형 매칭, 5분 판정 규칙, 실전 무너짐의 3요인, 개선 패치
오늘의 산출물 템플릿 사용 기록표 + 라이브러리 개선 커밋(v2)

2-1. 유형 매칭 — 10초 판단의 절차

대회에서 문제를 열면, 라이브러리를 뒤지기 전에 질문 순서가 정해져 있어야 합니다.

유형 매칭 3단 질문 (목표 10초):
1. 이 문제의 입출력 형태가 뭔가? (TCP 프롬프트 / 웹 폼 / 파일 다운로드)
2. 우리 템플릿 중 같은 형태가 있나? (목차의 '사용 조건' 줄만 훑는다)
3. 있다면, 수정 지점이 5분 분량인가? (HOST/PORT/값 몇 개인가, 구조가 다른가)

3번이 "구조가 다르다"이면 그 템플릿은 이 문제에 맞지 않습니다. 형태는 같아도 내부 프로토콜이 다른 경우가 흔합니다 — 그때는 재료(payloads)만 가져오고 뼈대는 새로 짭니다.

2-2. 5분 판정 규칙 — 억지 매칭의 함정

소재의 경고입니다 — 템플릿에 억지로 끼워 맞추다 시간을 더 씁니다. 라이브러리를 만든 직후의 대회에서 가장 흔한 사고가 이것입니다. 자산을 쓰고 싶은 마음이 판단을 흐립니다.

규칙은 단순합니다. 템플릿을 뜯어고치기 시작한 지 5분 안에 첫 전송이 안 나가면, 그 템플릿을 접고 처음부터 짭니다. 5분은 로컬 검증에서 템플릿 하나가 수정부터 실행까지 걸리는 시간의 두 배입니다 — 여유를 둬도 그 정도면 "맞는 템플릿"은 이미 동작 중이어야 합니다.

2-3. 실전에서 무너지는 지점 — 3요인

로컬 검증을 통과한 코드가 대회에서 깨지는 지점은 대체로 셋입니다.

요인 현상 예방
의존성 대회 PC에 pwntools/특정 패키지가 없음 표준 라이브러리판 유틸 병행(Step 290의 net.py)
경로 다른 디렉터리에서 실행해 파일을 못 찾음 상대 경로 금지, __file__ 기준 절대 경로
형식 대회 서버의 프롬프트·개행이 데모와 다름 EXPECT를 수정 지점으로 격리, 첫 수신을 찍는 습관

경로 요인은 실제로 재현해 봤습니다. 페이로드 파일을 상대 경로로 여는 코드를 라이브러리 폴더 밖에서 실행하면 어떻게 되는지 — 3-3에서 실측을 봅니다.

2-4. 측정 — "빨라졌다"를 숫자로

검증 챕터이므로 감상은 금지입니다. 기록할 것은 두 숫자입니다.

템플릿 사용 기록표 (대회 중 실시간 기입):
| 문제 | 유형 | 템플릿 | 매칭 판단 | 수정→첫전송 | 해결 | 비고 |
|------|------|--------|-----------|-------------|------|------|
| pingpong | 프롬프트 | tcp_prompt | 8초 | 4분 | O | EXPECT만 수정 |
| noteapp | 웹 SSTI | (없음) | — | — | X | payloads만 재사용 |

"수정→첫 전송" 시간이 라이브러리의 성적표입니다. 이전 대회에서 같은 유형을 처음부터 짜 30분 걸렸다면, 4분은 라이브러리가 벌어준 26분입니다. 회고의 "아낀 총시간"은 이 표의 합산으로 계산합니다.


3. 따라 하기

3-1. 대회 전 — 환경 리허설

대회 전날, 대회에 쓸 PC에서 라이브러리 전체를 한 번씩 돌립니다. 작성한 PC가 아니라 대회에 쓸 PC에서입니다. 의존성 요인은 여기서 다 걸러야 합니다.

환경 리허설 체크 (대회 PC에서):
[ ] git clone 받은 직후 그 상태로 목차 생성기가 도는가
[ ] 템플릿 각각이 로컬 데모 서버에 대해 도는가
[ ] 파이썬 버전이 템플릿과 맞는가 (f-string 등)
[ ] 목차 README를 대회 중 볼 위치(세컨드 모니터/인쇄)에 뒀는가

3-2. 로컬 측정 — 템플릿 풀이 시간의 기준선

"5분 판정"의 기준을 감으로만 잡지 말고, 로컬에서 재봅니다. 데모 서버를 매번 새로 띄우며 템플릿 풀이 시간을 5회 측정한 스크립트(step291_measure.py)를 실행해 봤습니다.

실측 (2026-09-09, Python 3.12):

=== 템플릿 기반 풀이 시간 측정 (5회, 로컬 데모) ===
1회차: 0.067초  플래그 획득=성공
2회차: 0.076초  플래그 획득=성공
3회차: 0.074초  플래그 획득=성공
4회차: 0.058초  플래그 획득=성공
5회차: 0.069초  플래그 획득=성공
평균 0.069초 / 최대 0.076초
※ 로컬 측정값 — 대회 서버는 네트워크 지연이 추가됩니다

읽는 법: 순수 실행은 0.1초 미만입니다 — 즉 템플릿 풀이 시간의 거의 전부는 사람이 수정 지점을 고치는 시간입니다. localhost와 대회 서버의 차이(수십~수백 ms)는 그 안에서 오차입니다. 결론: 5분 판정의 5분은 기술이 아니라 사람의 편집 시간에 맞춘 값이며, 익숙한 템플릿이라면 1~2분에 잡혀야 정상입니다.

3-3. 경로 실패 재현 — 대회 중 사고의 예방 주사

2-3의 경로 요인을 실제로 재현합니다. 페이로드를 상대 경로로 여는 코드를 라이브러리 폴더가 아닌 곳에서 실행하면:

실측 (2026-09-09 — tmp_test 폴더에서 ctf_lib의 스크립트 실행):

현재 작업 디렉터리: C:\Users\dlqht\Documents\kimi\tasks\2026-09-08\21-57-12-c850bf8c\tmp_test
FileNotFoundError 발생: [Errno 2] No such file or directory: 'payloads\\web_payloads.py'

해결: __file__ 기준 절대 경로 사용
절대 경로: C:\...\tmp_test\ctf_lib\payloads\web_payloads.py
읽기 성공: 908 바이트

읽는 법: 같은 스크립트가 실행 위치에 따라 죽습니다. 대회 중에는 파일을 여기저기 복사해 쓰게 되므로 이 사고가 거의 반드시 터집니다. 예방은 한 줄 규칙 — 파일을 여는 코드는 Path(__file__).resolve().parent 기준으로만 쓴다. 이 규칙을 이번 대회 전에 모든 템플릿에 적용하세요.

3-4. 대회 운영 — 유형 매칭과 5분 판정의 실행

대회가 시작되면 목차 README를 옆에 펼칩니다. 문제를 열 때마다 2-1의 3단 질문을 돌리고, 2-4의 기록표를 채웁니다.

화면 예시 (대회 중 운영 장면):

14:05 문제 'pingpong' 오픈 — TCP 프롬프트형. 목차 3초 스캔 → tcp_prompt_template 매칭
14:06 템플릿 복사, HOST/PORT/EXPECT 수정 (프롬프트가 '> '가 아니라 '$ ')
14:09 첫 전송 성공, 응답 확인 → TRIGGER 탐색 시작
14:31 해결. 기록: 매칭 8초, 수정→첫전송 3분, 해결 26분

15:20 문제 'notepad' 오픈 — 웹. 템플릿 없음 → payloads/web_payloads.py에서 SSTI 탐지만 재사용
      기록: 뼈대 신규 작성, 탐지 페이로드 재사용으로 10분 절약 추정

16:40 문제 'streamer' — 프롬프트형으로 보여 템플릿 매칭했으나 이중 프롬프트 구조
      5분 뜯어고쳐도 첫 전송 실패 → 판정 규칙 발동, 처음부터 짬 (이후 해결)
      기록: '이중 프롬프트' → 라이브러리 개선 항목

읽는 법: 마지막 사례가 이 대회의 핵심 장면입니다. 템플릿이 안 맞은 것은 실패가 아니라 발견입니다 — "이중 프롬프트"라는 구조 변형이 라이브러리의 사각지대로 등록됐습니다. 5분 판정 규칙이 있었기에 손실이 5분에서 멈췄습니다.

3-5. 회고 — 아낀 시간의 합산과 개선 패치

종료 후 기록표를 합산합니다.

=== 라이브러리 검증 회고 (서식) ===
템플릿 사용 해결: 2문제 (완료 기준 충족)
페이로드만 재사용: 1문제
억지 매칭 후 폐기: 1건 (5분 손실 — 판정 규칙 작동)
아낀 총시간 추정: 약 50분 (이전 대회 같은 유형 소요 대비)

개선 항목 (→ v2 패치):
[ ] 이중 프롬프트 대응 — recvuntil을 2회 연쇄하는 변형 템플릿 추가
[ ] 웹 템플릿 부재 — requests 기반 세션 템플릿 1종 추가
[ ] 경로 규칙 미적용 파일 2개 발견 — 전수 패치

그리고 그 자리에서 패치 커밋을 만듭니다.

cd ctf_lib
git add .
git commit -m "v2: 이중 프롬프트 템플릿 추가, 경로 규칙 전수 패치 (대회#7 회고)"

회고가 커밋으로 끝나야 라이브러리는 살아 있는 자산입니다. "나중에 고쳐야지"로 끝난 개선 항목은 다음 대회에서 같은 5분을 또 먹습니다.


4. 미션과 연습문제

미션 — 라이브러리 실전 투입과 v2 패치

  1. 대회 전날, 대회 PC에서 환경 리허설 체크리스트(3-1)를 수행합니다.
  2. 경로 규칙(__file__ 기준)이 모든 템플릿에 적용됐는지 전수 확인합니다.
  3. 대회 중 유형 매칭 3단 질문과 5분 판정 규칙을 적용하고, 기록표(2-4)를 실시간으로 채웁니다.
  4. 템플릿 활용 해결 2문제 이상을 목표로 합니다.
  5. 종료 후 아낀 총시간을 추정하고, 개선 항목을 커밋(v2)으로 마감합니다.

연습문제

문제 1. 유형 매칭 3단 질문에서 3번("수정 지점이 5분 분량인가")을 묻는 이유는 무엇인가요? 1, 2번만으로는 어떤 오판이 생기나요?

문제 2. 3-2의 실측에서 템플릿 순수 실행은 0.1초 미만이었습니다. 그런데 왜 5분 판정의 기준은 초가 아니라 분 단위인가요?

문제 3. 상대 경로로 파일을 여는 템플릿이 대회 중에 깨지는 이유를 "실행 위치" 관점에서 설명하고, 예방 규칙 한 줄을 써 보세요.

문제 4. 템플릿이 안 맞아 5분 만에 폐기한 건을 "실패"가 아니라 "발견"이라고 부르는 이유는 무엇인가요? 이 건이 회고에서 어떻게 처리되어야 하나요?


5. 모범 답안과 완료 기준

미션 모범 답안

검증 기준으로 확인하세요.

  1. 리허설의 흔적: 대회 PC에서의 clone 직후 실행 기록이 있는가 — 작성 PC에서만 확인한 것은 리허설이 아닙니다.
  2. 기록표의 실시간성: 문제별 매칭 판단·수정 시간이 시각과 함께 있는가 — 종료 후 기억으로 채운 표는 숫자가 부풀려집니다.
  3. 5분 판정의 작동: 억지 매칭 폐기 건이 있다면 5분 근처에서 멈췄는가. 없다면 — 정말 전부 맞았는지, 판정 규칙을 못 쓴 것은 아닌지 돌아봅니다.
  4. 완료 기준: 템플릿 활용 해결 2문제 이상.
  5. v2 커밋: 개선 항목이 커밋 메시지와 함께 저장소에 있는가 — 회고 문서만 있고 코드가 그대로면 검증의 절반만 한 것입니다.

연습문제 해답

문제 1 해답. 형태가 같아도 내부 구조가 다르면 템플릿이 맞지 않기 때문입니다. 1, 2번은 "입출력의 겉모습"만 비교합니다 — TCP 프롬프트라는 점이 같아도 이중 프롬프트·바이너리 섞임 등 구조 차이는 3번에서 걸러집니다. 3번을 생략하면 "있는 줄 알고 붙들었다가" 뜯어고치는 시간이 문제 풀이 시간을 넘는 억지 매칭으로 빠집니다.

문제 2 해답. 템플릿 풀이 시간의 대부분은 컴퓨터의 실행 시간이 아니라 사람의 편집 시간이기 때문입니다. 실행은 0.1초 미만이지만, HOST/PORT를 확인하고 EXPECT를 대회 서버 형식에 맞추고 TRIGGER를 찾는 것은 사람이 키보드로 하는 일입니다. 네트워크 지연은 이 안에서 오차 수준입니다. 그래서 판정 기준은 사람이 현실적으로 수정을 끝내는 시간(1~2분)의 여유를 둔 5분입니다.

문제 3 해답. 상대 경로는 스크립트 파일의 위치가 아니라 명령을 실행한 디렉터리(cwd) 를 기준으로 해석되기 때문입니다. 대회 중에는 템플릿을 문제별 폴더로 복사해 쓰게 되므로 cwd가 매번 달라지고, 같은 파일이 어디서는 열리고 어디서는 FileNotFoundError: [Errno 2]로 죽습니다. 예방 규칙: 파일을 여는 경로는 반드시 Path(__file__).resolve().parent 기준으로 쓴다.

문제 4 해답. 그 건이 라이브러리의 사각지대 좌표를 알려줬기 때문입니다. "이중 프롬프트에는 안 맞는다"는 정보는 다음 대회의 매칭 판단을 정확하게 만드는 자산입니다. 처리 방법은 두 갈래입니다 — ① 사용 조건 주석에 "깨지는 경우"를 명시하거나, ② 변형을 커버하는 새 템플릿을 v2에 추가합니다. 어느 쪽이든 기록으로 남아야 5분의 손실이 지식으로 회수됩니다.

완료 기준 체크리스트

  • [ ] 대회 PC에서 환경 리허설(clone 직후 실행)을 완료했다
  • [ ] 모든 템플릿에 __file__ 기준 경로 규칙을 적용했다
  • [ ] 대회 중 유형 매칭 판단 시간과 수정→첫 전송 시간을 기록했다
  • [ ] 5분 판정 규칙을 적용했다 (폐기 건이 있다면 시간 기록 포함)
  • [ ] 템플릿 활용 해결 2문제 이상을 달성했다
  • [ ] 아낀 총시간을 추정해 회고에 기록했다
  • [ ] 개선 항목을 반영한 v2 커밋을 만들었다

6. 흔한 실수와 해결

벽 1. 대회 PC에서 템플릿이 아예 안 돌아요

증상: 작성 PC에서는 됐는데 대회 PC에서 import 오류, 문법 오류가 납니다.

원인: 파이썬 버전 차이나 패키지 부재 — 2-3의 의존성 요인입니다. 작성 환경과 대회 환경이 다르면 반드시 터집니다.

해결: 3-1의 환경 리허설을 대회 전날 대회 PC에서 하는 것이 정답입니다. 그리고 구조적 예방 — Step 290처럼 표준 라이브러리만으로 도는 유틸을 병행해 두면, pwntools가 없는 환경에서도 뼈대는 살아 있습니다.

벽 2. 자식 프로세스의 출력이 읽히지 않고 측정 스크립트가 죽어요

실측 (2026-09-09 — 측정 스크립트 작성 중 실제로 발생):

UnicodeDecodeError: 'cp949' codec can't decode byte 0xec in position 38: illegal multibyte sequence

원인: 한글 윈도우에서 subprocess.run(..., text=True)는 출력을 시스템 기본 인코딩(cp949)으로 읽습니다. 자식이 UTF-8 한글을 출력하면 디코딩이 깨집니다.

해결: encoding="utf-8"을 명시하세요 — subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8"). 한글 윈도우에서 파이썬끼리 부모-자식 관계를 만들 때의 고질병이니, 측정·자동화 스크립트의 기본 습관으로 삼으세요.

벽 3. 라이브러리가 있으니 처음부터 짜는 법을 잊은 것 같아요

증상: 템플릿이 없는 유형이 나오면 손이 멈춥니다.

원인: 라이브러리 의존 — 템플릿이 사고를 대신하게 된 것입니다.

해결: 그것이 2-4 기록표의 "템플릿 없음" 칸이 있는 이유입니다. 신규 작성 건의 소요 시간이 예전보다 늘었다면, 그 유형은 다음 특훈 주제입니다. 라이브러리는 근육의 대체가 아니라 반복 작업의 위임이어야 합니다 — 처음부터 짜는 근육은 Step 288식 특훈으로 따로 유지하세요.

벽 4. 5분 판정을 못 지키고 계속 뜯어고치게 돼요

증상: "조금만 더 하면 될 것 같은데" 하며 20분이 갑니다.

원인: 매몰 비용 — 이미 고친 5분이 아까워 멈추지 못합니다.

해결: 판정 문장을 미리 정해 두세요 — "지금 이 상태에서 처음부터 짜는 것이 빠른가?" 라고 묻는 것입니다. 이미 쓴 5분은 답에 포함하지 않습니다. 그리고 폐기한 템플릿은 버리지 말고 wip/에 둡니다 — 회고 때 "왜 안 맞았는가"의 증거니까요. 아까운 5분은 사라지는 것이 아니라 개선 항목으로 환전됩니다.

벽 5. 대회 중에 라이브러리를 고치느라 문제를 못 풀어요

증상: 템플릿의 근본적 개선을 대회 중에 시작합니다.

원인: 발견의 흥분 — 사각지대를 보자마자 고치고 싶어집니다.

해결: 대회 중의 수정 허용 범위를 정하세요 — 수정 지점 블록 안의 값만. 구조 개선은 전부 기록만 하고 회고로 보냅니다. 대회 중 라이브러리는 고치는 것이 아니라 쓰면서 메모하는 것입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
유형 매칭 3단 질문 입출력 형태 → 목차 스캔 → 수정 분량 판단, 10초 안에
5분 판정 규칙 첫 전송이 5분 안에 안 나가면 폐기 — 억지 매칭의 차단선
실전 무너짐 3요인 의존성 / 경로 / 입력 형식 — 전부 대회 전에 걸러지는 종류
수정→첫 전송 시간 라이브러리의 성적표 — 실행은 0.1초, 나머지는 사람의 편집
개선 패치 커밋 회고의 마감 형식 — 메모가 아니라 v2 코드로

오늘의 도구·서식

도구·서식 하는 일
환경 리허설 체크리스트 대회 PC에서의 사전 검증 — 의존성 사고 차단
step291_measure.py 템플릿 풀이 시간의 로컬 기준선 측정
__file__ 경로 규칙 실행 위치에 무관한 파일 열기
템플릿 사용 기록표 매칭/수정/해결 시간의 실시간 기록
v2 패치 커밋 대회의 발견을 자산으로 환전

명령어보다 중요한 감각

라이브러리의 가치는 보관량이 아니라 대회당 회수 시간으로 잽니다. 오늘 대회에서 50분을 아꼈다면, 그 50분이 라이브러리에 투자한 시간의 이자입니다. 그리고 이자는 복리입니다 — v2가 다음 대회에서 더 큰 시간을 벌어옵니다.

반대로 기억할 것 — 템플릿은 판단을 대신하지 않습니다. 5분 판정과 "이건 안 맞는다"는 결단은 여전히 여러분의 일입니다. 자산이 커질수록 그 자산을 언제 내려놓는가의 감각이 더 중요해집니다.


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