โฑ๏ธ 60 Mins Intermediate

Lab 02: Web App Basics & Burp Suite

Today you put Burp Suite between your browser and OWASP Juice Shop, then catch, read, edit, and resend real HTTP traffic. You'll do the key moves three ways (DevTools, Burp, and curl) so you know which tool to grab when.

๐Ÿ‘ฏ Pair up. One person types, the other reads the steps out loud. Swap every 15 min.

0. Skip & Navigate: Where Are We in the Kill Chain?

๐Ÿงญ
We're jumping straight to Phase 3 (Exploitation)

Recon and scanning (Phases 1โ€“2) come in Weeks 6โ€“7. We're skipping ahead so you're poking a live web app within 10 minutes instead of reading about one. Recon will make a lot more sense once you've seen what a target actually looks like up close.

PIPELINE: You Are Here (Phase 3 of 5) Web Exploitation Track
PHASE 01 ๐Ÿ” Recon (Week 6) PHASE 02 ๐Ÿ“ก Scanning (Week 7) PHASE 03 ยท TODAY ๐Ÿ’ฅ Exploitation Burp + Juice Shop PHASE 04 ๐ŸŽฏ Post-Exploit (Week 12) PHASE 05 ๐Ÿ“ Reporting (Week 14)

1. HTTP Anatomy in 30 Seconds

Most web attacks come down to one thing: sending a request the developer didn't expect. You send method + URL/headers + body. The server answers with status + headers + body. Everything today is about messing with those pieces.

๐Ÿ“ค REQUEST (Kali โ†’ Target)

CLIENT-SENT
  • ๐Ÿ”น Method: GET / POST / PUT / DELETE
  • ๐Ÿ”น Path + query: /rest/products/search?q=apple
  • ๐Ÿ”น Headers: Host, Cookie, Authorization, User-Agent
  • ๐Ÿ”น Body (POST/PUT): form or JSON payload
  • ๐Ÿ”น You control every byte of this. The server can't tell if your browser wrote it or you did.

๐Ÿ“ฅ RESPONSE (Target โ†’ Kali)

SERVER-SENT
  • ๐Ÿ”น Status: 200 OK, 302 redirect, 401/403, 500
  • ๐Ÿ”น Headers: Set-Cookie, Server, X-Powered-By
  • ๐Ÿ”น Body: HTML, JSON, JS, imagesโ€ฆ
200 OKSuccess
302 RedirectLogin flow
401 AuthNeeds login
403 DeniedForbidden / WAF
404 MissingNot found
500 ErrorBackend crash
๐Ÿ–๏ธ DO #1 ยท Match the parts on the wire 2 min

Run curl -v https://httpbin.org/get. Lines starting with > are what you sent. Lines starting with < are what came back. Find the method, the Host header, the status line, and the Server header, and point at each one on your partner's screen.

bash ยท verbose HTTP
curl -v https://httpbin.org/get

2. DevTools vs. Burp vs. curl: Which One When?

All three can send the same request. They're just good at different things. Click a row to see how each one does it.

Task DevTools Burp Suite curl
Inspect a request that already fired
โœ“
โœ“
โš 
DevToolsF12 โ†’ Network โ†’ click the request โ†’ Headers / Payload / Response tabs.
BurpProxy โ†’ HTTP History โ†’ click any row โ†’ Request / Response panes side-by-side.
curlOnly inspects what you send: curl -v <url>. Can't retro-inspect the browser.
Edit a request before the server sees it
โœ—
โœ“
โš 
DevToolsCan't pause a request mid-flight. Firefox's "Edit and Resend" fires a new request, and the original already left.
BurpProxy โ†’ Intercept ON โ†’ request is held โ†’ edit any byte โ†’ Forward. This is the main reason Burp exists.
curlOnly if you write the request yourself. It can't touch what the browser sends.
Replay a single request 10 times
โš 
โœ“
โœ“
DevToolsRight-click โ†’ Replay XHR. Fine twice. By the fifth time you'll want to throw your laptop, and there's no history of what you changed.
BurpRepeater (Ctrl+R). Edit, click Send, compare responses. Built for this.
curlfor i in {1..10}; do curl -s <url>; done. One line, done.
Fuzz one parameter with a 10k wordlist
โœ—
โœ“
โš 
DevToolsNot built for this. If you're scripting the browser to do it, you should be using a real fuzzer.
BurpIntruder: mark where the payload goes, load a wordlist, click Start Attack. The free Community edition is slow on purpose, but you still learn the workflow.
curlScript it, or use ffuf / wfuzz, which are basically curl built for fuzzing.
See & manipulate cookies
โœ“
โœ“
โœ“
DevToolsApplication โ†’ Storage โ†’ Cookies. Edit values live; page picks them up on next request.
BurpEvery intercepted request shows the Cookie header. Edit it right there in the raw pane.
curlcurl -H "Cookie: session=abc123" <url>. Full control, zero UI.
Proxy a mobile app / non-browser client
โœ—
โœ“
โš 
DevToolsOnly sees traffic from its own browser tab. It can't see iOS apps, Postman, IoT devices, or anything else.
BurpPoint the device's proxy at your Burp IP:8080 and install Burp's CA on it. Now you see everything the app sends. Mobile and API pentests mostly look like this.
curlOnly if you can talk the app into using it (env vars, LD_PRELOAD). Usually you can't.

