OpenEMR - Remote Code Execution in your Healthcare System

We discussed this vulnerability during Episode 191 on 27 February 2023

A couple interesting issues in OpenEMR leading to unauthenticated remote code execution and file disclosure.

The file disclosure bug starts from the fact that the installer/setup functionality can still be triggered after installation. A complete reinstall is not possible, but it is possible to execute some specific methods of the setup process.

The setup.php page will create a new Installer object filled with the configuration information from $_REQUEST. It will then execute some method based on the current $_POST['state']. State will lead to the displayNewThemeDiv method being executed, this method will make a MySQL query using the configuration information derived from $_REQUEST. So an attacker can query a server they control. A rogue MySQL server can potentially read files from the client side of the connection. This is to support a LOAD DATA query with a LOCAL INFILE modifier. By default this is not enabled but as OpenEMR makes legitimate use of the functionality it should be enabled on the PHP server, so the MySQL server can make requests for files of the client.

The second set of issues abuses some existing functionality (a file upload) along with a reflected XSS and LFI due to path traversal for code execution. The reflected XSS is fairly straightforward but also interesting:

<a onclick="dopopup('<?php echo $_SERVER['REQUEST_URI'] . '&display=fullscreen&encounter=' . $encounter; ?>');"

You can see that is reflects the REQUEST_URI into the onclick handler, normally any ' or " would be urlencoded in this and so no escape possible, but the author notes that you can include HTML entities within the onclick handler that will be decoded by the time the JavaScript inside is processed. So one can escape the inner single-quoted string by including an &apos; and then inject their JavaScript.

The directory traversal and local file include is rather traditional. It includes a file based on a $_GET paramater and appends .plugin.php to it. Without checking for traversals its possible to load the file from other locations, and combined with the file upload functionality (the post does not detail the intented purpose of the file upload) one can upload a PHP file with the correct name and have it be included.