Universal Robots delivers unprotected IP through their insecure development platform, Universal Robots+.
Advertised as the leading global development environment for collaborative automation, Universal Robots+ (or UR+ for short) presents no direct security measures, exposing the intellectual property of hundreds of partners around the world.
Unfortunately, It's not the first time (see a previous article) I write about Universal Robots insecurity. This time it's about their development platform: Universal Robots+.
An insecure leading development environment for robots
UR+ gets presented as a lead dev. environment for robots. One that provides developers with the tools and utilities needed to create collaborative applications for UR robots.
According to their website, there're more than 600 partners that have already submitted their "Plug-and-Produce products". That is, 600+ partners sending their Intellectual Property (IP) to this marketplace and likely, monetizing it. Altogether, it appears like a rather relevant matter for both UR and the corresponding partners.
According to Carol Lawerence [1]:
Two years ago, Universal created an online marketplace that makes it easier for customers to tap into its growing ecosystem of accessory providers.
These efforts have been “absolutely critical” to the company’s growth, Shepherd added. Meanwhile, industrial robot manufacturers are taking aim at the cobot space.
This environment is rather centric in Universal Robots strategy. Moreover, likely, many of these partners might be using this platform as their way of monetization. With all this, One would expect Universal Robots would take security of these IP very seriously. Unfortunately, it doesn't seem to be the case.
URCap
s are deployed by default under/root/.urcaps
as plain zip files containing all the logic to add functionality to the UR robots. The partners' IP is thereby deployed without security protection measures. No encryption, no integrity checks, nothing. Nothing at all.
Provided an attacker could access that directory within the robot, it'd basically be able to steal, tamper and/or delete relevant intellectual property.
The video above shows how a malicious attacker that's connected to the robot adjacent's network (via a local network, or any of the exposed physical ports for that matter) does the following:
- performs a dictionary attack over the SSH open port, obtaining the root credentials
- launches an exploit that exfiltrates the IP within the robot using previously found credentials as default
This flaw was first reported 23 days ago at https://github.com/aliasrobotics/RVD/issues/1489. Later, it obtained a CVE ID: CVE-2020-10267. Several weeks have past since, yet the vendor ignores communication attempts and hasn't taken a single step towards mitigation. Specially concerning is the fact that such a trivial attack with a couple of very silly exploits can impact hundreds of partners across the world investing resources on further popularizing this platform. Reproducing the attack above is straightforward with the alurity toolbox and following yaml file:
alurity.yml file to reproduce RVD#1410
networks:
- network:
- driver: overlay
- name: urnetwork
- encryption: false
containers:
- container:
- name: ur_3121
- modules:
- base: registry.gitlab.com/aliasrobotics/offensive/alurity/robo_ur_cb3_1:3.12.1
- network: urnetwork
- cpus: 4
- memory: 4096
- container:
- name: attacker
- modules:
- base: registry.gitlab.com/aliasrobotics/offensive/alurity/alurity:latest
- volume: registry.gitlab.com/aliasrobotics/offensive/alurity/expl_robosploit/expl_robosploit:latest
- volume: registry.gitlab.com/aliasrobotics/offensive/alurity/deve_atom:latest
- volume: registry.gitlab.com/aliasrobotics/offensive/alurity/reco_nmap:latest
- network: urnetwork
##################
# flow for testing and development
##################
flow:
- container:
- name: attacker
- window:
- name: attacker
- commands:
- command: "sleep 3"
- type: "nmap -sV -T5 ur_3121"
- container:
- name: ur_3121
- window:
- name: gui
- commands:
- type: "source /root/run_gui.sh && $RUN_GUI"
# - command: "source /root/run_gui.sh && $RUN_GUI"
- window:
- name: urcap
- commands:
# Launch ssh daemon
- command: "mkdir /var/run/sshd"
- command: "/usr/sbin/sshd"
# # Fetch ROS Industrial driver's UR cap
- command: "cd /root/.urcaps && wget https://github.com/UniversalRobots/Universal_Robots_ROS_Driver/raw/master/ur_robot_driver/resources/externalcontrol-1.0.1.urcap"
# Fetch a commercial dashboard solution for evaluation
- command: "cd /programs && wget https://github.com/KimNyholm/web-dashboard-urcap/archive/v2.1.0.zip && unzip v2.1.0.zip"
# Force install it
- command: "cp /programs/web-dashboard-urcap-2.1.0/webdashboard-2.1.0.urcap /root/.urcaps"
- split: horizontal
- command: "htop"
- attach: ur_3121
Concerns don't stop with IP loss though. There's more.
Data privacy concerns
Some of these software components might keep data from end-user systems. Data that's subject to GDPR and where privacy matters. The lack of security measures in the Universal Robots+ development kits makes one wonder how this sensitive data might get treated.
Safety concerns
Unfortunately, the worst case scenario isn't IP or data privacy loss, it's safety violations. The existence of cybersecurity flaws in an industrial robot means that it can no longer be deemed safe.
Let's walk together through a simple inspection of the file system of these robots:
# Launch a simulated instance of the UR CB3.1
alurity run registry.gitlab.com/aliasrobotics/offensive/alurity/robo_ur_cb3_1:latest ✔ 5718 22:11:39
latest: Pulling from aliasrobotics/offensive/alurity/robo_ur_cb3_1
a7a556b25c68: Pull complete
714fbba61192: Pull complete
cc08b1c384b4: Pull complete
073d0ca84e74: Pull complete
Digest: sha256:ef3ced3aaf7b313c4ad5f4c9ed0cabc5969503973ad9ade1ac25332b0c19644f
Status: Downloaded newer image for registry.gitlab.com/aliasrobotics/offensive/alurity/robo_ur_cb3_1:latest
registry.gitlab.com/aliasrobotics/offensive/alurity/robo_ur_cb3_1:latest
Downloading images, this could take a bit
registry.gitlab.com/aliasrobotics/offensive/alurity/robo_ur_cb3_1:latest already available locally
All images downloaded successfully
registry.gitlab.com/aliasrobotics/offensive/alurity/robo_ur_cb3_1:latest, successfully init.
Entering to alurity-run-a334d31818a4485e962a2735b4b63dfd:
# Inspect a bit the file system
alias@alurity-run-a334d31818a4485e962a2735b4b63dfd:~$ cd /root/
alias@alurity-run-a334d31818a4485e962a2735b4b63dfd:/root$ ls -a
. .bashrc .polyscope .ssh .urcontrol GUI firmware histogram.properties kiosk-session legal_notices metadata.n3 run_gui.sh stopurcontrol.sh xsession
.. .modules .profile .urcaps .viminfo fdisk.script flightreports install_module.sh kiosk.sh log_history.txt polyscope.log starturcontrol.sh usbplug.sh
# Inspect the /root/.urcontrol folder
alias@alurity-run-a334d31818a4485e962a2735b4b63dfd:/root$ cd .urcontrol/
alias@alurity-run-a334d31818a4485e962a2735b4b63dfd:/root/.urcontrol$ ls
calibration_trajectory_level0_UR10.v1.ct calibration_trajectory_level1_UR10.v1.ct daemon joint_size0_CB3_rev1.conf language urcontrol.conf.UR3
calibration_trajectory_level0_UR3.v1.ct calibration_trajectory_level1_UR3.v1.ct force_control_UR10_CB3_rev1.conf joint_size1_CB3_rev1.conf metadata.json urcontrol.conf.UR5
calibration_trajectory_level0_UR5.ct calibration_trajectory_level1_UR5.ct force_control_UR5_CB3_rev1.conf joint_size2_CB3_rev1.conf safety.conf
calibration_trajectory_level0_UR5.v1.ct calibration_trajectory_level1_UR5.v1.ct from_controller_safety.conf joint_size3_CB3_rev1.conf touch_calibration.properties
calibration_trajectory_level0_UR5G5.v1.ct calibration_trajectory_level1_UR5G5.v1.ct joint_null.conf joint_size4_CB3_rev1.conf urcontrol.conf.UR10
# Check the content of safety.conf
alias@alurity-run-a334d31818a4485e962a2735b4b63dfd:/root/.urcontrol$ cat safety.conf
# Beware: This file is auto-generated from PolyScope.
# NOTE: The SafetyParameters section is protected by a CRC checksum, please use the supplied tool
## SafetyParameters ##
[NormalModeSafetyLimits]
maxTcpSpeed = 1.5
maxForce = 150.0
maxPower = 300.0
maxMomentum = 25.0
maxJointSpeed = [3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926]
minJointPosition = [6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254]
maxJointPosition = [0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988]
minJointRevolutions = [-2, -2, -2, -2, -2, -2]
maxJointRevolutions = [1, 1, 1, 1, 1, 1]
plane0 = [0.0, 0.0, 0.0, 0.0]
plane1 = [0.0, 0.0, 0.0, 0.0]
plane2 = [0.0, 0.0, 0.0, 0.0]
plane3 = [0.0, 0.0, 0.0, 0.0]
plane4 = [0.0, 0.0, 0.0, 0.0]
plane5 = [0.0, 0.0, 0.0, 0.0]
plane6 = [0.0, 0.0, 0.0, 0.0]
plane7 = [0.0, 0.0, 0.0, 0.0]
tcpOrientationVector = [0.0, 0.0, 1.0]
maximumTcpOrientationDeviation = 6.2831855
[ReducedModeSafetyLimits]
maxTcpSpeed = 0.75
maxForce = 120.0
maxPower = 200.0
maxMomentum = 10.0
maxJointSpeed = [3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926, 3.3415926]
minJointPosition = [6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254, 6.2308254]
maxJointPosition = [0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988, 0.05235988]
minJointRevolutions = [-2, -2, -2, -2, -2, -2]
maxJointRevolutions = [1, 1, 1, 1, 1, 1]
plane0 = [0.0, 0.0, 0.0, 0.0]
plane1 = [0.0, 0.0, 0.0, 0.0]
plane2 = [0.0, 0.0, 0.0, 0.0]
plane3 = [0.0, 0.0, 0.0, 0.0]
plane4 = [0.0, 0.0, 0.0, 0.0]
plane5 = [0.0, 0.0, 0.0, 0.0]
plane6 = [0.0, 0.0, 0.0, 0.0]
plane7 = [0.0, 0.0, 0.0, 0.0]
tcpOrientationVector = [0.0, 0.0, 1.0]
maximumTcpOrientationDeviation = 6.2831855
[SafetyIOConfiguration]
emergencyStopInputA = 255
emergencyStopInputB = 255
reducedModeInputA = 255
reducedModeInputB = 255
safeguardStopResetInputA = 0
safeguardStopResetInputB = 1
threePositionEnablingInputA = 255
threePositionEnablingInputB = 255
operationalModeInputA = 255
operationalModeInputB = 255
systemEmergencyStopOutputA = 255
systemEmergencyStopOutputB = 255
robotMovingOutputA = 255
robotMovingOutputB = 255
robotNotStoppingOutputA = 255
robotNotStoppingOutputB = 255
reducedModeOutputA = 255
reducedModeOutputB = 255
notReducedModeOutputA = 255
notReducedModeOutputB = 255
[MiscConfiguration]
teach_pendant = 1
euromap67 = 0
[ReducedModeTriggerPlanes]
plane0 = [0.0, 0.0, 0.0, 0.0]
plane1 = [0.0, 0.0, 0.0, 0.0]
plane2 = [0.0, 0.0, 0.0, 0.0]
plane3 = [0.0, 0.0, 0.0, 0.0]
plane4 = [0.0, 0.0, 0.0, 0.0]
plane5 = [0.0, 0.0, 0.0, 0.0]
plane6 = [0.0, 0.0, 0.0, 0.0]
plane7 = [0.0, 0.0, 0.0, 0.0]
## SafetyParameters ##
[Checksum]
safetyParameters = 2588156642
majorVersion = 3
minorVersion = 2
The safety.conf
file already warns about the fact that there's a checksum in use. Integrity seems to be in place for safety configuration but is this enough to avoid attackers to easily tamper with such configurations? I'll stop here, but this should already provide most with an intuition on the safety setup within Universal Robots technology.
Some thoughts
Universal Robots should probably reconsider their position with regard UR+. Not just for their 600+ partners, but specially, for their users. Those 42K+ robots sold contain IP, some purchased, I strongly believe it's a general shared feeling that this situation should change. Many of us have purchased IP from third parties and would like to prevent the bad guys from tampering it, stealing it or simply remove it.
Universal Robots should probably reconsider their position with regard UR+. Not just for their 600+ partners, but specially, for their users.
In a previous article, I left some thoughts and actions that a concerned user may want to take to let Universal Robots know about it. Here're some additional thoughts:
I'm a UR+ partner, what should I do
Well, first I'd encourage you to review the architecture of your software and/or hardware components. Check which data are you using, what's the data you're storing and how. Depending on what you're saving and/or doing, it might be the case that you would need to ask for legal counsel.
Second, turn to Universal Robots and demand that your components get shipped to a secure environment. Many of us are investing our time to build robotic applications and putting your our in this platform.
I'm a UR user, how can I protect my IP
At the time of writing, I'm aware of only one single solution that will allow you to harden the file system of Universal Robots controller and that's actively being maintained: the Robot Immune System (RIS).
Disclaimer: I currently work for Alias Robotics, the company behind the Robot Immune System.
Carol Lawrence. Rise and Fall of Rethink Robotics (2019). https://www.asme.org/topics-resources/content/rise-fall-of-rethink-robotics ↩︎