2.5 The Three Ways Principle

You'll do the same tamper three ways today: change ?q=apple to ?q=banana on Juice Shop. The server can't tell which tool you used.

One attack ยท Three tools ยท Same wire
DevTools Copy as fetch โ†’ edit โ†’ Console โ†’ paste
Burp Proxy Intercept ON โ†’ edit q= โ†’ Forward
curl curl 'http://localhost:3000/rest/products/search?q=banana'
๐Ÿ’ก
Why do the same thing three times?

Real targets break your favorite tool all the time. A WAF blocks curl's user agent, or the target is a mobile app and DevTools is useless. When that happens you want a backup you've already practiced, not one you're googling during the engagement.

3. Target: OWASP Juice Shop (Docker)

๐Ÿงƒ

OWASP Juice Shop

Node ยท Angular SPA ยท runs offline after first pull
๐ŸŽฏ Launch Target
๐Ÿ–๏ธ DO #2 ยท Pull & run
bash ยท start Juice Shop
docker run --rm -d --name juice -p 3000:3000 bkimminich/juice-shop
Open in browser http://localhost:3000
Verify container
docker ps --filter name=juice should list a running container on port 3000

๐Ÿ”ฌ Live Target Verification

Don't just trust the checklist. Click each probe. If one fails, the likely fixes are right under it. (One catch: if Burp's Intercept is ON, it holds these requests and the probe will time out. Turn Intercept OFF before probing.)

1. Juice Shop reachable

IDLE
GET http://localhost:3000
If this fails, check:
  • Container not running: docker ps | grep juice
  • Port collision: something else is bound to 3000
  • Docker Desktop paused (macOS/Windows)

2. Burp Proxy listening

IDLE
GET http://127.0.0.1:8080
If this fails, check:
  • Burp not launched โ†’ open Burp Suite Community
  • Proxy listener disabled: Proxy โ†’ Proxy settings โ†’ Proxy listeners โ†’ tick 127.0.0.1:8080
  • Firewall blocking loopback (rare)

3. Juice Shop API answers

IDLE
GET /rest/products/search?q=test
If this fails, check:
  • Container is still booting. Wait 10s and try again
  • Wrong image (a fork). Use bkimminich/juice-shop
  • Container crashed: docker logs juice

4. Point a Browser Through Burp

For Burp to catch traffic, the browser has to send it through Burp first. There are two ways to do that. Use the fast one today.

โšก
Fast path: use Burp's own browser (recommended)

Burp ships with a Chromium browser that's already pointed at the proxy and already trusts Burp's certificate. Zero setup, nothing to break.

In Burp: Proxy โ†’ Intercept โ†’ Open Browser. A browser window opens. Type localhost:3000 in it. Everything you do in that window flows through Burp. That's the whole setup.

Slow path: wiring up your own Firefox (worth doing once)

The built-in browser hides what's actually happening. Do this manual setup once so you understand it, because on a real engagement you'll often need to proxy a browser (or app) Burp didn't launch.

Proxy Target Intruder Repeater
Burp Suite Community
STEP 1 ยท Burp listener

Proxy โ†’ Proxy settings โ†’ Proxy listeners. You should see one entry, 127.0.0.1:8080, with "Running" ticked. (Older Burp calls this Proxy โ†’ Options. They rename it every so often to keep you humble.)

STEP 2 ยท Firefox proxy

Firefox Settings โ†’ Network Settings โ†’ Manual proxy: HTTP proxy 127.0.0.1, port 8080, tick "Also use this proxy for HTTPS". Then open about:config and set network.proxy.allow_hijacking_localhost to true. Otherwise Firefox skips the proxy for localhost and Burp sees nothing. (This trips up almost everyone.)

STEP 3 ยท Burp CA cert

Visit http://burp โ†’ CA Certificate โ†’ download โ†’ Firefox Settings โ†’ Certificates โ†’ Import โ†’ trust for websites. Juice Shop is plain HTTP so you won't need this today, but PortSwigger labs are HTTPS.

STEP 4 ยท Intercept OFF

Proxy โ†’ Intercept tab. Turn intercept off for now so pages load normally while we set scope.

๐Ÿค” You browse to localhost:3000 with Intercept OFF. Predict: does Burp still see the traffic?
๐Ÿ–๏ธ DO #3 ยท First proxied browse 3 min
  1. Open Burp's browser: Proxy โ†’ Intercept โ†’ Open Browser. (Or use your wired-up Firefox if you did the slow path.)
  2. Visit http://localhost:3000. The page should load normally.
  3. Switch back to Burp โ†’ Proxy โ†’ HTTP History.
  4. You should see 20โ€“40 rows (page assets and API calls). Click any row and read the raw request and response.

5. Scope: Filter the Noise

A modern web app fires 100+ requests to fonts, analytics, and CDNs. Scope tells Burp to only care about your target, so History stops looking like a slot machine.

FUNNEL: All Traffic โ†’ Scoped Only Signal to Noise
google-analytics.com noise ยท not our target localhost:3000 โœ“ our Juice Shop fonts.gstatic.com noise ยท CDN ๐ŸŽฏ SCOPE FILTER Target โ†’ Scope โ†’ include host CLEAN HISTORY Just what matters
๐Ÿ–๏ธ DO #4 ยท Add Juice Shop to scope 4 min
  1. Target โ†’ Site map. Right-click http://localhost:3000 โ†’ Add to scope.
  2. Answer Yes when Burp asks "stop logging out-of-scope items?"
  3. Switch to Proxy โ†’ HTTP History. Tick the "Filter" bar โ†’ "Show only in-scope items".
  4. Refresh localhost:3000. History now shows only your target.
Why "stop logging out-of-scope" matters in a real engagement

In a paid pentest, the contract says exactly what you're allowed to touch. If Burp logs requests to a domain you weren't hired to test, that log is evidence you went where you shouldn't. Scope is how you stay on the right side of that line (and out of an awkward meeting with a lawyer).

6. Recon: Get to Know the Target

Before you attack anything, you use it. Click through Juice Shop like a bored customer. You're behind Burp, so every click is quietly logged. Browsing the app is recon.

๐Ÿ–๏ธ DO #5 ยท The 3-minute tour 3 min

Don't hack anything yet. Just poke every corner of the app. The more features you touch, the more of the attack surface lands in Burp for the next step.

  1. Browse the shop, open a product, read its reviews.
  2. Register an account, then log in.
  3. Drop something in the basket.
  4. Open the Customer Feedback form.
  5. Switch the language from the top menu.

๐Ÿšถ Walk the app

USE EVERY FEATURE
  • ๐Ÿ”น Every feature you click shows up in Site map and HTTP History.
  • ๐Ÿ”น You learn what the app does: its forms, its logins, its API calls.
  • ๐Ÿ”น Zero skill required. This is just using the website.

๐Ÿ“– Read what it ships

DIG IN THE TRAFFIC
  • ๐Ÿ”น The JS bundle (main.js) and API responses reveal routes you never clicked.
  • ๐Ÿ”น Endpoints often leak version numbers, config, and internal fields.
  • ๐Ÿ”น This is where you find the stuff the UI tries to hide.
