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?
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.
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:
200OK,302redirect,401/403,500 - Headers:
Set-Cookie,Server,X-Powered-By - Body: HTML, JSON, JS, imagesโฆ
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.
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.
curl -v <url>. Can't retro-inspect the browser.
for i in {1..10}; do curl -s <url>; done. One line, done.
ffuf / wfuzz, which are basically curl built for fuzzing.
Cookie header. Edit it right there in the raw pane.
curl -H "Cookie: session=abc123" <url>. Full control, zero UI.
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.
Copy as fetch โ edit โ Console โ paste
Intercept ON โ edit q= โ Forward
curl 'http://localhost:3000/rest/products/search?q=banana'
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
docker run --rm -d --name juice -p 3000:3000 bkimminich/juice-shop
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- 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- 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- 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.
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 โ 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.)
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.)
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.
Proxy โ Intercept tab. Turn intercept off for now so pages load normally while we set scope.
localhost:3000 with Intercept OFF. Predict: does Burp still see the traffic?- Open Burp's browser: Proxy โ Intercept โ Open Browser. (Or use your wired-up Firefox if you did the slow path.)
- Visit
http://localhost:3000. The page should load normally. - Switch back to Burp โ Proxy โ HTTP History.
- 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.
- Target โ Site map. Right-click
http://localhost:3000โ Add to scope. - Answer Yes when Burp asks "stop logging out-of-scope items?"
- Switch to Proxy โ HTTP History. Tick the "Filter" bar โ "Show only in-scope items".
- 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.
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.
- Browse the shop, open a product, read its reviews.
- Register an account, then log in.
- Drop something in the basket.
- Open the Customer Feedback form.
- 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.
- Target โ Site map. Expand
localhost:3000. This is the tree of everything you touched on the tour. - Proxy โ HTTP History. Scan the URL column for API calls:
/rest/*and/api/*. Those are the app's real moving parts. - 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. - Find
GET /rest/admin/application-config. Click it. The response is probably304 Not Modifiedwith 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.
| What you found | Where it showed up | Why it matters |
|---|---|---|
/rest/products/search?q= | the search bar | your text reaches a backend query (we weaponize this next week) |
/rest/admin/application-version | HTTP History | exact version leaks โ search public CVEs for it |
/rest/admin/application-config | HTTP History | backend config metadata โ force a 200 in Repeater to read it |
/rest/user/login | the login page | auth endpoint, JSON POST (we tamper it in ยง10) |
main.js | every page load | ships 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.
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.
main.js, which is sitting in your HTTP History right now. Read that file and you find pages the menu never links to. That's exactly how you'll crack the hidden Score Board later.
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.
- Burp โ Proxy โ Intercept โ click "Intercept is off" to toggle ON.
- In the browser, use the Juice Shop search bar: type
appleand hit Enter. - Burp holds the request. Read the raw HTTP: the method line,
Host, the other headers, and an empty body (it's a GET). - Click Forward. The response comes back and the page finishes loading.
Nothing captured? 3 quick fixes.
- Is Firefox actually pointing at
127.0.0.1:8080? And did you setallow_hijacking_localhost? That one gets almost everyone. - The browser may be serving from cache. Hard-refresh with Ctrl+Shift+R.
- 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.
q=';-- to Juice Shop's product search?500 and the page title reads Error: SQLITE_ERROR: near ";": syntax error. It doesn't hand you the full query (the stack trace is empty), but that one line is already a finding: it confirms your input reached a SQLite database and broke the query. Log it. We don't exploit it today, that's all of next week. Right now you're just proving the input reaches the database.
- Intercept ON. Trigger a new search from Juice Shop (any word).
- In the intercepted request, find
?q=<yourword>. Change the value to%27%3B--(that's';--URL-encoded). - Click Forward. Watch the browser render the response.
- 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.
Click a row above to load a requestโฆ
โฆand its response.
Send a request from Proxy first (click a history row โ Send to Repeater).
Response will render here after you click Send.
Part A: the search. HTTP History โ right-click the ?q=apple row โ Send to Repeater. Send each payload, note the status and length.
q=apple
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 '{โฆ}'
- Juice Shop โ top-right Account โ Login.
- Intercept ON. Enter
test@juice-sh.op+ any password โ click Log in. - Burp holds a
POST /rest/user/loginwith a JSON body. Change thepasswordto whatever you like and Forward. - Response is
401withInvalid email or password.Now send that request to Repeater and changeemailto' or 1=1--(in JSON:"email":"' or 1=1--"). This time you get a200and a real login token. You just walked in as the first user in the table without knowing a password.
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.
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
Pick one and solve it in Burp before next Saturday.
SQLi: Login bypass (today's bug, hosted) SQLi: Retrieve hidden data Auth: Username enumeration12. Juice Shop Score Board (Optional Rabbit Hole)
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 #12id parameter of /user?id=1 with 5,000 numeric values and diff response lengths. Best tool?
GET /rest/products/search?q=apple HTTP/1.1 Host: localhost:3000 User-Agent: Mozilla/5.0 Cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoidGVzdCJ9 Accept: application/json
GET /rest/products/search?q=apple HTTP/1.1 Host: localhost:3000 User-Agent: Mozilla/5.0 Cookie: token=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ Accept: application/json
curl command that pipes a GET to http://localhost:3000 through Burp on 127.0.0.1:8080.
docker run command to start Juice Shop, publishing container port 3000 to host port 3000.
password, and compare the 4 responses side-by-side. Best tool?