Auth Mastery Part 2: Sessions, Cookies, and Staying Authenticated
Getting in once is easy. Staying in across ten requests is the skill.Press enter or click to view im 2026-6-2 05:6:49 Author: infosecwriteups.com(查看原文) 阅读量:17 收藏

Getting in once is easy. Staying in across ten requests is the skill.

Roshan Rajbanshi

Press enter or click to view image in full size

Series: curl — The Request Engine You Never Learned Properly Article: 6B of 16

Article 6A got you authenticated. This article keeps you authenticated.

A single authenticated request proves the credentials work. A multi-step attack workflow — login, enumerate, attack, extract — requires that authentication persist across every request. With a browser, this happens automatically. With curl, you manage it yourself.

This article covers cookie jars, session persistence, CSRF token handling, and OAuth2 flows. These are the stateful plumbing skills that every THM/HTB machine with a login page will require from you.

How Sessions Work Over HTTP

HTTP is stateless. Each request is independent — the server has no memory of previous requests by default.

Applications solve this with sessions: after a successful login, the server creates a session record and sends the client a session identifier in a Set-Cookie header. The client sends this identifier back with every subsequent request in the Cookie header. The server looks up the identifier and retrieves the session data.

Without a session cookie, every request you make after login is treated as a fresh, unauthenticated request.

With curl, you are responsible for capturing the session cookie from the login response and sending it with every subsequent request. The cookie jar system automates this.

Cookie Jar Mechanics

Two flags. Get the order right — beginners constantly reverse them.

-c — Save cookies to a file (capture)

curl -c cookies.txt http://target.com/login

-c appends any cookies from the server's Set-Cookie response headers into the file. This is the collection step.

-b — Send cookies from a file (use)

curl -b cookies.txt http://target.com/dashboard

-b reads cookies from the file and sends them in the Cookie header of the outgoing request. This is the authentication step.

The memory rule: -c = collect. -b = bring.

Both together — the login and persist pattern:

# Login: collect the session cookie
curl -s \
-c cookies.txt \
-d "username=admin&password=password" \
http://127.0.0.1:8080/login
# Use the session on the next request
curl -s \
-b cookies.txt \
http://127.0.0.1:8080/dashboard
Login successful. Welcome, admin.
[Dashboard] Authenticated as: admin
Session active. You have access to protected resources.

The first request hit the login endpoint, credentials matched, and the server issued a Set-Cookie header — -c wrote it to cookies.txt. The second request read that file with -b and sent the session token in the Cookie header. The server recognized it and returned authenticated content.

Inspect what is in your cookie jar:

cat cookies.txt
# Netscape HTTP Cookie File
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
127.0.0.1 FALSE / FALSE 0 session d52f6273029c4c769beeda5dfd618d34

Press enter or click to view image in full size

Login with -c captures the session cookie. The dashboard confirms it works. cat cookies.txt shows what libcurl actually stored — domain, flags, expiry, and the token itself in Netscape format.

The cookie jar is plain text in the Netscape cookie format. Each data line is tab-separated with seven fields: domain, include-subdomains flag, path, secure flag, expiry timestamp, cookie name, and cookie value.

In this line: 127.0.0.1 is the domain. FALSE in the second field means the cookie does not apply to subdomains. FALSE in the fourth field means the Secure flag is not set. 0 In the fifth field means no expiry — a session cookie that lives until the server invalidates it or you delete the jar. session is the cookie name. The hex string is the token value the server will validate on every subsequent request.

Reading the jar tells you what session identifiers you have captured. Whether they are still valid, you find out only by using them — the jar itself does not track server-side session state.

Session Persistence Across Multiple Requests

A real attack workflow is not two requests — it is many. You need the session to persist across the entire chain.

Use the same cookie jar file for every request in the workflow:

# 1. Login
curl -s -c jar.txt -d "username=admin&password=pass" http://target.com/login
# 2. Enumerate users (authenticated endpoint)
curl -s -b jar.txt http://target.com/api/users
# 3. Access target resource
curl -s -b jar.txt http://target.com/api/users/5/profile
# 4. Perform action
curl -s -b jar.txt \
-X POST \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}' \
http://target.com/api/users/5/email

The same jar.txt file threads authentication through every step. One login, many requests.

If the session expires or the cookie is rejected, you will often see a redirect to the login page or a 401. When that happens:

  1. Delete the cookie jar: rm jar.txt
  2. Re-run the login request
  3. Continue from where the chain broke

Combining -c and -b for automatic cookie refresh:

curl -c jar.txt -b jar.txt http://127.0.0.1:8080/endpoint
==================================================
curl Lab Echo Server
==================================================
METHOD : GET
PATH : /endpoint
FULL URL : /endpoint
--- REQUEST HEADERS ---
Host: 127.0.0.1:8080
User-Agent: curl/7.68.0
Accept: */*
--- QUERY STRING PARAMS ---
(none)
--- RAW BODY ---
(empty)
--- PARSED BODY PARAMS ---
(none)
==================================================

Using both flags simultaneously tells curl to send existing cookies from the jar and write any new cookies the server sends back. This handles session renewal — if the server rotates the session cookie mid-workflow, the jar is updated automatically. When the jar starts empty (as above), no Cookie header appears in the outgoing request. Once a login populates it, every subsequent combined-flag request both sends and refreshes.

Set-Cookie Attributes and What They Mean for Your Testing

When you inspect responses in verbose mode, you will see cookie attributes alongside the values. These affect both security posture and your testing approach.

curl -v http://target.com/login -d "username=admin&password=pass" 2>&1 | grep "Set-Cookie"
< Set-Cookie: session=d52f6273029c4c769beeda5dfd618d34; Path=/

HttpOnly — The cookie cannot be accessed by JavaScript. This is a defense against XSS-based cookie theft. For your curl testing, it makes no difference — curl sends HTTP requests, not JavaScript. But if you find a stored XSS and the session cookie is HttpOnly, cookie theft via XSS is blocked.

Secure — The cookie is only transmitted over HTTPS connections. If a cookie carries the Secure flag and you are testing over plain HTTP, it will not be sent — a common source of confusion in lab environments. One exception: curl treats http://localhost and http://127.0.0.1 as secure contexts and may still send Secure-flagged cookies there. Do not rely on that behavior when concluding production targets.

SameSite — Controls when the cookie is sent on cross-origin requests. Strict means the cookie is only sent on same-origin requests. Lax allows some cross-origin requests (top-level navigation). None means the cookie is always sent — required for cross-origin use, but note that SameSite=None requires the Secure flag in modern browsers, and it enables CSRF if additional protections are absent.

Get Roshan Rajbanshi’s stories in your inbox

Join Medium for free to get updates from this writer.

Remember me for faster sign in

A cookie without an SameSite attribute is treated as SameSite=Lax In modern browsers, the behavior that shifted in 2020 varies by browser version. Note this when cataloging cookies during recon.

CSRF Token Extraction and Reuse

Many web applications protect state-changing endpoints with CSRF tokens — session-tied values embedded in forms. When you submit a form, the server checks that the CSRF token in the request matches the one it issued. This prevents cross-site request forgery.

For curl-based testing, CSRF tokens are an obstacle: you cannot POST to a protected form endpoint without first fetching the valid token from the form page.

The workflow — use a single jar file throughout:

# Step 1: Authenticate and fetch the form page, extracting the CSRF token
CSRF=$(curl -s -c jar.txt -b jar.txt http://127.0.0.1:8080/settings | \
grep -oP '(?<=name="csrf_token" value=")[^"]*')
echo "CSRF token: $CSRF"
CSRF token: csrf_abc123xyz789_lab

grep -oP uses Perl-compatible regex with a lookbehind to extract the token value. Note that -P (PCRE) requires GNU grep — it is standard on most Linux systems, but may not be available on BSD or macOS without installing grep separately. If grep -oP fails, grep -o 'value="[^"]*"' is a portable fallback that gets you close.

The pattern above assumes the form field looks like:

<input type="hidden" name="csrf_token" value="abc123xyz">

Adjust the field name to match your target. Common names: csrf_token, _token, csrfmiddlewaretoken, authenticity_token. When in doubt, use grep -i csrf on the form HTML to find them.

Step 2: Use the token in your request:

curl -s \
-b jar.txt \
-d "[email protected]&csrf_token=$CSRF" \
http://127.0.0.1:8080/change-email
Email updated successfully.
User: admin
New email: [email protected]

The key insight: the CSRF token must come from the same session. The combined -c jar.txt -b jar.txt In step 1, both send the existing session cookie and capture any renewed cookie the server issues. Step 2 sends that same session back with the extracted token. The server validates that the CSRF token belongs to that session — a token from a different session will be rejected.

Compact one-liner — for reference, not the recommended workflow:

curl -s -c jar.txt \
-d "[email protected]&csrf_token=$(curl -s -c jar.txt -b jar.txt http://target.com/form | grep -oP '(?<=csrf_token" value=")[^"]*')" \
-b jar.txt \
http://target.com/change-email

This is the inline extraction pattern you will see in one-liner exploit scripts. It works, but it is brittle — a form field name change or encoding difference breaks the inner command silently and sends an empty token. Use the two-step version in any workflow you need to debug.

Session Fixation Testing

Session fixation is a vulnerability where an attacker forces a known session ID onto a victim before authentication, and the server preserves that same ID after login. Because the attacker already knows the ID, they can use it to access the now-authenticated session.

Test it with curl:

curl -v \
-b "PHPSESSID=attackercontrolledvalue" \
http://10.48.179.222/cookie.php \
-d "username=admin&password=admin" 2>&1 | grep -E "Set-Cookie|HTTP/"
* using HTTP/1.x
> POST /cookie.php HTTP/1.1
< HTTP/1.1 200 OK

Press enter or click to view image in full size

Session fixation test — a crafted PHPSESSID sent at login, output filtered to show only Set-Cookie and HTTP status lines. No new cookie in the response. The interpretation depends on the target's session mechanism — see the article for what each outcome means.

No Set-Cookie in the response. This particular target does not use PHP sessions — it uses a different session mechanism — so this result is not conclusive for fixation either way.

On a PHP application, the response tells you more. If you see:

< Set-Cookie: PHPSESSID=newrandomvalue; Path=/

The server regenerated the session on login — the crafted value was discarded. No fixation vulnerability.

If you see:

< Set-Cookie: PHPSESSID=attackercontrolledvalue; Path=/

The server kept your value and attached it to the authenticated session. That is session fixation. The real confirmation step is to make an authenticated request using your crafted value and verify it succeeds — a new cookie in the login response is a signal, not the finding itself.

OAuth2 Bearer Token Flow (Surface Level)

OAuth2 is a delegation framework with several grant types. The one you encounter most often in lab environments is the Resource Owner Password Credentials grant — the client sends credentials directly and receives a token. Note that this grant type is discouraged in modern OAuth2 deployments in favor of the Authorization Code flow, but it appears regularly in older APIs and internal tooling.

# Step 1: Request an access token
RESPONSE=$(curl -s \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=password&username=admin&password=password&client_id=myapp" \
http://127.0.0.1:8080/oauth/token)
echo $RESPONSE | python3 -m json.tool
ACCESS_TOKEN=$(echo $RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Token: $ACCESS_TOKEN"
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4iLCJleHAiOjk5OTk5OTk5OTl9.lab_token_demo",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read write"
}
Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4iLCJleHAiOjk5OTk5OTk5OTl9.lab_token_demo

Press enter or click to view image in full size

OAuth2 token request → JSON response → shell variable extraction in three commands. The token is now in $ACCESS_TOKEN and ready to travel in every Authorization: Bearer header for the rest of the session.

The token response gives you the token itself, its type, expiry in seconds, and the scopes it covers. Pipe through python3 -m json.tool to read it cleanly — raw token responses are a single line of JSON.

Step 2: Use the access token:

curl -s \
-H "Authorization: Bearer $ACCESS_TOKEN" \
http://target.com/api/protected-resource

Bearer tokens travel in the Authorization header — no cookie jar needed. Treat them like passwords: anyone holding the token can make authenticated requests as that user until it expires. Store them in a shell variable, not in a file on a shared system.

If a request returns 401 after previously working, the token has expired — the expires_in field in the token response tells you how long it is valid. Request a new one using the same credentials.

For testing purposes: check whether the token endpoint has rate limiting, whether expired tokens are actually rejected (some applications skip expiry validation), and whether the scope field is enforced server-side or just decorative.

The Full Stateful Attack Chain

Pulling it together on a real target. Login, confirm, extract CSRF token, act. This is the skeleton of every multi-step web attack workflow — the specific endpoints change, the pattern does not.

# 1. Login and capture session
curl -s -c jar.txt \
-d "username=admin&password=admin" \
http://10.48.179.222/cookie.php
Login successful. Cookie set.
# 2. Confirm authentication
curl -s -b jar.txt http://10.48.179.222/cookie.php | grep -i "welcome"
Welcome back, admin!
# 3. Fetch CSRF token from settings form
CSRF=$(curl -s -b jar.txt http://127.0.0.1:8080/settings | \
grep -oP '(?<=name="csrf_token" value=")[^"]*')
# 4. Submit action with CSRF token
curl -s -b jar.txt \
-d "csrf_token=$CSRF&[email protected]" \
http://127.0.0.1:8080/change-email
Email updated successfully.
User: admin
New email: [email protected]

The same jar.txt threads through every step. One login, four commands, end-to-end authenticated action.

Press enter or click to view image in full size

Real-target login — -c jar.txt captures the session cookie from the THM machine's response.

Press enter or click to view image in full size

One flag swap, -b instead of -c, and the session travels with the request. The server recognizes it and returns authenticated content.

The two articles on authentication together give you the complete workflow: identify the scheme, authenticate, capture the session, maintain it across requests, and handle CSRF tokens that protect state-changing endpoints. Every login-gated machine on THM/HTB uses some combination of these patterns.

Next: Article 7 — Header Manipulation: Bypasses, Probing, and the Security Audit Nobody Does


文章来源: https://infosecwriteups.com/auth-mastery-part-2-sessions-cookies-and-staying-authenticated-6a0653814a07?source=rss----7b722bfd1b8d---4
如有侵权请联系:admin#unsafe.sh