54 lines
3.2 KiB
Bash
54 lines
3.2 KiB
Bash
#!/usr/bin/env bash
|
|||
|
|
# Manual end-to-end probe of a deployed decoy. Read-only: every request is recorded as
|
||
|
|
# evidence; nothing is executed. Configure via environment (see .env.example).
|
||
|
|
# BASE_URL e.g. http://127.0.0.1:8443 or https://your-decoy.example
|
||
|
|
# HOST_HEADER Host header the edge proxy routes on (default: localhost)
|
||
|
|
set -u
|
||
|
|
B=${1:-${BASE_URL:-http://127.0.0.1:8443}}
|
||
|
|
H="Host: ${HOST_HEADER:-localhost}"
|
||
|
|
printf '\000\377MZ-inert-manual-payload\r\n' > /tmp/manual-payload.bin
|
||
|
|
printf -- '--testboundary\r\nContent-Disposition: form-data; name="file"; filename="../../manual-multipart.bin"\r\nContent-Type: application/octet-stream\r\n\r\nINERT-MULTIPART-BODY\r\n--testboundary--\r\n' > /tmp/manual-multipart.bin
|
||
|
|
|
||
|
|
code() {
|
||
|
|
local label=$1; shift
|
||
|
|
printf '%-42s -> %s\n' "$label" "$(curl -sS --max-time 10 -o /dev/null -w '%{http_code} %{size_download}b' "$@")"
|
||
|
|
}
|
||
|
|
|
||
|
|
echo "== target $B =="
|
||
|
|
code "GET / (login page)" -H "$H" "$B/"
|
||
|
|
code "GET /favicon.ico" -H "$H" "$B/favicon.ico"
|
||
|
|
code "GET background.jpg" -H "$H" "$B/logon/LogonPoint/receiver/images/common/ReceiverFullScreenBackground.jpg"
|
||
|
|
code "GET logo.png" -H "$H" "$B/vpn/media/citrixgateway_logo_white.png"
|
||
|
|
code "GET login.js" -H "$H" "$B/logon/LogonPoint/login.js"
|
||
|
|
code "HEAD /" -I -H "$H" "$B/"
|
||
|
|
code "GET /nonexistent" -H "$H" "$B/nonexistent"
|
||
|
|
code "GET path traversal" --path-as-is -H "$H" "$B/../../etc/passwd"
|
||
|
|
code "OPTIONS /" -X OPTIONS -H "$H" "$B/"
|
||
|
|
code "POST auth form" -H "$H" -H 'Content-Type: application/x-www-form-urlencoded' --data 'login=demo&passwd=demo' "$B/nf/auth/doAuthentication.do"
|
||
|
|
code "PUT binary payload" -X PUT -H "$H" -H 'Content-Type: application/octet-stream' --data-binary @/tmp/manual-payload.bin "$B/sample.bin"
|
||
|
|
code "POST multipart upload" -H "$H" -H 'Content-Type: multipart/form-data; boundary=testboundary' --data-binary @/tmp/manual-multipart.bin "$B/upload"
|
||
|
|
code "POST oversize body" -X POST -H "$H" -H 'Content-Length: 999999999' --max-time 4 "$B/"
|
||
|
|
code "POST ambiguous framing" -X POST -H "$H" -H 'Transfer-Encoding: chunked' -H 'Content-Length: 5' --data '0' "$B/"
|
||
|
|
code "GET injection in User-Agent" -H "$H" -A 'pitboss PPE unexpectedly died NSPPE;:`id>/netscaler/ns_gui/manual-test.txt`;# X' "$B/"
|
||
|
|
|
||
|
|
echo -n "auth XML body: "
|
||
|
|
curl -sS --max-time 8 -H "$H" -H 'Content-Type: application/x-www-form-urlencoded' --data 'login=x&passwd=y' "$B/nf/auth/doAuthentication.do" | head -c 160
|
||
|
|
echo
|
||
|
|
l=0; while [ $l -lt 12 ]; do l=$((l+1)); curl -sS --max-time 5 -o /dev/null -H "$H" --data 'login=a&passwd=b' "$B/nf/auth/doAuthentication.do"; done
|
||
|
|
echo "sent 12 rapid auth attempts (brute-force threshold is 10)"
|
||
|
|
python3 - "$B" "${HOST_HEADER:-localhost}" <<'PY'
|
||
|
|
import socket, sys, urllib.parse
|
||
|
|
url = urllib.parse.urlsplit(sys.argv[1])
|
||
|
|
host = sys.argv[2]
|
||
|
|
s = socket.create_connection((url.hostname, url.port or 80), timeout=8)
|
||
|
|
s.sendall(f'GET / HTTP/1.1\r\nHost: {host}\r\n\r\n'.encode()
|
||
|
|
+ f'GET /favicon.ico HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n'.encode())
|
||
|
|
data = b''
|
||
|
|
while True:
|
||
|
|
chunk = s.recv(65536)
|
||
|
|
if not chunk:
|
||
|
|
break
|
||
|
|
data += chunk
|
||
|
|
print(f'keep-alive pipelined -> {data.count(b"HTTP/1.1 ")} responses, {len(data)} bytes')
|
||
|
|
PY
|
||
|
|
echo "done"
|