Step 152. 드림핵 웹 (누적 16) — 기법의 변형 인식

Step 152. 드림핵 웹 (누적 16) — 기법의 변형 인식

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 151을 마쳤다. 드림핵 웹 문제 8개를 풀었고, Write-up을 써 봤다.

  • 준비물: 드림핵 계정(누적 8문제 상태), Burp Suite, 개인 위키, 파이썬 3(로컬 재현용).
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 합법 연습장 안내: 드림핵은 문제별 공격 대상 서버를 공식 발급하는 합법 학습 플랫폼이고, 오늘의 검색창 실험은 내 컴퓨터 안의 로컬 랩입니다. 이 두 곳 외에는 오늘의 기술을 쓰지 않습니다.

첫 8문제를 풀었다면 눈치챘을 것입니다 — 실전 문제는 교과서대로 주지 않습니다. SQL 인젝션은 로그인 폼이 아니라 검색창에 있고, 쿠키 조작은 role=guest가 아니라 JWT로 변장해 있고, 파일 읽기는 "이미지 다운로드"라는 순한 이름 뒤에 숨어 있습니다.

문제 풀이 속도를 결정하는 것은 암기한 페이로드의 수가 아닙니다. "이 기능이 내부에서 무엇을 할까?"를 추측하는 힘입니다. 검색창을 보면 LIKE '%...%' 쿼리를 떠올리고, 다운로드 버튼을 보면 readfile(경로)를 떠올리는 것. 오늘은 그 추측력을 훈련하는 날입니다. 먼저 로컬 랩에서 "옷을 갈아입은 SQLi"를 실측하고, 실전 8문제를 추가로 깨 누적 16을 만듭니다.


1. 학습 목표

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

  • 같은 취약점이 다른 기능(검색창, 다운로드, 장바구니)에 숨는 방식을 설명한다
  • 기능을 보고 내부 동작(SQL 쿼리, 파일 읽기)을 추측하는 습관을 들인다
  • 추측의 적중 여부를 풀이 후에 기록해 추측력을 추적한다
  • 막힌 문제를 "다음 날 재도전" 규칙으로 관리한다
  • 누적 16문제 달성과 기법 빈도 TOP 3 정리를 완료한다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 드림핵 워게임 + 파이썬 3 + Flask·sqlite3(로컬 재현 랩)
오늘의 명령 ' OR 1=1 -- 인젝션 페이로드, Burp Repeater, write-up의 "추측" 칸
필요한 개념 취약점의 기능 변장, LIKE 쿼리, 내부 동작 추측, 기법 빈도 분석
오늘의 산출물 누적 16문제 + 추측-적중 기록표 + 기법 TOP 3

2-1. 취약점은 옷을 갈아입는다

Step 134~150에서 배운 기법들은 "어떤 화면"에 있었는지가 아니라 "어떤 코드 패턴"에서 생겼습니다. 문자열 이어붙이기로 SQL을 만들면 어디든 인젝션입니다 — 로그인 폼이든, 검색창이든, 상품 정렬 드롭다운이든.

그래서 실전 문제 풀이의 첫 질문은 "이 문제의 취약점이 뭐지?"가 아닙니다. "이 화면의 각 기능이 서버 안에서 무슨 코드를 돌릴까?"입니다. 검색창은 DB 검색 쿼리를, 이미지 보기는 파일 읽기를, 환율 계산기는 외부 요청을 돌립니다. 기능이 보이면 코드가 보이고, 코드가 보이면 취약점의 후보가 정해집니다.

2-2. 추측-검증 루프

오늘 정착시킬 훈련법입니다.

① 기능 목록 작성 — 이 페이지의 입력·버튼을 전부 나열
② 내부 동작 추측 — 각 기능마다 "아마 이런 코드"를 한 줄로 글로 적는다
③ 기법 매칭 — 그 코드 패턴에 해당하는 배운 기법을 붙인다
④ 실험 — 매칭된 페이로드를 시험한다
⑤ 채점 — 풀고 나서 ②의 추측이 맞았는지 기록한다

핵심은 ②를 "글로 먼저 쓰는 것"입니다. 머릿속 추측은 틀려도 배우는 것이 없지만, 적어 둔 추측은 채점이 됩니다. 열 문제를 이렇게 채점하면 자기 추측의 편향(예: "나는 파일 경로를 과소평가한다")이 데이터로 보입니다.

2-3. 배포형 문제의 시간 제한

드림핵의 많은 웹 문제는 접속할 때마다 임시 서버를 띄워 주는 배포형입니다 (화면 예시 — 플랫폼 화면은 직접 확인). 서버 주소는 일정 시간 뒤 만료되고, 만료되면 풀이가 날아가는 게 아니라 서버만 새로 받으면 됩니다. 단, 플래그를 찾기 직전에 만료되면 처음부터 다시 뚫어야 하니, 진행 상황(성공한 페이로드까지의 경로)을 수시로 메모해 두세요.


3. 따라 하기

3-1. 로컬 랩 — 검색창에 숨은 SQLi

"옷을 갈아입는다"가 무슨 뜻인지 직접 만듭니다. Step 92~93의 SQL 지식과 Step 104의 인젝션을 합친 실험입니다. search_sqli_lab.py:

import sqlite3
from flask import Flask, request

app = Flask(__name__)

db = sqlite3.connect(":memory:", check_same_thread=False)
db.execute("CREATE TABLE products (name TEXT, price INTEGER, note TEXT)")
db.executemany("INSERT INTO products VALUES (?, ?, ?)", [
    ("사과", 1000, "신선함"),
    ("바나나", 1500, "달콤함"),
    ("비밀상품", 999999, "flag{search_sqli_too}"),
])
db.commit()

@app.route("/search")
def search():
    q = request.args.get("q", "")
    # 취약: 문자열 이어붙이기 — 로그인 폼이 아니라 '검색창'에도 같은 구멍
    sql = f"SELECT name, price FROM products WHERE name LIKE '%{q}%'"
    try:
        rows = db.execute(sql).fetchall()
    except sqlite3.Error as e:
        return f"DB 오류: {e}\n실행된 SQL: {sql}\n"
    out = [f"실행된 SQL: {sql}", "--- 결과 ---"]
    out += [f"{name} ({price}원)" for name, price in rows]
    return "\n".join(out) + "\n"

if __name__ == "__main__":
    app.run(port=5494)

3-2. 정상 검색과 인젝션 비교

서버를 켜고(python search_sqli_lab.py), 브라우저나 파이썬으로 검색합니다. 먼저 정상 검색:

http://127.0.0.1:5494/search?q=사과

출력 (2026-09-09 실측):

실행된 SQL: SELECT name, price FROM products WHERE name LIKE '%사과%'
--- 결과 ---
사과 (1000원)

이제 검색창에 검색어가 아니라 SQL 조각을 넣습니다:

http://127.0.0.1:5494/search?q=' OR 1=1 -- 

출력 (2026-09-09 실측):

실행된 SQL: SELECT name, price FROM products WHERE name LIKE '%' OR 1=1 -- %'
--- 결과 ---
사과 (1000원)
바나나 (1500원)
비밀상품 (999999원)

