Step 133. Burp Suite 2: Repeater와 Intruder — 반복 실험대와 자동 기관총

Step 133. Burp Suite 2: Repeater와 Intruder — 반복 실험대와 자동 기관총

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

전제: Step 132(프록시 가로채기), Step 131(대상 웹앱)을 마쳤다.

  • 준비물: Burp Suite, Step 131의 웹앱, 짧은 단어 리스트(직접 만들거나 Kali의 /usr/share/wordlists). Burp의 GUI 화면은 화면 예시, 원리 검증 실험은 실측(2026-09-09, 파이썬 requests + curl, 대상은 Step 131 서버)으로 표시했습니다.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.

Step 132에서 우리는 요청을 잡아 한 번 고쳐 보냈습니다. 그런데 웹 공격의 실제 노동은 반복입니다 — 따옴표를 넣어 보고, 바꿔 보고, 다시 넣어 보는 수십 번의 실험. Burp에는 그 노동을 위한 두 도구가 있습니다. Repeater는 요청 하나를 수백 번 고쳐 쏘는 실험대이고, Intruder는 지정한 자리에 사전(단어 목록)을 자동으로 대입해 쏘는 기관총입니다. 오늘 둘 다 손에 넣습니다.


1. 학습 목표

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

  • 잡은 요청을 Repeater로 보내 반복 실험할 수 있다
  • 파라미터 값을 바꿔 가며 서버 응답(상태 코드·길이)의 차이를 관찰한다
  • Intruder의 페이로드 위치(§)를 지정하고 단어 리스트를 대입해 실행한다
  • 응답을 상태 코드와 길이로 정렬해 "다른 응답"을 찾아낸다
  • Repeater와 Intruder의 원리를 파이썬 반복문으로 재현할 수 있다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 Burp Suite Community, 대상은 Step 131 웹앱, 원리 실측은 파이썬 requests
오늘의 도구 Burp Repeater(반복 실험), Burp Intruder(자동 대입), § 페이로드 위치
필요한 개념 퍼징(fuzzing), 페이로드(payload), 상태 코드/응답 길이 정렬, 스레드 조절
오늘의 산출물 내 웹앱의 숨겨진 경로 1개 발굴 기록 + 원리 재현 스크립트

2-1. Repeater — 수백 번의 "이번에는?"

웹 취약점 실험은 "입력을 조금 바꿨을 때 서버가 다르게 반응하는가"의 연속입니다. 브라우저로 하면 매번 주소창을 고쳐야 하지만, Repeater는 요청 원문을 고정해 두고 Send 버튼 하나로 재전송합니다 — 고치고, 쏘고, 응답 보고, 다시 고치고.

SQL 인젝션 챕터(Step 93, 104)에서 따옴표를 넣었다 뺐다 한 것, Natas에서 파라미터를 바꿔 본 것 — 그 실험 전부가 Repeater의 사용 장면입니다.

2-2. Intruder — 자리바꿈 + 사전 대입

Intruder의 아이디어는 단순합니다. ① 요청에서 바꿀 자리§(섹션 기호)로 표시하고, ② 그 자리에 넣을 페이로드 목록(payload — 차례로 대입할 값들)을 주면, ③ 목록 수만큼 요청을 자동으로 만들어 쏘고, ④ 결과를 표로 모아 줍니다.

GET /§§ HTTP/1.1처럼 경로 자체에 걸 수도 있고, pw=§§처럼 파라미터 값에 걸 수도 있습니다. 전자는 숨은 경로 찾기, 후자는 로그인 브루트포스의 형태가 됩니다.

2-3. 퍼징과 "다른 응답" 찾기

이런 자동 대입을 퍼징(fuzzing)이라고 합니다. 핵심 기술은 쏘는 것이 아니라 읽는 것 — 수십 개의 응답 중 대부분은 똑같은 404 페이지입니다. 그래서 결과 표를 상태 코드와 응답 길이로 정렬합니다. 404가 전부 207바이트인데 하나만 53바이트라면? 그 하나가 숨어 있던 문입니다. 퍼징의 발견은 언제나 "나머지와 다른 녀석"입니다.


3. 따라 하기

3-1. Repeater로 보내기 — 실험대에 올리기

Step 131 서버를 띄워 둡니다. Burp의 HTTP history에서 로그인 POST 요청을 찾아 우클릭 → Send to Repeater. Repeater 탭에 요청 원문이 그대로 옮겨졌습니다.

화면 예시 (Repeater에 올라온 요청):

POST /login HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/x-www-form-urlencoded
Content-Length: 24

uid=nadia&pw=wrong

Send를 누르면 오른쪽에 응답이 나타납니다 — 우리 웹앱 기준으로 401과 실패 메시지. 이제 wrongblue-fox-31로 고쳐 다시 Send — 302Set-Cookie가 돌아옵니다. 고치고, 쏘고, 비교하고. 이 사이클이 빨라지는 것이 Repeater의 존재 이유입니다.

3-2. Repeater의 원리 실측 — 같은 요청, 값만 바꿔 반복

Repeater가 하는 일의 본질은 반복문입니다. curl로 그 원리를 그대로 재현해 실측했습니다 — 대상은 Step 131 서버의 /login.

입력

for u in nadia guest admin; do
  code=$(curl -s -o /dev/null -w "%{http_code}" -X POST -d "uid=$u&pw=wrong" http://127.0.0.1:5000/login)
  echo "uid=$u (틀린 pw) -> $code"
done
curl -s -o /dev/null -w "uid=nadia (맞는 pw) -> %{http_code}\n" -X POST -d "uid=nadia&pw=blue-fox-31" http://127.0.0.1:5000/login

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

uid=nadia (틀린 pw) -> 401
uid=guest (틀린 pw) -> 401
uid=admin (틀린 pw) -> 401
uid=nadia (맞는 pw) -> 302

읽는 법: 네 번의 요청은 구조가 같고 값만 다릅니다 — 그리고 응답 상태 코드가 401 401 401 … 302로 갈립니다. Repeater 화면에서 여러분이 눈으로 하던 비교를, 여기서는 숫자 나열로 했습니다. 도구가 바뀌어도 "같은 요청, 다른 값, 응답 비교"라는 실험 구조는 하나입니다.

3-3. Intruder 준비 — §로 자리 표시하기

HTTP history에서 GET / HTTP/1.1 요청을 우클릭 → Send to Intruder. Intruder 탭의 Positions 화면에서, 요청의 경로 부분을 드래그하고 Add §를 누릅니다.

화면 예시 (위치 지정 후):

GET /§§ HTTP/1.1
Host: 127.0.0.1:5000

읽는 법: § 사이가 "여기가 매 요청마다 바뀔 자리"라는 표시입니다. Attack type은 하나의 자리만 바꾸는 Sniper(기본값)로 둡니다. 다음으로 Payloads 탭에서 단어 리스트를 붙입니다 — 직접 만든 짧은 목록이면 충분합니다 (admin, backup, test, login, secret, config …).

3-4. Intruder 실행 — 기관총이 쏘고, 표가 모은다

Start attack을 누르면(Community 판은 속도 제한이 있습니다 — 느려도 정상입니다) 결과 창에 요청별 응답이 쌓입니다. StatusLength 열 머리를 눌러 정렬하세요.

화면 예시 (결과 표):

Payload      Status   Length
backup       200      53     <-- 나머지와 다르다!
admin        200      27     <-- 이것도!
login        200      271
dashboard    302      268
test         404      276
secret       404      276
config       404      276

읽는 법: 404 행들은 길이까지 똑같습니다 — 같은 오류 페이지니까요. 정렬 한 번으로 "다른 응답" 두 개가 위로 떠오릅니다. 우리 웹앱에 /backup, /admin이라는 숨은 경로가 있었다는 뜻입니다.

3-5. Intruder의 원리 실측 — 파이썬으로 같은 짓 하기

Intruder의 내부도 결국 반복문입니다. 같은 단어 리스트를 파이썬 requests로 대입해 실측했습니다 — Burp 결과와 비교해 보세요.

입력 (fuzz.py):

import requests

BASE = "http://127.0.0.1:5000"
WORDLIST = ["admin", "backup", "test", "login", "dashboard", "secret",
            "config", "upload", "robots.txt", "api", "old", "dev"]

for word in WORDLIST:
    r = requests.get(f"{BASE}/{word}", allow_redirects=False)
    print(f"{r.status_code:<6}{len(r.content):<8}/{word}")

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

200   27      /admin
200   53      /backup
200   202     /login
302   199     /dashboard
404   207     /api
404   207     /config
404   207     /dev
404   207     /old
404   207     /robots.txt
404   207     /secret
404   207     /test
404   207     /upload

읽는 법: 3-4의 화면 예시와 같은 결론입니다 — 404 여덟 개는 전부 207바이트(같은 오류 페이지), 그 외 /admin, /backup, /login, /dashboard만 다른 얼굴. 단어 12개로 우리 웹앱의 지도가 그려졌습니다. allow_redirects=False는 302를 따라가지 않고 원래 상태 코드를 보기 위한 옵션입니다 — Burp가 결과 표에 보여 주는 것도 원래 응답입니다.

: Intruder를 "마법의 도구"가 아니라 "내가 짤 수 있는 12줄짜리 반복문"으로 이해하면, 도구가 없는 환경(시험, 제한된 랩)에서도 같은 공격을 할 수 있습니다.

3-6. 속도 조절과 예의 — 랩 서버도 버거워한다

Intruder는 순식간에 수백 요청을 쏩니다. 내 랩의 작은 Flask 서버도 많은 동시 요청에는 버거워하고, Community 판은 그래서 속도 제한이 기본입니다. 실습 팁 두 가지:

  • Intruder의 Resource pool 설정에서 동시 요청 수(스레드)를 낮추면 대상 서버가 숨 쉴 틈이 생깁니다.
  • 서버 로그를 함께 띄워 두세요 — 퍼징 한 번에 로그 수십 줄이 찍히는 것을 보면, 자동화된 공격은 서버에 시끄럽게 기록된다는 것이 체감됩니다. 탐지의 관점에서 기억해 둘 사실입니다.

4. 미션과 연습문제

미션 — 숨겨진 경로 발굴과 원리 재현

  1. Step 131 웹앱에, 링크가 어디에도 없는 라우트 2개를 추가합니다 (예: /backup, /admin — 짧은 문자열을 반환하면 충분).
  2. Burp Intruder로 GET /§§ 위치 퍼징을 실행해, 상태 코드·길이 정렬만으로 그 두 경로를 찾아냅니다. 결과 표를 기록합니다.
  3. 로그인 POST의 pw=§§ 자리에 짧은 단어 리스트를 대입해 브루트포스 형태의 퍼징도 경험합니다 — 올바른 비밀번호를 목록에 넣어 두고, 302로 갈리는 행을 찾으세요.
  4. 3-5의 파이썬 퍼저를 직접 작성해 같은 경로들을 찾고, Burp 결과와 대조합니다.
  5. 퍼징하는 동안의 서버 로그를 저장합니다 — "자동화 공격이 로그에 어떻게 보이는가"를 확인하는 것이 목적입니다.
  6. 위키에 Repeater와Intruder.md를 작성합니다 — 두 도구의 차이 3줄, "다른 응답 찾기" 요령 3줄.

연습문제

문제 1. Repeater와 Intruder의 용도 차이를 "실험 횟수와 자동화" 관점에서 설명해 보세요.

문제 2. 퍼징 결과에서 404 응답들의 길이가 전부 같은 이유는 무엇이며, 이 성질이 발견에 어떻게 쓰이나요?

문제 3. GET /§§처럼 경로에 페이로드 위치를 거는 것과 pw=§§처럼 값에 거는 것은 각각 어떤 공격이 되나요?

문제 4. 3-5 실측에서 allow_redirects=False를 빼면 /dashboard의 결과가 어떻게 달라지고, 왜 그것이 분석을 헷갈리게 하나요?


5. 모범 답안과 완료 기준

미션 모범 답안

검증 포인트:

  1. 발굴의 대조: Intruder 결과 표에서 404와 길이가 다른 행들만 골라낸 화면이 기록돼 있는가. 3-4 화면 예시처럼 backup(53바이트), admin(27바이트)이 404 무리(모두 같은 길이)에서 떠오르면 성공입니다.
  2. 브루트포스 퍼징: pw=§§ 퍼징에서 올바른 비밀번호 행만 302이고 나머지는 401 — 3-2 실측과 같은 패턴이 재현됐는가.
  3. 원리 재현: 파이썬 퍼저의 출력이 Burp의 발견과 일치하는가. (2026-09-09 실측에서는 둘 다 /admin, /backup을 찾았습니다.)
  4. 로그 관찰: 서버 로그에 단어 수만큼의 GET /<단어> 404 줄이 시간 순으로 찍혀 있는가 — 이것이 "공격은 시끄럽다"의 증거입니다.

연습문제 해답

문제 1 해답. Repeater는 사람이 값을 하나씩 고쳐 쏘는 수동 반복 실험대 — "이번에는 이 값이면?"이라는 가설 검증에 씁니다. Intruder는 위치(§)와 목록을 주면 사람이 손을 뗀 사이 전부 쏘는 자동 대입기 — 후보가 수십, 수백 개일 때(경로 탐색, 브루트포스) 씁니다. 실험 하나의 깊이는 Repeater, 실험의 넓이는 Intruder입니다.

문제 2 해답. 없는 경로에는 서버가 같은 오류 페이지를 돌려주므로 응답 길이도 같아집니다 (3-5 실측: 404 여덟 개 전부 207바이트). 이 성질 덕에 길이 정렬 한 번으로 "같은 것들의 무리"와 "다른 녀석"이 갈리고, 퍼징의 발견은 곧 이 다른 녀석을 찾는 일이 됩니다.

문제 3 해답. 경로(GET /§§)에 걸면 주소 자체를 대입하며 숨은 경로·디렉터리 탐색(디렉터리 퍼징)이 되고, 값(pw=§§)에 걸면 같은 경로에 다른 입력을 대입하는 것이라 로그인 브루트포스나 파라미터 탐색이 됩니다. 자리를 어디에 놓는가가 공격의 종류를 결정합니다.

문제 4 해답. allow_redirects=False가 없으면 requests가 302를 자동으로 따라가서 최종 응답(로그인 페이지, 200)을 보여 줍니다. /dashboard가 "302(로그인 필요해서 쫓겨남)"인지 "200(통과)"인지 구분이 사라져, 인증 상태를 읽는 분석이 무너집니다. Burp의 결과 표가 원래 응답을 보여 주는 것과 같은 이치입니다 — 리다이렉트 자체가 정보입니다.

완료 기준 체크리스트

  • [ ] 요청을 Repeater로 보내 고쳐 쏘는 사이클을 할 수 있다
  • [ ] 상태 코드의 차이(401 vs 302)로 서버 판정을 읽을 수 있다
  • [ ] Intruder에 § 위치를 지정하고 단어 리스트를 대입해 실행할 수 있다
  • [ ] 결과를 상태 코드·길이로 정렬해 "다른 응답"을 찾아냈다
  • [ ] 파이썬 반복문으로 같은 퍼징을 재현하고 결과를 대조했다
  • [ ] 퍼징이 서버 로그에 남는 모습을 확인했다
  • [ ] 미션: 숨은 경로 2개 발굴 기록과 위키 문서를 완성했다

6. 흔한 실수와 해결

벽 1. Intruder 결과가 전부 같은 값으로만 나와요

증상: 페이로드가 안 바뀌고 같은 요청이 반복됩니다.

원인: § 표시가 없거나, Positions 화면에서 Burp가 자동으로 잡아 준 위치를 지우고(Clear §) 다시 지정하지 않았습니다.

해결: Positions에서 요청 원문을 확인하세요 — 바꿀 자리가 정확히 §...§로 둘러싸여 있어야 합니다. 자동 지정된 불필요한 표시는 Clear §로 지우고 필요한 곳만 Add § 하세요.

벽 2. Community 판인데 공격이 너무 느려요

증상: 요청이 초당 1개꼴로 나갑니다.

원인: Burp Community 판의 의도적 속도 제한입니다. 고장이 아닙니다.

해결: 단어 리스트를 짧게(수십 개) 유지하세요 — 입문 실습에서는 짧은 목록으로 원리를 익히는 것이 목표입니다. 큰 사전이 필요한 순간이 오면 3-5처럼 파이썬으로 직접 짜는 것이 더 빠릅니다(실측에서도 12개 경로를 즉시 끝냈습니다).

벽 3. 퍼징 중에 웹앱이 죽거나 반응이 없어요

증상: 서버가 응답을 멈추거나 오류를 냅니다.

원인: 동시 요청이 몰려 개발용 서버(Step 94의 경고 문구 기억하시죠)가 버거워졌습니다.

해결: Resource pool의 동시 요청 수를 1~2로 낮추세요. Flask 개발 서버를 --threaded 없이 쓰고 있다면 그것도 원인입니다 — 실습용으로는 느린 퍼징이 오히려 안전합니다.

벽 4. 응답 길이가 미세하게 달라서 "다른 응답" 판별이 어려워요

증상: 404인데도 길이가 몇 바이트씩 다릅니다.

원인: 오류 페이지에 요청 경로나 시각처럼 매번 바뀌는 값이 포함된 경우입니다.

해결: 길이 대신 상태 코드를 1차 기준으로 삼고, 그래도 애매하면 응답 본문을 직접 몇 개 열어 비교하세요. Burp의 필터(검색어 포함 여부 등)로 "에러 문구 없는 것"만 골라내는 방법도 있습니다.

벽 5. 로그인 브루트포스에서 성공했는지 구분이 안 돼요

증상: 올바른 비밀번호를 쐈는데 결과 표에서 티가 안 납니다.

원인: 성공과 실패의 응답 차이(상태 코드, 길이, 리다이렉트)를 미리 몰라서 생기는 일입니다.

해결: 퍼징 전에 Repeater에서 성공·실패 각각 한 번씩 쏴서 차이를 먼저 기록해 두세요 — 우리 웹앱은 실패 401(64바이트), 성공 302(207바이트 안팎)였습니다 (2026-09-09 실측). 기준선이 있어야 편차가 보입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
Repeater 요청 하나를 고쳐 쏘는 수동 반복 실험대
Intruder § 자리에 목록을 자동 대입해 쏘는 도구
페이로드(payload) 대입할 값들의 목록
퍼징(fuzzing) 입력을 마구 바꿔 넣으며 반응을 관찰하는 탐색
"다른 응답" 찾기 상태 코드·길이 정렬로 이상치를 골라내는 요령
스레드/속도 조절 대상 서버를 배려하고 로그를 읽기 위한 절제

오늘의 명령·조작

명령/조작 하는 일
우클릭 → Send to Repeater / Intruder 잡은 요청을 각 도구로 보내기
Repeater Send 같은 요청 재전송 (고친 값으로)
Add § / Clear § 페이로드 위치 지정/해제
Payloads 탭 대입할 단어 리스트 로드
Status/Length 열 정렬 "다른 응답" 골라내기
requests.get(url, allow_redirects=False) 파이썬 퍼저 — 원래 상태 코드 보기
curl -w "%{http_code}" 상태 코드만 뽑는 반복 실험

명령어보다 중요한 감각

Repeater와 Intruder는 웹 공격의 손과 발입니다 — 앞으로 배우는 거의 모든 웹 공격(SQLi, XSS, 인증 우회)이 "Repeater로 가설을 확인하고, Intruder로 후보를 쓸어 담는" 이 두 동작의 변주입니다.

그리고 오늘 파이썬으로 재현해 봤듯, 이 도구들의 심장은 반복문 하나입니다. § 표시는 곧 "for word in wordlist"이고, 결과 표는 곧 "print(status, length)"입니다. 도구를 쓸 줄 아는 것과 원리를 아는 것 — 여러분은 이제 둘 다입니다.


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