Step 133. Burp Suite 2: Repeater and Intruder — The Repeat Experiment Bench and the Automatic Machine Gun

Step 133. Burp Suite 2: Repeater and Intruder — The Repeat Experiment Bench and the Automatic Machine Gun

Level 2 — Introduction to Security and Attack Skill Basics | Difficulty ★★★☆☆ | Estimated time: 3 hours

Prerequisites: Step 132 (intercepting with a proxy) and Step 131 (the target web app).

  • What you need: Burp Suite, Step 131’s web app, and a short wordlist (make your own or use Kali’s /usr/share/wordlists). Burp’s GUI screens are marked as screen examples; the principle-verification experiments are measured (2026-09-09, Python requests + curl, against the Step 131 server).
  • ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.

In Step 132 we caught a request and sent it once, modified. But the actual labor of web attacks is repetition — dozens of experiments of inserting a quote, changing it, inserting it again. Burp has two tools for that labor. Repeater is the experiment bench where you fire one request hundreds of times, modified each time, and Intruder is the machine gun that automatically substitutes a dictionary (word list) into a designated spot and fires. Today you take both into your hands.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Send a caught request to Repeater and run repeated experiments
  • Observe differences in server responses (status code, length) while varying parameter values
  • Designate Intruder payload positions (§), load a wordlist, and run it
  • Sort responses by status code and length to find the "different response"
  • Reproduce Repeater’s and Intruder’s principles with a Python loop

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Burp Suite Community, target is the Step 131 web app; principle measurements use Python requests
Today’s tools Burp Repeater (repeat experiments), Burp Intruder (automatic substitution), § payload positions
Concepts needed Fuzzing, payloads, sorting by status code/response length, throttling threads
Today’s artifact A record of discovering one hidden path in my web app + a principle-reproduction script

2-1. Repeater — Hundreds of "What About This Time?"

Web-vulnerability experimentation is a continuous series of "does the server react differently when I change the input a little?" With a browser you’d have to edit the address bar every time, but Repeater pins the raw request in place and resends it with a single Send button — edit, fire, read the response, edit again.

Inserting and removing quotes in the SQL injection chapters (Steps 93, 104), changing parameters in Natas — all of those experiments are Repeater’s usage scenes.

2-2. Intruder — Mark the Spot + Substitute a Dictionary

Intruder’s idea is simple. ① Mark the spots to change in the request with § (section signs), ② give it a payload list (payloads — the values to substitute in turn), ③ it automatically builds and fires as many requests as there are list entries, and ④ it gathers the results into a table.

You can place it on the path itself, like GET /§§ HTTP/1.1, or on a parameter value, like pw=§§. The former becomes hidden-path discovery; the latter becomes a login brute force.

2-3. Fuzzing and Finding the "Different Response"

This kind of automatic substitution is called fuzzing. The key skill is not firing but reading — among dozens of responses, most are the identical 404 page. So you sort the results table by status code and response length. If every 404 is 207 bytes but one is 53 bytes? That one is a hidden door. A fuzzing discovery is always "the one that’s different from the rest."


3. Follow Along

3-1. Sending to Repeater — Putting It on the Experiment Bench

Have the Step 131 server running. In Burp’s HTTP history, find the login POST request, right-click → Send to Repeater. The raw request has been carried over to the Repeater tab.

Screen example (the request on 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

Press Send and the response appears on the right — for our web app, 401 and the failure message. Now change wrong to blue-fox-31 and Send again — 302 and Set-Cookie come back. Edit, fire, compare. Making this cycle faster is Repeater’s reason for existing.

3-2. Measuring Repeater’s Principle — Same Request, Repeat with Only Values Changed

The essence of what Repeater does is a loop. We reproduced that principle with curl exactly, and measured it — the target is the Step 131 server’s /login.

Input

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 (wrong pw) -> $code"
done
curl -s -o /dev/null -w "uid=nadia (correct pw) -> %{http_code}n" -X POST -d "uid=nadia&pw=blue-fox-31" http://127.0.0.1:5000/login

Output (measured 2026-09-09):

uid=nadia (wrong pw) -> 401
uid=guest (wrong pw) -> 401
uid=admin (wrong pw) -> 401
uid=nadia (correct pw) -> 302

How to read it: the four requests share the same structure and differ only in values — and the response status codes split into 401 401 401 … 302. The comparison you were doing with your eyes on the Repeater screen, we did here as a row of numbers. Whatever the tool, the experiment structure of "same request, different values, compare responses" is one.

3-3. Preparing Intruder — Marking the Spot with §

In HTTP history, right-click the GET / HTTP/1.1 request → Send to Intruder. On the Intruder tab’s Positions screen, drag over the path part of the request and press Add §.

Screen example (after designating the position):

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

How to read it: between the § marks is "the spot that will change with every request." Leave the attack type as Sniper (the default), which changes only one position. Next, on the Payloads tab, attach a wordlist — a short homemade list is enough (admin, backup, test, login, secret, config …).

3-4. Running Intruder — The Machine Gun Fires, the Table Collects

Press Start attack (the Community edition has a speed limit — slow is normal) and per-request responses pile up in the results window. Click the Status and Length column headers to sort.

Screen example (the results table):

Payload      Status   Length
backup       200      53     <-- different from the rest!
admin        200      27     <-- this one too!
login        200      271
dashboard    302      268
test         404      276
secret       404      276
config       404      276

How to read it: the 404 rows are identical down to their length — same error page. One sort and two "different responses" float to the top. It means our web app had hidden paths /backup and /admin.

3-5. Measuring Intruder’s Principle — Doing the Same Thing in Python

Intruder’s insides are ultimately a loop too. We substituted the same wordlist with Python requests and measured — compare with Burp’s results.

Input (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}")

Output (measured 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

How to read it: the same conclusion as 3-4’s screen example — all eight 404s are 207 bytes (the same error page), and only /admin, /backup, /login, /dashboard show different faces. With 12 words, our web app’s map has been drawn. allow_redirects=False is an option to see the original status code instead of following a 302 — what Burp shows in its results table is also the original response.

Why: once you understand Intruder not as a "magic tool" but as "a 12-line loop I could write myself," you can perform the same attack even in environments without the tool (exams, restricted labs).

3-6. Throttling and Manners — Even a Lab Server Struggles

Intruder fires hundreds of requests in an instant. Even the small Flask server in my lab struggles under many simultaneous requests, which is why the Community edition has a default speed limit. Two practice tips:

  • Lower the number of simultaneous requests (threads) in Intruder’s Resource pool settings, and the target server gets room to breathe.
  • Keep the server log open alongside — watching dozens of log lines get written per fuzz run makes it visceral that automated attacks are recorded noisily on the server. A fact worth remembering from a detection standpoint.

4. Missions & Exercises

Mission — Discovering Hidden Paths and Reproducing the Principle

  1. Add 2 routes to the Step 131 web app that are linked nowhere (e.g., /backup, /admin — returning a short string is enough).
  2. Run a GET /§§ position fuzz with Burp Intruder and find those two paths using only status-code/length sorting. Record the results table.
  3. Also experience a brute-force-style fuzz by loading a short wordlist into the pw=§§ spot of the login POST — put the correct password in the list and find the row that splits off with a 302.
  4. Write the Python fuzzer from 3-5 yourself, find the same paths, and compare with Burp’s results.
  5. Save the server log from during the fuzz — the goal is to confirm "how an automated attack looks in the logs."
  6. Write repeater-and-intruder.md in your wiki — 3 lines on the difference between the two tools, 3 lines on the knack of "finding the different response."

Exercises

Exercise 1. Explain the difference in purpose between Repeater and Intruder from the perspective of "experiment count and automation."

Exercise 2. In fuzzing results, why do the 404 responses all have the same length, and how is this property used for discovery?

Exercise 3. What kind of attack does placing the payload position on the path (GET /§§) become, and what about on a value (pw=§§)?

Exercise 4. In the 3-5 measurement, what happens to /dashboard‘s result if you drop allow_redirects=False, and why does that confuse the analysis?


5. Model Answers & Completion Criteria

Mission Model Answer

Verification points:

  1. Contrast of discovery: is there a record of the screen where only the rows with lengths different from the 404s were singled out in Intruder’s results table? If, like 3-4’s screen example, backup (53 bytes) and admin (27 bytes) float up from the 404 crowd (all the same length), it’s a success.
  2. Brute-force fuzz: in the pw=§§ fuzz, only the correct-password row is 302 and the rest are 401 — was the same pattern as the 3-2 measurement reproduced?
  3. Principle reproduction: does the Python fuzzer’s output match Burp’s discoveries? (In the 2026-09-09 measurement, both found /admin and /backup.)
  4. Log observation: are as many GET /<word> 404 lines as there are words recorded in time order in the server log — this is the evidence that "attacks are noisy."

Exercise Answers

Answer 1. Repeater is a manual repeat experiment bench where a person edits values one at a time and fires — used for hypothesis testing of "what about this value this time?" Intruder is an automatic substituter that fires everything while the person’s hands are off, given positions (§) and a list — used when there are dozens or hundreds of candidates (path discovery, brute force). The depth of one experiment is Repeater; the breadth of experiments is Intruder.

Answer 2. Because the server returns the same error page for nonexistent paths, the response lengths are identical too (3-5 measurement: all eight 404s are 207 bytes). Thanks to this property, one length sort separates "the crowd of identical ones" from "the different one," and a fuzzing discovery becomes precisely the work of finding that different one.

Answer 3. Placing it on the path (GET /§§) substitutes addresses themselves and becomes hidden path/directory discovery (directory fuzzing); placing it on a value (pw=§§) substitutes different inputs into the same path, becoming a login brute force or parameter discovery. Where you place the spot determines the kind of attack.

Answer 4. Without allow_redirects=False, requests automatically follows the 302 and shows the final response (the login page, 200). The distinction between /dashboard being "302 (chased away because login is required)" and "200 (passed)" disappears, and any analysis reading authentication state collapses. It’s the same logic as Burp’s results table showing the original response — the redirect itself is information.

Completion Criteria Checklist

  • [ ] I can send a request to Repeater and run the edit-and-fire cycle
  • [ ] I can read the server’s verdict from status-code differences (401 vs 302)
  • [ ] I can designate a § position in Intruder, load a wordlist, and run it
  • [ ] I sorted results by status code/length and found the "different responses"
  • [ ] I reproduced the same fuzzing with a Python loop and compared the results
  • [ ] I confirmed how fuzzing appears in the server log
  • [ ] Mission: I completed the 2 hidden-path discovery record and the wiki document

6. Common Pitfalls & Fixes

Wall 1. Intruder results all come out with the same value

Symptom: the payload doesn’t change and the same request repeats.

Cause: there are no § marks, or after clearing (Clear §) the positions Burp auto-designated on the Positions screen, you didn’t designate new ones.

Fix: check the raw request in Positions — the spot to change must be wrapped exactly as §...§. Clear unneeded auto-designated marks with Clear § and Add § only where needed.

Wall 2. I’m on the Community edition and the attack is too slow

Symptom: requests go out at about one per second.

Cause: Burp Community edition’s deliberate speed limit. It’s not broken.

Fix: keep the wordlist short (a few dozen) — in introductory practice, the goal is learning the principle with a short list. When the moment comes that you need a big dictionary, writing it yourself in Python as in 3-5 is faster (in the measurement, it finished 12 paths instantly).

Wall 3. The web app dies or stops responding during the fuzz

Symptom: the server stops responding or throws errors.

Cause: a flood of simultaneous requests overwhelmed the development server (remember Step 94’s warning message).

Fix: lower the Resource pool’s simultaneous-request count to 1–2. If you’re running the Flask development server without --threaded, that’s also a cause — for practice, a slow fuzz is actually safer.

Wall 4. Response lengths differ slightly, making "different response" judgment hard

Symptom: they’re all 404 but lengths differ by a few bytes each.

Cause: the error page contains values that change every time, like the request path or a timestamp.

Fix: use status code as the primary criterion instead of length, and if it’s still ambiguous, open a few response bodies directly and compare. There’s also the method of using Burp’s filter (e.g., contains a search string) to single out "ones without the error phrase."

Wall 5. I can’t tell whether the login brute force succeeded

Symptom: I fired the correct password but it doesn’t stand out in the results table.

Cause: this happens when you don’t know in advance the response differences between success and failure (status code, length, redirect).

Fix: before fuzzing, fire one success and one failure each in Repeater and record the differences first — for our web app, failure was 401 (41 bytes) and success 302 (about 207 bytes) (measured 2026-09-09). You need a baseline to see the deviation.


7. Summary

Today’s Concepts

Concept One-line explanation
Repeater A manual repeat experiment bench for editing and firing one request
Intruder A tool that automatically substitutes a list into § spots and fires
Payload The list of values to substitute
Fuzzing Exploration that shoves in wildly varied inputs and observes reactions
Finding the "different response" The knack of singling out outliers by sorting status codes/lengths
Thread/speed control Restraint for sparing the target server and reading the logs

Today’s Commands & Actions

Command/action What it does
Right-click → Send to Repeater / Intruder Send a caught request to each tool
Repeater Send Resend the same request (with edited values)
Add § / Clear § Designate/remove payload positions
Payloads tab Load the wordlist to substitute
Sort Status/Length columns Single out the "different responses"
requests.get(url, allow_redirects=False) Python fuzzer — see the original status code
curl -w "%{http_code}" Repeat experiments extracting only the status code

An Instinct More Important Than Commands

Repeater and Intruder are the hands and feet of web attacks — almost every web attack ahead (SQLi, XSS, auth bypass) is a variation on these two motions: "confirm the hypothesis with Repeater, sweep up the candidates with Intruder."

And as you reproduced in Python today, these tools’ heart is a single loop. The § marks are just "for word in wordlist," and the results table is just "print(status, length)." Knowing how to use the tool and knowing the principle — you now have both.


Once every box is checked, Step 133 is complete.