Last active
February 13, 2026 12:10
-
-
Save BrixSat/b802c7f64199b4cefdc86d74357c1b59 to your computer and use it in GitHub Desktop.
Safire nvr obtain serial and verificationCode for p2p
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| import socket, threading, time, ssl | |
| # --- Configuration --- | |
| LISTEN_IP = '0.0.0.0' | |
| # We will use the primary IPs discovered in your previous logs | |
| DISCOVERY_HOST = "dev.hicloudcam.com" | |
| AUTH_HOST = "54.228.90.143" | |
| CERTFILE, KEYFILE = 'server.crt', 'server.key' | |
| def pipe(src, dst, direction): | |
| """Passes data through and prints the FULL, UNTRIMMED content.""" | |
| try: | |
| while True: | |
| data = src.recv(16384) | |
| if not data: break | |
| ts = time.strftime("%H:%M:%S") | |
| print(f"\n[{ts}] {direction} ({len(data)} bytes)") | |
| print("=" * 60) | |
| # Printing raw bytes decoded as ignore to see all ASCII | |
| print(data.decode(errors="ignore")) | |
| print("=" * 60) | |
| # Search for the key in the raw stream | |
| if b"SecretKey" in data or b"VerificationCode" in data: | |
| print("\n\033[93m" + "!"*60) | |
| print("TARGET FOUND: THE SECRET KEY IS IN THE XML ABOVE!") | |
| print("!"*60 + "\033[0m\n") | |
| dst.sendall(data) | |
| except Exception as e: | |
| print(f"[*] {direction} connection closed: {e}") | |
| finally: | |
| try: src.close(); dst.close() | |
| except: pass | |
| def handle_client(client_sock, addr, s_ctx, c_ctx, port): | |
| try: | |
| # For ports 8555 and 6900, we use TLS | |
| if port in [8555, 6900]: | |
| tls_client = s_ctx.wrap_socket(client_sock, server_side=True) | |
| target = DISCOVERY_HOST if port == 8555 else AUTH_HOST | |
| remote_raw = socket.create_connection((target, port), timeout=10) | |
| tls_remote = c_ctx.wrap_socket(remote_raw, server_hostname=target) | |
| threading.Thread(target=pipe, args=(tls_client, tls_remote, f"CLIENT -> {target}"), daemon=True).start() | |
| threading.Thread(target=pipe, args=(tls_remote, tls_client, f"{target} -> CLIENT"), daemon=True).start() | |
| # For port 6800, we use plain TCP | |
| else: | |
| remote = socket.create_connection((AUTH_HOST, 6800), timeout=10) | |
| threading.Thread(target=pipe, args=(client_sock, remote, f"CLIENT -> {AUTH_HOST}"), daemon=True).start() | |
| threading.Thread(target=pipe, args=(remote, client_sock, f"{AUTH_HOST} -> CLIENT"), daemon=True).start() | |
| except Exception as e: | |
| print(f"[-] Handshake error on port {port}: {e}") | |
| def main(): | |
| s_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | |
| s_ctx.load_cert_chain(CERTFILE, KEYFILE) | |
| c_ctx = ssl._create_unverified_context() | |
| c_ctx.set_ciphers('DEFAULT@SECLEVEL=0') | |
| # Start listeners for all three potential ports | |
| for p in [8555, 6900, 6800]: | |
| s = socket.socket() | |
| s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
| s.bind((LISTEN_IP, p)) | |
| s.listen(10) | |
| # Use a lambda to pass the specific port to the handler | |
| threading.Thread(target=lambda s=s, p=p: [handle_client(s.accept()[0], addr, s_ctx, c_ctx, p) for addr in iter(lambda: s.accept()[1], None)], daemon=True).start() | |
| print(f"[*] Listening for hijack on port {p}...") | |
| print("\n=== TRANSPARENT SNIFFER ACTIVE ===") | |
| print("Point the NVR to your computer's IP or let it use the cloud.") | |
| print("Wait for Phase 2 (Port 6900) to appear.\n") | |
| while True: time.sleep(1) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import requests | |
| from requests.auth import HTTPBasicAuth | |
| import urllib3 | |
| # --- CONFIGURATION --- | |
| NVR_IP = "192.168.1.244" # Your NVR's local IP | |
| USERNAME = "admin" | |
| PASSWORD = "YOUR_PASSWORD" # The one you use to log in | |
| url = f"https://{NVR_IP}/ISAPI/System/Network/EZVIZ" | |
| try: | |
| response = requests.get( | |
| url, | |
| auth=HTTPBasicAuth(USERNAME, PASSWORD), | |
| verify=False, | |
| timeout=10 | |
| ) | |
| print("\n--- NVR CURRENT CONFIGURATION ---") | |
| print(response.text) | |
| except Exception as e: | |
| print(f"Error: {e}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Hello, i needed to connect my safire nvr that had p2p config to my phone. The problem was that it did not generate any qrcode for me to scan. | |
| I had to dig a litle bit to find it. | |
| I was able to crack the thing by using this mim.py and changing the gateway from the device to 192.168.1.10 (my pc) instead of 192.168.2.254. | |
| I also configured the p2p cloud to be my pc 192.168.1.10 since it allowed me to do that. | |
| Then i runned this fw rules on my pc | |
| sudo sysctl -w net.ipv4.ip_forward=1 | |
| sudo iptables -t nat -A PREROUTING -p tcp --dport 8555 -j REDIRECT --to-port 8555 | |
| sudo iptables -t nat -A PREROUTING -p tcp --dport 6900 -j REDIRECT --to-port 6900 | |
| python3 mim.py and the output nice, i founded the serial, this proved that our proxy worked. (not always as they change ips from the remote). | |
| From here i got the serial it uses: | |
| (6<?xml version="1.0" encoding="utf-8"?> | |
| <Request> | |
| <DevSerial>DVR/DVS-18-.......</DevSerial> | |
| <FirmwareVersion>V3.4.81 build 170320</FirmwareVersion> | |
| <OperationCode Code="....." Checksum="...."/> | |
| <DevType>DVR/DVS-18-A</DevType> | |
| <Address LocalIp="192.168.1.244" MAC="xxxxxxxx"/> | |
| <DevTypeDisplay>HTVR6116A</DevTypeDisplay> | |
| <SubSerial>THIS_IS_WHAT_WE_NEED</SubSerial> | |
| <DomainName Redirect="1">192.168.1.10</DomainName> | |
| <FirmwareIdentificationCode>.......</FirmwareIdentificationCode> | |
| <DevIdentificationCode>......</DevIdentificationCode> | |
| <Licence/> | |
| <ProtocolVersion Mode="2" Type="1">V1.5.7</ProtocolVersion> | |
| <Status>1</Status> | |
| </Request> | |
| 13a8f85d09ae3edb0b1c2f37666e99cc | |
| I got a lot of trash and this magical xml. | |
| This xml is signed with an hash that uses a hardcoded salt so we cannot change the content or it will be rejected. | |
| From here i could obtain the magic serial number used in the app and know was just missing the verificationCode. | |
| I was able to obtain the key with post.py | |
| --- NVR CURRENT CONFIGURATION --- | |
| <?xml version="1.0" encoding="UTF-8" ?> | |
| <EZVIZ version="1.0" xmlns="http://www.std-cgi.com/ver20/XMLSchema"> | |
| <enabled>true</enabled> | |
| <registerStatus>true</registerStatus> | |
| <redirect>true</redirect> | |
| <serverAddress> | |
| <addressingFormatType>hostname</addressingFormatType> | |
| <hostName>dev.hicloudcam.com</hostName> | |
| </serverAddress> | |
| <verificationCode>MAGIC_NUMBERS_HERE</verificationCode> | |
| </EZVIZ> | |
| With this 2 fields, revert the device gateway and cloud server to the original. | |
| Now connect the phone to the same network of the device and manual add the device. | |
| Enter the serial number, press ok and the verificationCode when asked. | |
| This was a nice journey that was accomplished in a safire nvr old model. | |
| Thanks in advance |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment