Tech Forge

Exercises › Skills

QR code linking to techforge.community/curl/

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

Safety rules

Before you start

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.

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"
done

8 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"
done

Basic 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

FlagMeaning
-vVerbose — show request/response details
-iInclude response headers in output
-IHeaders only (typically HEAD)
-sSilent — no progress meter
-o fileWrite body to file
-OSave using remote filename
-X METHODHTTP method (GET/POST/PUT/DELETE…)
-d / --dataRequest body (form by default)
--data-urlencodeURL-encode body fields
-HAdd a header
-c fileWrite cookies to jar
-b file|strSend cookies from jar or string
-u user:passHTTP Basic auth
-kSkip TLS verify (lab-only caution)
-fFail on HTTP errors
-LFollow redirects
-w fmtWrite-out format (e.g. %{http_code})
-FMultipart form / file upload
-AUser-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

Hints only (students)

  1. Start with headers-only against the homepage. Brand headers sometimes wink back.
  2. Custom course headers on /curl/ may unlock a wink (and a tiny payload header).
  3. Ask the homepage what methods it Allows — OPTIONS is underrated.
  4. Check robots.txt, then whatever path it teases under .well-known/.
  5. Content negotiation: some clients send fancy Accept types and get JSON identity cards.
  6. Announce yourself as TechForge-Learner/1.0 and re-check response headers.
  7. Bring a cookie named like our community mood (forge=…) and watch Set-Cookie.
  8. There is a training Basic-auth door under /training/… (lab user, not a real account).
  9. Bearer shape lives nearby — public training token, no sessions.
  10. One status path is intentionally a teapot. Practice -w "%{http_code}".
  11. POST echo accepts only an allowlisted form key and caps body size.
  12. PUT can return success-with-no-body without storing anything.
  13. Path tricks with .. should not escape the egg you already found.
  14. Robots point the curious; humans follow.
  15. One manifesto hides a base64 path clue for a later egg.
  16. Fake “AI OSINT” exists — look for an X-AI-* assist header. Outputs are canned; field model admits it.
  17. 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)
  1. curl -I https://www.techforge.community/ → X-Tech-Forge: stay-curious
  2. curl -sI -H 'X-Course: curl-zero-to-ceh' https://www.techforge.community/curl/ → X-Tech-Forge-Wink, X-Egg-Payload (base64 JSON)
  3. curl -i -X OPTIONS https://www.techforge.community/ → Allow + X-Tech-Forge-Comment
  4. curl -s https://www.techforge.community/.well-known/techforge.txt
  5. curl -sH 'Accept: application/vnd.techforge.v1+json' https://www.techforge.community/
  6. curl -sI -A 'TechForge-Learner/1.0' https://www.techforge.community/ → X-Welcome-Learner
  7. curl -sI -b 'forge=curious' https://www.techforge.community/ → Set-Cookie + X-Forge-Tip
  8. curl -su learner:forge https://www.techforge.community/training/basic
  9. curl -sH 'Authorization: Bearer training-token' https://www.techforge.community/training/bearer
  10. curl -s -o /dev/null -w '%{http_code}\n' https://www.techforge.community/training/status/418 → 418
  11. curl -s -X POST -d 'ping=pong' https://www.techforge.community/training/echo (only ping echoed; 1 KB cap)
  12. curl -s -o /dev/null -w '%{http_code}\n' -T /dev/null https://www.techforge.community/training/put-ok → 204
  13. curl -s https://www.techforge.community/curl/../.well-known/techforge.txt — same manifesto; no escape
  14. curl -s https://www.techforge.community/robots.txt points at .well-known/techforge.txt
  15. Manifesto line is base64 for /training/ai-osint (echo L3RyYWluaW5nL2FpLW9zaW50 | base64 -d)
  16. curl -sH 'X-AI-Assist: osint' https://www.techforge.community/training/ai-osint — Nordlicht Moden GmbH fiction; "model":"none-static-stub"
  17. curl -sH 'Accept: text/markdown' -H 'X-Role: junior-analyst' https://www.techforge.community/training/ai-osint/report — mistakes: AS15169 on TEST-NET; private 10.255.255.1 as “public edge”

Lab helpers

Live course URL: https://www.techforge.community/curl/

Join the Tech Forge community

Stuck on a flag? Share your capstone output (redact anything sensitive) and compare notes with other learners on the Tech Forge Discord.

Join the Discord

Or scan the code with your phone: techforge.community/discord

QR code linking to techforge.community/discord