Exercises › Skills
curl from Zero to CEH
About 2–3 hours hands-on. Built for CEHv14 students who keep seeing curl in web, API, and cloud modules — and want the flags to stick. Practice on any Linux VM against safe targets only — including this site.
What you will learn
- Use curl as an HTTP client (not a browser) for recon, API calls, and evidence collection.
- Master the flags that show up again and again: methods, headers, cookies, auth, TLS, status codes, pipelines.
- Map CEH-style request patterns to safe twins on httpbin, example.com, and local Python stubs we ship.
Safety rules
- Allowed targets:
https://www.techforge.community(this site — headers/eggs only),https://httpbin.org,https://example.com, and the local helpers under /curl/lab-helpers/ (loopback only). - Never hit real cloud metadata (
169.254.169.254, GCP metadata) from a student home network. Usefake-metadata.pyinstead. - No attacking systems you do not own or that have not invited testing. Course attack practice stays in authorized EC-Council iLabs.
- Practice flags ethically. If a command would change someone else’s system, do not run it outside a lab you control.
Before you start
- Linux VM with
curl(and optionallyjq:sudo apt install jq). - Python 3 for the local stubs.
- Download helpers: fake-metadata.py · mini-ssrf.py · sample.json · sample.txt · README
Quick check: curl -sI https://www.techforge.community | head -15 and curl -sI https://example.com | head -5 should show HTTP headers.
Lesson map
0 Why curl in CEH
A browser renders pages, runs JavaScript, and hides most of the raw HTTP exchange. curl is a command-line HTTP client: you choose the method, headers, body, and cookies, then read exactly what came back. That matches how testers probe APIs, check banners, replay sessions, and capture evidence.
- Browser: great for UI and JS-heavy apps; noisy and hard to automate precisely.
- curl: one request at a time, scriptable, same flags you will paste into notes and reports.
- In class: many CEH slides show
curlagainst a lab target. Here: same flags, safe twins.
1 First requests
Fetch a URL, then add verbosity so you can see the conversation.
# Simplest GET — body only curl https://www.techforge.community/ curl https://example.com # Show response headers + body curl -i https://www.techforge.community/ curl -i https://example.com # Headers only (HEAD-like convenience: often sends HEAD) curl -I https://www.techforge.community/ curl -I https://example.com # Verbose: request + response wire details (great for learning) curl -v https://www.techforge.community/ 2>&1 | head -40 curl -v https://example.com # Silent body (no progress meter) — good for scripts curl -s https://httpbin.org/get # Save to a named file / remote filename curl -o page.html https://www.techforge.community/ curl -O https://httpbin.org/image/png
-I is ideal for quick banner/fingerprint peeks. Watch for a custom X-Tech-Forge header on this site. -v shows TLS and request lines when something feels wrong.
2 Methods & bodies
REST APIs care about the verb and the body. Practice on httpbin, which echoes what you sent.
# Explicit methods
curl -s -X GET https://httpbin.org/get
curl -s -X POST https://httpbin.org/post
curl -s -X PUT https://httpbin.org/put
curl -s -X DELETE https://httpbin.org/delete
# Form-style body (application/x-www-form-urlencoded by default with -d)
curl -s -X POST https://httpbin.org/post \
-d 'username=ava&password=demo-only'
# Safer encoding for special characters
curl -s -X POST https://httpbin.org/post \
--data-urlencode 'note=hello world & friends'
# JSON body — set Content-Type yourself
curl -s -X POST https://httpbin.org/post \
-H 'Content-Type: application/json' \
-d '{"user":"ava","role":"tester"}'3 Headers
Custom headers change how servers treat you: content negotiation, auth, and (in cloud) metadata gate headers. Learn the -H flag here; treat cloud metadata header names as concepts until Lesson 8 + fake-metadata.py.
# User-Agent and Accept curl -s https://httpbin.org/headers \ -H 'User-Agent: TechForge-curl-lab/1.0' \ -H 'Accept: application/json' # Multiple -H flags stack curl -s https://httpbin.org/headers \ -H 'X-Lab: techforge' \ -H 'X-Request-Id: demo-001' # CONCEPT ONLY — cloud metadata gate headers (practice against fake-metadata.py later): # -H 'Metadata-Flavor: Google' # -H 'Metadata: true' # Do NOT send these at real link-local metadata IPs from a home network.
4 Cookies & sessions
Save cookies to a jar (-c), send them later (-b). Same pattern as staying logged in after a POST login.
# Ask httpbin to set a cookie, store it curl -s -c cookies.txt 'https://httpbin.org/cookies/set?session=labdemo' -o /dev/null # Send the jar back curl -s -b cookies.txt https://httpbin.org/cookies # One-off cookie without a jar file curl -s -b 'session=labdemo' https://httpbin.org/cookies # Inspect the jar cat cookies.txt
5 Auth
Basic auth and Bearer tokens are the two shapes you will see most in API slides.
# HTTP Basic — httpbin expects user/pass in the path for a 200 curl -s -u user:pass https://httpbin.org/basic-auth/user/pass # Wrong password → 401 curl -s -i -u user:wrong https://httpbin.org/basic-auth/user/pass | head -5 # Bearer token (any string; httpbin echoes it) curl -s https://httpbin.org/bearer \ -H 'Authorization: Bearer eyJhbGciOiJub25lIn0.lab-only.demo' # Same idea for JWT usage (sending a token) — forging/cracking JWTs stays in authorized labs
6 TLS & quiet failures
Labs sometimes use broken certs; scripts need reliable exit codes and status printing.
# Follow redirects
curl -sI -L https://httpbin.org/redirect/2
# -k / --insecure: skip TLS verify — ONLY on lab hosts you trust, never as a habit on the open internet
# curl -k https://lab-only.example
# Fail on HTTP errors (exit non-zero on 4xx/5xx) — useful in scripts
curl -sf https://httpbin.org/status/404 || echo "request failed as expected"
# Print only the status code
curl -s -o /dev/null -w "%{http_code}\n" https://www.techforge.community/
curl -s -o /dev/null -w "%{http_code}\n" https://example.com
curl -s -o /dev/null -w "%{http_code}\n" https://httpbin.org/status/401-k hides certificate problems. Prefer fixing trust or using a lab CA. Use -k only when the instructor says the lab cert is intentionally broken.
7 Pipelines
Combine silent curl with jq and shell to build tiny recon helpers.
# Pretty JSON
curl -s https://httpbin.org/get | jq .
# Pick one field
curl -s https://httpbin.org/get | jq -r '.headers["User-Agent"]'
# Save body, print status
curl -s -o body.json -w "status=%{http_code}\n" https://httpbin.org/get
jq . body.json
# Status-only probe loop (safe hosts)
for path in /get /status/200 /status/404; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://httpbin.org$path")
echo "$path -> $code"
done8 CEH pattern cookbook
In class, these shapes often hit a lab target. Here you practice the same flags on safe twins so muscle memory transfers.
Banner / fingerprint
In class → lab host. Here → techforge.community / example.com / httpbin.
curl -I https://www.techforge.community/ curl -I https://example.com curl -v https://httpbin.org/get 2>&1 | head -40
Login POST
In class → app login. Here → httpbin echoes your form fields.
curl -s -X POST https://httpbin.org/post \ -d 'username=ava&password=demo-only' | jq '.form'
Cookie session
In class → session after login. Here → httpbin cookie jar.
curl -s -c jar.txt 'https://httpbin.org/cookies/set?sid=abc123' -o /dev/null curl -s -b jar.txt https://httpbin.org/cookies | jq .
Status-code loops
In class → path/status discovery on lab app. Here → httpbin /status.
for c in 200 301 401 403 404 500; do
echo -n "$c -> "
curl -s -o /dev/null -w "%{http_code}\n" "https://httpbin.org/status/$c"
doneBasic auth
In class → protected lab path. Here → httpbin /basic-auth.
curl -s -u user:pass https://httpbin.org/basic-auth/user/pass | jq .
SSRF shape
In class → vulnerable fetch feature. Here → mini-ssrf.py allowlisted to httpbin/localhost only.
# terminal 1 python3 mini-ssrf.py # terminal 2 — allowed curl -s 'http://127.0.0.1:9091/fetch?url=https://httpbin.org/get' | jq . # blocked (not on allowlist) curl -s 'http://127.0.0.1:9091/fetch?url=https://example.com/' | jq .
Bearer / JWT usage
In class → API with Authorization header. Here → httpbin /bearer. (Forging tokens stays in authorized labs.)
curl -s https://httpbin.org/bearer \ -H 'Authorization: Bearer lab-demo-token' | jq .
API JSON
In class → REST JSON APIs. Here → httpbin + jq.
curl -s -H 'Accept: application/json' https://httpbin.org/json | jq '.slideshow.title'
File upload shape
In class → multipart upload. Here → httpbin /post with -F.
curl -s -F '[email protected];type=application/json' \ -F 'note=techforge-lab' \ https://httpbin.org/post | jq '.files, .form'
Cloud metadata shape
In class → IMDS concepts. Here → fake-metadata.py on 127.0.0.1. Never real link-local metadata from student networks.
# terminal 1 python3 fake-metadata.py # terminal 2 — AWS-style paths (stub) curl -s http://127.0.0.1:9090/latest/meta-data/ curl -s http://127.0.0.1:9090/latest/meta-data/instance-id # GCP-style header gate (stub) curl -s -H 'Metadata-Flavor: Google' \ http://127.0.0.1:9090/computeMetadata/v1/instance/id
Health / API probes
In class → MLflow-style or app health endpoints. Here → a tiny local JSON stub + httpbin. Helper also linked as health_stub.py.
# terminal 1 — tiny health stub on :9092 (save as health_stub.py then: python3 health_stub.py)
# health_stub.py contents:
# from http.server import BaseHTTPRequestHandler, HTTPServer
# class H(BaseHTTPRequestHandler):
# def do_GET(self):
# body = b'{"status":"ok","service":"lab-api","version":"0.1"}'
# self.send_response(200)
# self.send_header('Content-Type','application/json')
# self.send_header('Content-Length', str(len(body)))
# self.end_headers(); self.wfile.write(body)
# def log_message(self, *a): pass
# HTTPServer(('127.0.0.1', 9092), H).serve_forever()
# terminal 2
curl -s http://127.0.0.1:9092/ | jq .
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:9092/★ Capstone — mini recon script
Put the flags together: GET/POST, headers, cookie jar, and status codes against localhost + httpbin. Save as mini_recon.sh, chmod +x, run.
#!/usr/bin/env bash
# Tech Forge — curl capstone (safe targets only)
set -euo pipefail
JAR=$(mktemp)
trap 'rm -f "$JAR"' EXIT
echo "== banner =="
curl -sI https://www.techforge.community/ | head -15
curl -sI https://example.com | head -5
echo "== json get =="
curl -s -H 'User-Agent: TechForge-capstone/1.0' https://httpbin.org/get | jq -r '.headers["User-Agent"]'
echo "== login-shaped POST + cookie =="
curl -s -c "$JAR" -X POST https://httpbin.org/post \
-d 'username=ava&password=demo-only' -o /dev/null
curl -s -c "$JAR" -b "$JAR" \
'https://httpbin.org/cookies/set?sid=capstone' -o /dev/null
curl -s -b "$JAR" https://httpbin.org/cookies | jq .
echo "== bearer =="
curl -s https://httpbin.org/bearer -H 'Authorization: Bearer capstone-demo' | jq -r '.authenticated'
echo "== status probe =="
for p in /status/200 /status/401 /status/404; do
printf '%s -> ' "$p"
curl -s -o /dev/null -w "%{http_code}\n" "https://httpbin.org$p"
done
echo "done — flags stick when you can re-run this cold."? Flag cheat sheet
| Flag | Meaning |
|---|---|
-v | Verbose — show request/response details |
-i | Include response headers in output |
-I | Headers only (typically HEAD) |
-s | Silent — no progress meter |
-o file | Write body to file |
-O | Save using remote filename |
-X METHOD | HTTP method (GET/POST/PUT/DELETE…) |
-d / --data | Request body (form by default) |
--data-urlencode | URL-encode body fields |
-H | Add a header |
-c file | Write cookies to jar |
-b file|str | Send cookies from jar or string |
-u user:pass | HTTP Basic auth |
-k | Skip TLS verify (lab-only caution) |
-f | Fail on HTTP errors |
-L | Follow redirects |
-w fmt | Write-out format (e.g. %{http_code}) |
-F | Multipart form / file upload |
-A | User-Agent shortcut |
🥚 Egg hunt — Tech Forge as a safe target
This site (https://www.techforge.community) is hardened for training: allowlisted paths and methods, no outbound fetch, no file uploads, no XSS toys. Hidden responses reward curious curl — odd headers, verbs, Accept values, and paths. Find them yourself; spoilers are folded for instructors.
Hardening reminder
- Eggs are fixed stubs from Pages Functions / static files — not a vulnerable app.
- Do not expect SSRF, reflected XSS, open redirects, TRACE, or real cloud-metadata mimics here.
- Rate limits + Cloudflare WAF apply. Keep practice polite.
Hints only (students)
- Start with headers-only against the homepage. Brand headers sometimes wink back.
- Custom course headers on
/curl/may unlock a wink (and a tiny payload header). - Ask the homepage what methods it Allows — OPTIONS is underrated.
- Check
robots.txt, then whatever path it teases under.well-known/. - Content negotiation: some clients send fancy
Accepttypes and get JSON identity cards. - Announce yourself as
TechForge-Learner/1.0and re-check response headers. - Bring a cookie named like our community mood (
forge=…) and watchSet-Cookie. - There is a training Basic-auth door under
/training/…(lab user, not a real account). - Bearer shape lives nearby — public training token, no sessions.
- One status path is intentionally a teapot. Practice
-w "%{http_code}". - POST echo accepts only an allowlisted form key and caps body size.
- PUT can return success-with-no-body without storing anything.
- Path tricks with
..should not escape the egg you already found. - Robots point the curious; humans follow.
- One manifesto hides a base64 path clue for a later egg.
- Fake “AI OSINT” exists — look for an
X-AI-*assist header. Outputs are canned; fieldmodeladmits it. - Ask for markdown with a junior-analyst role header; spot two planted mistakes in the report.
When you find one, screenshot the headers/body for your notes — never attack beyond these allowlisted toys.
Instructor spoilers — eggs 1–17 (do not read if you are hunting)
curl -I https://www.techforge.community/→X-Tech-Forge: stay-curiouscurl -sI -H 'X-Course: curl-zero-to-ceh' https://www.techforge.community/curl/→X-Tech-Forge-Wink,X-Egg-Payload(base64 JSON)curl -i -X OPTIONS https://www.techforge.community/→Allow+X-Tech-Forge-Commentcurl -s https://www.techforge.community/.well-known/techforge.txtcurl -sH 'Accept: application/vnd.techforge.v1+json' https://www.techforge.community/curl -sI -A 'TechForge-Learner/1.0' https://www.techforge.community/→X-Welcome-Learnercurl -sI -b 'forge=curious' https://www.techforge.community/→Set-Cookie+X-Forge-Tipcurl -su learner:forge https://www.techforge.community/training/basiccurl -sH 'Authorization: Bearer training-token' https://www.techforge.community/training/bearercurl -s -o /dev/null -w '%{http_code}\n' https://www.techforge.community/training/status/418→ 418curl -s -X POST -d 'ping=pong' https://www.techforge.community/training/echo(onlypingechoed; 1 KB cap)curl -s -o /dev/null -w '%{http_code}\n' -T /dev/null https://www.techforge.community/training/put-ok→ 204curl -s https://www.techforge.community/curl/../.well-known/techforge.txt— same manifesto; no escapecurl -s https://www.techforge.community/robots.txtpoints at.well-known/techforge.txt- Manifesto line is base64 for
/training/ai-osint(echo L3RyYWluaW5nL2FpLW9zaW50 | base64 -d) curl -sH 'X-AI-Assist: osint' https://www.techforge.community/training/ai-osint— Nordlicht Moden GmbH fiction;"model":"none-static-stub"curl -sH 'Accept: text/markdown' -H 'X-Role: junior-analyst' https://www.techforge.community/training/ai-osint/report— mistakes: AS15169 on TEST-NET; private10.255.255.1as “public edge”
Lab helpers
- fake-metadata.py — local cloud-metadata path stub (127.0.0.1:9090)
- mini-ssrf.py — allowlisted
/fetch?url=(127.0.0.1:9091) - health_stub.py — tiny JSON health endpoint (127.0.0.1:9092)
- sample.json · sample.txt — upload practice files
- README — how to run
Live course URL: https://www.techforge.community/curl/