읽는 법: 입력의 따옴표(')가 LIKE '%의 따옴표를 닫아 버렸고, OR 1=1이 "항상 참"을 추가해 전 상품이 나왔습니다. --로 뒤의 %'를 주석 처리해 문법 오류를 피했습니다. 로그인 폼에서 배운 그 기법이, 화면은 완전히 다른 검색창에서 그대로 먹혔습니다. 숨겨진 비밀상품 — 실전이라면 이 자리에 플래그가 있습니다.

: 이 실험의 요점은 페이로드가 아니라 위치입니다. 취약점은 "검색 기능"이라는 옷을 입고 있었지만, 본질은 Step 104의 문자열 이어붙이기 SQL이었습니다. 옷을 벗기는 능력이 2-2의 추측력입니다.

3-3. 추측-적중 기록표 만들기

개인 위키에 guess-log.md를 만들고 표를 준비합니다.

| 문제 | 기능 | 내부 동작 추측 | 매칭 기법 | 적중? | 틀렸다면 실제 |
|------|------|--------------|-----------|-------|--------------|
| 예시 | 상품 검색창 | LIKE 쿼리 이어붙이기 | SQLi | O | |
| 예시 | 이미지 다운로드 | readfile(경로) | 경로 조작(LFI) | X | 실제는 파일명 화이트리스트 우회 |

문제를 시작하기 전에 앞의 네 칸을 먼저 채우고, 푼 뒤에 뒤의 두 칸을 채웁니다. 이 표가 오늘의 핵심 산출물입니다.

3-4. 실전 8문제 — 추측력 훈련

드림핵 웹 카테고리에서 난이도 1~2 문제 8개를 고릅니다. 각 문제에서 절차는:

① 정찰 — 페이지 전부, 소스, 쿠키, Network 탭의 API
② 기능 목록 — 입력창·버튼·링크 나열
③ 추측 기록 — guess-log.md에 "아마 이런 코드" 작성
④ 실험 — 매칭한 기법의 페이로드 시험 (배운 것: SQLi, 쿠키, 소스 읽기, IDOR, JWT...)
⑤ 채점 — 풀고 나서 추측 적중 여부 기록

소스 코드를 제공하는 문제(파일 다운로드 버튼이 있는 문제)는 반드시 코드를 전부 읽으세요. 힌트는 설명란이 아니라 코드에 있습니다 — 검증 함수가 어떤 입력을 걸러내는지 읽으면 우회법이 보입니다.

3-5. 막힌 문제의 관리 — 다음 날 재도전 규칙

30분 이상 진전이 없으면 멈춥니다. 풀이를 보지 말고, 메모에 이렇게 남깁니다:

■ 재도전 대기: (문제명)
- 시도한 것: (페이로드 목록)
- 다음 가설 후보: (아직 안 해본 것)
- 재도전일: 내일

풀이를 보면 그 문제는 훈련 데이터에서 탈락합니다 — "찾는 힘"이 아니라 "읽는 힘"만 길러지기 때문입니다. 다음 날 눈이 리셋된 상태에서 다시 보면 보이는 경우가 놀라울 만큼 많습니다. 재도전에서도 막히면 그때 풀이를 보고, 배운 기법을 기법 카드(Step 105)에 추가합니다.


4. 미션과 연습문제

미션 — 누적 16과 추측력 데이터

  1. 로컬 랩에서 검색창 인젝션으로 비밀상품을 노출시킵니다
  2. 드림핵 웹 문제 8개를 추가로 해결해 누적 16개를 만듭니다
  3. 8문제 전부에서 "기능 목록 → 내부 동작 추측"을 풀이 전에 기록합니다
  4. 풀이 후 추측 적중 여부를 채점하고, 추측이 틀린 문제는 실제 정답을 함께 적습니다
  5. 16문제의 기법 빈도를 세어 TOP 3를 정리합니다

연습문제

문제 1. 3-2 실험에서 ' OR 1=1 -- 가 검색창에서도 먹힌 이유를, 실행된 SQL 문자열을 근거로 설명해 보세요.

문제 2. "취약점은 옷을 갈아입는다"는 말의 의미를, 페이로드와 코드 패턴의 관계로 설명해 보세요.

문제 3. 추측을 글로 먼저 쓰는 것이 머릿속 추측보다 나은 이유는 무엇인가요?

문제 4. 막힌 문제의 풀이를 바로 보지 않는 이유를 "훈련 데이터" 관점에서 설명해 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

1번은 3-2의 실측 그대로입니다 — q=' OR 1=1 -- 로 세 행 전부 노출, 그중 비밀상품 확인.

2~4번은 플랫폼에서 진행합니다. guess-log.md가 이런 밀도면 합격입니다 (화면 예시):

| cookie-2  | 권한 쿠키  | 단순 문자열 비교      | 쿠키 값 변경 | O | |
| photo-dl  | 다운로드   | readfile(파라미터)    | LFI ../../   | X | 실제는 ?file= 뒤 필터 우회 |

5번의 TOP 3 정리 예시 (화면 예시 — 여러분의 실제 데이터로):

16문제 기법 빈도: 쿠키/세션 조작 5, SQLi 4, 소스·파일 읽기 3, 기타 4
→ TOP 3: 쿠키/세션, SQLi, 파일 읽기

검증하는 법: ① 프로필의 해결 수가 16 이상인가. ② guess-log의 "적중?" 칸이 8행 채워져 있는가. ③ TOP 3가 표의 실제 개수와 일치하는가 — 합계가 16이어야 합니다.

연습문제 해답

문제 1 해답. 서버가 입력을 검증 없이 SQL 문자열에 이어 붙였기 때문입니다. 실측에서 실행된 SQL은 WHERE name LIKE '%' OR 1=1 -- %'였고, 내 입력의 따옴표가 문자열을 닫고 OR 1=1이 참 조건을 추가해 전 행이 반환됐습니다. 기능이 검색이든 로그인이든, "입력을 이어 붙여 SQL을 만든다"는 코드 패턴이 같으면 같은 공격이 성립합니다.

문제 2 해답. 페이로드는 코드 패턴에 결합하는 것이지 화면에 결합하는 것이 아닙니다. 문자열 이어붙이기 SQL이라는 패턴은 로그인 폼·검색창·정렬 옵션 어디에나 존재할 수 있고, 패턴이 같으면 같은 페이로드가 통합니다. 그래서 문제의 화면(옷)이 아니라 내부 코드(몸)를 추측해야 합니다.

문제 3 해답. 적어 둔 추측만 채점할 수 있기 때문입니다. 채점이 누적되면 "내가 어떤 유형의 추측을 잘하고 못하는지"가 데이터로 드러나고, 약한 유형을 집중 훈련할 수 있습니다. 머릿속 추측은 틀린 순간 흔적 없이 사라져 같은 오류를 반복합니다.

문제 4 해답. 풀이를 본 문제는 "취약점을 스스로 찾아내는 힘"을 측정하는 데이터로 쓸 수 없게 되기 때문입니다. 정답을 아는 상태의 재풀이는 발견 능력이 아니라 재현 능력만 연습합니다. 기록해 두고 재도전하면 그 문제는 여전히 유효한 훈련 데이터로 남습니다.

완료 기준 체크리스트

  • [ ] 검색창에서 SQLi로 숨겨진 행을 노출시켰다
  • [ ] "기능 → 내부 코드 추측 → 기법 매칭" 절차를 말할 수 있다
  • [ ] 추측-적중 기록표를 만들어 사용했다
  • [ ] 소스 제공 문제에서 코드를 끝까지 읽는 습관이 붙었다
  • [ ] "다음 날 재도전" 규칙으로 막힌 문제를 관리한다
  • [ ] 배포형 서버의 만료와 재발급을 이해한다
  • [ ] 미션: 누적 16문제 + 기법 TOP 3 정리 완료

6. 흔한 실수와 해결

벽 1. 인젝션을 넣었더니 DB 오류만 난다

증상 (로컬 랩 2026-09-09 실측 계열):

DB 오류: unrecognized token: ...
실행된 SQL: SELECT ... LIKE '%' ...

원인: 따옴표만 넣고 뒤처리를 안 해서 SQL 문법이 깨졌습니다. 오류는 오히려 희소식입니다 — 내 입력이 SQL에 들어간다는 증거입니다.
해결: --(또는 DB에 따라 #)로 나머지를 주석 처리하세요. '만 넣어 오류가 나는지 먼저 확인하는 것이 인젝션 탐지의 첫 시험입니다.

벽 2. 배포형 문제 서버가 중간에 죽는다

증상 (출력 예시): 잘 되던 서버가 응답하지 않고 연결이 끊깁니다.
원인: 임시 서버의 수명이 끝났습니다.
해결: 문제 페이지에서 재배포받으세요. 플래그 직전에 날리지 않으려면, 성공한 단계의 페이로드를 진행 중에 수시로 메모해 두는 습관이 답입니다.

벽 3. 추측이 매번 틀린다

원인: 정상입니다. 추측력은 데이터가 쌓여야 생기는 감각이라, 처음 10문제는 적중률이 낮은 것이 표준입니다.
해결: 적중률이 아니라 "추측을 썼는가"를 완료 기준으로 삼으세요. 틀린 추측 + 실제 정답의 쌍이 10개 쌓이면 11번째부터 달라집니다.

벽 4. 소스 코드가 주어졌는데 어디를 봐야 할지 모르겠다

원인: 코드를 "앞에서부터" 읽고 있습니다.
해결: 거꾸로 읽으세요. ① 플래그가 어디 있는지(파일? 환경변수? 코드 내?) ② 그곳에 도달하는 조건문 ③ 그 조건을 통과시키는 입력. 이 역순 추적이 소스 읽기 문제의 정석입니다.

벽 5. 페이로드를 넣었는데 아무 변화도 없다

원인: 서버가 필터링 중이거나, 페이로드가 다른 곳(예: 응답이 아닌 로그)으로 갔거나, 요청 형식(JSON 등)이 틀렸습니다.
해결: Burp Repeater로 원본 요청과 내 요청의 응답을 나란히 비교하세요. 상태 코드·길이·본문 중 하나라도 다르면 반응이 있는 것입니다. 완전히 같다면 입력이 코드에 도달하지 않은 것 — Network 탭에서 실제 전송 값을 확인하세요.


7. 정리

오늘의 개념

개념 한 줄 설명
기법의 변형 같은 취약점이 다른 기능의 옷을 입고 나타남
내부 동작 추측 기능을 보고 서버 코드를 상상하는 훈련
추측-적중 기록 추측을 글로 쓰고 채점해 추측력을 데이터화
배포형 문제 접속마다 임시 서버 발급 — 만료와 재배포가 있음
재도전 규칙 막힌 문제는 풀이를 보지 말고 다음 날 다시

오늘의 명령어·페이로드

명령·페이로드 하는 일
' (따옴표 하나) 인젝션 탐지 — DB 오류 유도 시험
' OR 1=1 -- LIKE·WHERE 조건 무력화
/search?q=... 형태 GET 파라미터로 검색어 전달
guess-log.md 표 추측-적중 채점 기록
재도전 대기 메모 막힌 문제의 상태 관리

명령어보다 중요한 감각

화면을 보면 코드를 상상하세요. 검색창은 LIKE 쿼리이고, 다운로드 버튼은 readfile이고, 장바구니 수량은 곱셈 한 줄입니다. 이 상상이 빠른 사람이 문제를 빨리 풉니다 — 그리고 이 감각은 재능이 아니라 채점된 추측의 누적으로 생깁니다. 틀린 추측도 자산입니다. 오늘부터 여러분의 가장 중요한 도구는 페이로드 목록이 아니라, 매 문제마다 먼저 쓰는 한 줄의 "아마 이 코드"입니다.


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