Loading up a pair of Qt bugs: Detailing CVE-2019-1636 and CVE-2019-6739

April 03, 2019 | Ziad Badawi

We came across an interesting bug recently that affects a multitude of products that are based on Qt5. Since many developers rely on the Qt framework for C++ and Python development, the consequences of the bug could be quite far reaching.

Any GUI application built using the Qt5 framework will have a set of supported command line options that can be passed to the executable binary. For example, running

        QtGUIapp.exe -qwindowtitle foobar

will replace whatever the developer has as the window title with foobar. One interesting command-line option is platformpluginpath. This parameter is supposed to contain a directory path or UNC share pointing to Qt5 plugins. In other words, the target location should contain Dynamic Link Library (DLL) files on Windows. The Qt5 application, depending on certain metadata, will automatically execute those plugins as soon as they are loaded in memory.

You might ask, how can this “feature” be exploited? What kind of attack vectors are applicable? Well, it is true that there is no suitable vector in many situations, but this changes when a custom URI scheme is configured. Let us talk about a couple of cases, CVE-2019-1636 for Cisco Webex Teams and CVE-2019-6739 for Malwarebytes, where the odds did not end up in their favor.

Cisco Webex Teams (CVE-2019-1636)

After installing Cisco Spark and Webex Teams, a URI handler for the “ciscospark” protocol gets configured in the registry under the following key:

Figure 1 - Registry entry that configures a custom URI scheme for Webex

This key ensures any URI that uses the ciscospark protocol identifier will end up calling CiscoCollabHost.exe. The Cisco Spark application is based on Qt5 and, as mentioned previously, it supports several command line arguments -- including platformpluginpath. Spark allows users to read and write multiple image formats, such as .gif, .jpg, and .bmp files. This functionality requires several plugins for parsing image formats, including qgif.dll, qicns.dll, qico.dll, qjpeg.dll, qsvg.dll, qtga.dll, qtiff.dll, qwbmp.dll, and qwebp.dll. Those plugins are loaded by default from “\imageformats\” directory. However, passing “-platformpluginpath” to the executable (CiscoCollabHost.exe) will allow loading external plugins.

For example, the command

CiscoCollabHost.exe -platformpluginpath C:/Users/research/Desktop/poc

will load and execute all DLLs in the C:/Users/research/Desktop/poc/imageformats directory. Here’s the code that handles that DLL load.

Figure 2 - Code responsible for loading DLLs in Qt5Core.dll

Figure 3 - Code that reads from /imageformats dir and starts parsing images

Knowing this, exploitation is straightforward. A POC for example can be as simple as

Where the remote “share” contains an “imageformats” directory that holds a “malicious.dll” file. The DLL name does not matter in this case since QT5 loads plugins based on their metadata and not their name.

Creating that malicious DLL might seem trivial to Qt5 developers, but it initially wasn’t for us. Having no experience in Qt5, compiling a specially crafted DLL that will be loaded by Qt5 took some time. After spending a while trying to figure out the reason for the file not being loaded and its “DllMain” not being executed, we dove a bit deeper into the Qt core to find out that the missing piece was a PE section named .qtmetad. It turns out that a metadata section is required to be present in DLL plugins for them to be identified by Qt5. That section contains details about the plugin and the data it handles like mime types. Copying the section contents from another valid plugin like “qgif.dll” should do the trick.

Figure 4 - Metadata contents from a GIF parsing plugin

In Visual Studio, you can create a section using #pragma const_seg(".qtmetad"). Other than the DllMain entry point, the qt_plugin_instance function is also executed when the DLL is loaded. Cisco patched this with SA20190123.

Malwarebytes Anti-Malware (CVE-2019-6739)

 The same exact concept applies to Malwarebytes Anti-Malware. The protocol identifier used is “malwarebytes” and its registry key is as follows:

Figure 5 - Registry entry that configures a custom URI scheme for Anti-Malware

The difference here is the type of plugins loaded by default. Unlike Webex, Anti-Malware does not read and write image files, therefore the previously mentioned DLLs will not be loaded. Instead, the Windows Integration Plugin qwindows.dll is used. By default, this DLL is located at “\platforms\”.

The POC is similar as well:

It is the same with the specially crafted DLL. Simply copying the “.qtmetad” section from “qwindows.dll” will work just fine. By feeding this command line option to Malwarebytes when it loads, an attacker could potentially take over a system by loading their DLL instead of the program defaults. Malwarebytes resolved this vulnerability with builds including and after 3.6.1.2711-1.0.508.

Conclusion

This technique can be applied to most, if not all, Qt5-based applications. However, in most cases it will occur without any real effect unless there is a clear attack vector as shown in the above CVEs. It all depends on the vendor’s implementation after all and what kind of vectors they provide. In these cases, the developers implemented a legitimate feature of Qt that was leveraged by attack vectors present in other mechanisms in the product. For developers, if you are using a framework to assist in your content creation, definitely be sure you understand options and features that will always be loaded – even if you don’t specify them. Failure to do so could lead to situations like these examples where built-in options end up causing unintended consequences. 

You can find me on Twitter at @ziadrb, and follow the team for the latest in exploit techniques and security patches.