Early in May of 2020, Contrast Labs was exploring different ways in which we could help the community or world combat the increase in attacks against medical and testing facilities. We decided quickly that doing some form of hack-a-thon on OpenMRS (an open medical records system) would help us learn but at the same time help OpenMRS find any application vulnerabilities they could fix to increase their security posture.
Chaining Path Traversal Vulnerability
We spent a day looking through code and examining the application. One of the issues Contrast Labs reported involved chaining path traversal in the htmlformentry-3.10.0 module (OpenMRS has many different modules that can be added to the system) with unvalidated upload of a server-side template to achieve remote code execution. The application vulnerability requires authentication; however, it does not require admin access.
Considering OpenMRS is written in Java, we decided to first instrument the application with Contrast Assess to find vulnerabilities. Once instrumented, we used the OpenMRS application and immediately detected a number of issues. One of the interesting findings was a path traversal vulnerability in the definitionUiResource parameter of the /htmlformentryui/htmlform/enterHtmlFormWithSimpleUi.page endpoint. The details of the vulnerability can be seen below:
To explain what was seen, Contrast Assess detected user data entered in the definitionUiResource URL query string parameter that was used directly in viewing a new File object. This resulted in the path traversal vulnerability.
Reproducing the Path Traversal Vulnerability
Since Contrast Assess detected that the new File object was being accessed, we thought this could be an avenue to break out into other directories on the system or an opportunity to achieve remote code execution (RCE). Attempting to reproduce the path traversal vulnerability, we entered ../ directory escape characters into the definitionUiResource parameter in the request query string. It resulted in a very detailed error message indicating the application was looking for a file on disk.
This was good news. However, what was determined is that this particular endpoint expected that the file retrieved be a specially crafted server-side template. Any other file type resulted in an error, such as including sensitive local files like /etc/passwd (observe the following org.xml.sax.SAXParseException error message).
This told us that we needed to somehow take advantage of the server-side template by uploading a malicious template file to the system. We researched this further and discovered that OpenMRS implements a custom templating format through its HTML form entry module to render application views for the end-user. We were able to locate documentation for the HTML forms and noticed that particular parsing fields exist that evaluate Velocity template language.
After discovering this, we were able to craft a well-formatted template that included code execution, using the vitals.xml from the reference application as a starting point. As you can see in the screenshot below, we utilized Velocity template language to execute a direct system command of cat /etc/passwd and in turn return the contents of the “passwd” file. If we could get this to run on the server and view the contents of that file, then we could prove RCE.
Uploading the File and Locating It
Now that we have a vulnerable endpoint and malicious template, we need to figure out how to upload the file and locate it on the system. Since OpenMRS is a medical records system, there are multiple places to upload files like medical records and much more. One such location that allows any type of file to be uploaded is when a patient checks in for a visit within the application. When doing so, it is possible to attach files to a patient record in the system, as seen in the screenshot below.
Once the malicious template was uploaded into this location, the next step was to determine the name and location of the uploaded file. The resulting filename on the system can be inferred based on a combination of the original upload filename and UIDs included in responses from the application.
Reporting the Vulnerability
As you can see, the original filename was rce_20200826_13393.xml and the response included the UUID of 955cf4a9-6b64-435a-b676-546801955e63. In further research, we determined the files were saved in the appdata/complex_obs directory and that the filename is now rce_20200826_13393_955cf4a9-6b64-435a-b676-546801955e63.xml. Once we reliably correlated an uploaded file to the file location on disk, it was possible to upload our crafted template and then subsequently request the file through the discovered path traversal vulnerability. As you can see below, the exploit request resulted in the contents of the /etc/passwd file being returned to the end-user.
At this point, we had reached a level of confidence to report the issue to OpenMRS. OpenMRS has a defined process for reporting security issues, which is outlined here. We submitted the issue, and the timeline below outlines the speedy fix.
- 05-05-2020 Contrast Labs reported the issue to OpenMRS.
- 05-06-2020 OpenMRS responded, acknowledging the vulnerability.
- 06-02-2020 Code was merged to master to fix the directory traversal.
- 07-24-2020 Updated htmlformentry was added to version 3.11.0 and pushed to the public.
- 08-25-2020 Preliminary CVE issued: CVE-2020-24621
Any systems of OpenMRS that utilize the htmlformentry module should upgrade to version 3.11.0 here.
Credit is given to the Contrast Labs team involved in the research:
- David Lindner, @golfhackerdave
- Dan Amodio, @DanAmodio
- Adam Schaal, @clevernyyyy
- Matt Austin, @mattaustin