๐Ÿ–๏ธ DO #6 ยท Read your own trail in Burp 5 min
  1. Target โ†’ Site map. Expand localhost:3000. This is the tree of everything you touched on the tour.
  2. Proxy โ†’ HTTP History. Scan the URL column for API calls: /rest/* and /api/*. Those are the app's real moving parts.
  3. Find GET /rest/admin/application-version. It hands you the exact Juice Shop version. That's version disclosure, and it's a finding: an attacker looks up known CVEs for that version.
  4. Find GET /rest/admin/application-config. Click it. The response is probably 304 Not Modified with an empty body, because your browser cached it. We'll force a fresh copy in Repeater shortly. For now, just log it.
Why 304 and no body? (you'll hit this again in Repeater)

Your browser already fetched that file once, so it re-asks with an If-None-Match header carrying the cached ETag. The server checks, sees nothing changed, and replies 304 Not Modified with no body to save bandwidth. Burp shows the honest wire truth: an empty 304. The browser secretly fills in the cached copy, which is why the page still looks fine. To read the real body, you strip If-None-Match and the server has to send the whole thing again. That's the first thing you'll do in Repeater.

๐Ÿ““ FINDINGS LOG write it down as you go
What you foundWhere it showed upWhy it matters
/rest/products/search?q=the search baryour text reaches a backend query (we weaponize this next week)
/rest/admin/application-versionHTTP Historyexact version leaks โ†’ search public CVEs for it
/rest/admin/application-configHTTP Historybackend config metadata โ†’ force a 200 in Repeater to read it
/rest/user/loginthe login pageauth endpoint, JSON POST (we tamper it in ยง10)
main.jsevery page loadships every client-side route, including hidden ones

This little table is the seed of every pentest report you'll ever write. Findings are the deliverable, not the exploits.

๐ŸŽจ
Pro move: color-code your History

Right-click any HTTP History row โ†’ Highlight. Pick a convention and stick to it: red for a confirmed finding, yellow for endpoints tied to it, blue for "interesting, come back later." And name your Repeater tabs instead of leaving them "1, 2, 3". On a multi-day test with 4,000 requests, this is the difference between finding your evidence and rediscovering it.

๐Ÿค” You never clicked an admin page anywhere in the app. So how could you still find one?

7. Intercept a GET Request

Now turn Intercept ON. Every request stops at Burp and waits for you to click Forward, like a very patient bouncer. This is where you get to edit traffic in flight.

SEQUENCE: Browser โ†’ Burp (PAUSED) โ†’ Server Intercept ON
๐ŸŒ BROWSER ๐ŸŸ  BURP (paused) ๐ŸŽฏ JUICE SHOP 1. GET /rest/products/search?q=apple 2. โธ HELD: edit q= 3. Forward (tampered q=) 4. 200 OK ยท JSON products list
๐Ÿ–๏ธ DO #7 ยท Catch your first request 4 min
  1. Burp โ†’ Proxy โ†’ Intercept โ†’ click "Intercept is off" to toggle ON.
  2. In the browser, use the Juice Shop search bar: type apple and hit Enter.
  3. Burp holds the request. Read the raw HTTP: the method line, Host, the other headers, and an empty body (it's a GET).
  4. Click Forward. The response comes back and the page finishes loading.
Nothing captured? 3 quick fixes.
  1. Is Firefox actually pointing at 127.0.0.1:8080? And did you set allow_hijacking_localhost? That one gets almost everyone.
  2. The browser may be serving from cache. Hard-refresh with Ctrl+Shift+R.
  3. You opened the Juice Shop tab before setting the proxy. Close it and open a new one.

8. Tamper the GET, Then Forward

You caught the search request. Now change it before the server sees it: swap q=apple for a payload and see what Juice Shop's backend gives away.

๐Ÿค” Predict: what happens when we send q=';-- to Juice Shop's product search?
๐Ÿ–๏ธ DO #8 ยท Live tamper 5 min
  1. Intercept ON. Trigger a new search from Juice Shop (any word).
  2. In the intercepted request, find ?q=<yourword>. Change the value to %27%3B-- (that's ';-- URL-encoded).
  3. Click Forward. Watch the browser render the response.
  4. Open Proxy โ†’ HTTP History โ†’ click the last request โ†’ Response tab. Look for the database error.
Why URL-encode the payload?

Characters like ;, ', and space break URL parsing. %27 is ' and %3B is ;. Burp can do this for you (right-click โ†’ Convert selection โ†’ URL โ†’ URL-encode key characters), but you'll see %27 so often in the next few labs that it's worth knowing by sight.

9. Send to Repeater: Try Again Without Reloading

Intercept gives you one shot per request. Repeater lets you keep the request and fire it as many times as you like: edit, send, compare. You'll basically live here during the SQLi lab.

Burp Suite Community ยท Simulated
Intercept OFF HTTP History ยท in-scope only ยท Filter โœ“
#MethodURLStatusLenType
17GET /rest/products/search?q=apple 200412JSON
18POST /rest/user/login 40126HTML
19GET /rest/products/search?q=%27%3B-- 5002.1kHTML
Request
Click a row above to load a requestโ€ฆ
Response
โ€ฆand its response.
๐Ÿ’ก Row selected. Click Send to Repeater above to switch tabs and iterate on it.
Request #1 Edit the request โ†’ click Send โ†’ compare response
Request (editable)
Send a request from Proxy first (click a history row โ†’ Send to Repeater).
Response
Response will render here after you click Send.
Intruder (wordlist fuzzing) shows up in Lab 03 with SQLi automation.
Target โ†’ Site map tree. You've been using this in ยง5 to add scope.
๐Ÿ–๏ธ DO #9 ยท Iterate in Repeater 6 min

Part A: the search. HTTP History โ†’ right-click the ?q=apple row โ†’ Send to Repeater. Send each payload, note the status and length.

Payloads to send
Baseline q=apple
SQL sink probe q=%27%3B--

Part B: cash in the recon finding. Remember the application-config row that came back 304 with no body? Send that row to Repeater, then in the request panel delete the If-None-Match line and click Send. With no cached ETag to match, the server can't answer 304, so you get a fresh 200 OK with the full config body. Read it.

This is the everyday Repeater loop: keep one request, change one thing, send, compare.

10. Tamper a POST: The Login Form

GET puts data in the URL. POST puts it in the body. Juice Shop's login is a JSON POST, so it's the same tampering, just in a different spot.

Form-encoded POST

Classic HTML form
  • ๐Ÿ“ฎ Content-Type: application/x-www-form-urlencoded
  • ๐Ÿ“ Body: email=a%40b.com&password=xyz
  • ๐Ÿ” Curl: --data-raw 'email=a&password=b'

JSON POST

SPA / API
  • ๐Ÿ“ฎ Content-Type: application/json
  • ๐Ÿ“ Body: {"email":"a@b.com","password":"xyz"}
  • ๐Ÿ” Curl: -H "Content-Type: application/json" --data '{โ€ฆ}'
๐Ÿ–๏ธ DO #10 ยท Intercept & edit the login POST 6 min
  1. Juice Shop โ†’ top-right Account โ†’ Login.
  2. Intercept ON. Enter test@juice-sh.op + any password โ†’ click Log in.
  3. Burp holds a POST /rest/user/login with a JSON body. Change the password to whatever you like and Forward.
  4. Response is 401 with Invalid email or password. Now send that request to Repeater and change email to ' or 1=1-- (in JSON: "email":"' or 1=1--"). This time you get a 200 and a real login token. You just walked in as the first user in the table without knowing a password.
๐Ÿ”
Wait, how did that work?

Your fake "email" wasn't an email, it was a snippet of database logic that tricked the login check into matching every user and skipping the password. That's SQL injection, and it's the whole of next week. Today the point is smaller and just as important: Burp let you put that snippet on the wire, and the server ran it because it trusted whatever arrived. Don't worry about the syntax yet. We build it from zero in Lab 03.

bash ยท replay login via curl through Burp
curl -x http://127.0.0.1:8080 \
  -X POST http://localhost:3000/rest/user/login \
  -H "Content-Type: application/json" \
  --data '{"email":"test@juice-sh.op","password":"tampered"}'

-x sends curl through Burp, so this request shows up in HTTP History too. That's tool number three.

11. Take-Home: PortSwigger Web Security Academy

๐ŸŽ“

PortSwigger Web Security Academy

Hosted ยท free account required ยท works everywhere
๐ŸŒ Hosted Lab
๐Ÿ–๏ธ DO #11 ยท At-home lab (15 min)

Pick one and solve it in Burp before next Saturday.

SQLi: Login bypass (today's bug, hosted) SQLi: Retrieve hidden data Auth: Username enumeration

12. Juice Shop Score Board (Optional Rabbit Hole)

๐Ÿ†
The Score Board is hidden on purpose

Juice Shop hides its Score Board, and finding it is the first challenge. We're not giving you the route. Hint: the app is a single-page Angular app, so every route it can reach is defined in its JavaScript bundle, and Burp's HTTP History already has that file. Go read it.

โ†’ Your progress is saved per browser. Good low-pressure practice between sessions.

Lab 02 Self-Assessment ยท 8 questions

๐Ÿ–๏ธ DO #12
Q1: Burp's "Intercept is off" toggle. What is Burp still doing?
Q2 [Which tool?]: You need to fuzz the id parameter of /user?id=1 with 5,000 numeric values and diff response lengths. Best tool?
Q3 [Spot the difference]: Which line has been tampered by Burp compared to the browser's original?
Original (browser)
GET /rest/products/search?q=apple HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0
Cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoidGVzdCJ9
Accept: application/json
Forwarded (Burp)
GET /rest/products/search?q=apple HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0
Cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ
Accept: application/json
Q4: In a paid pentest, why is Burp's Target โ†’ Scope more than a UI convenience?
Q5 [Terminal]: Write a curl command that pipes a GET to http://localhost:3000 through Burp on 127.0.0.1:8080.
Q6 [Terminal]: Write the docker run command to start Juice Shop, publishing container port 3000 to host port 3000.
Q7 [Which tool?]: You want to prove a suspected auth bug by sending the exact same login POST 4 times, each with a slightly different password, and compare the 4 responses side-by-side. Best tool?
Q8: A pentester asks you to change one header before it hits an iOS app's backend. Neither DevTools nor curl helps you here. Why does Burp?