Pwn2Own or Not2Pwn, Part 1

Cim Stordal
Cognite
Published in
6 min readFeb 17, 2020

By Fredrik Østrem, Emil Sandstø and Cim Stordal

For many years industrial equipment and systems were manually monitored and managed by people and had no connection to the outside world or network. Today, as a result of digitalization, the reality is quickly changing. Industrial systems are no longer isolated from the outside world, but integrated into the information and data flows of IT-driven processes. And as industrial equipment and systems get connected, they also expose their vulnerabilities.

Cognite wants to take an active role in researching, understanding, and advising on vulnerabilities in industrial systems. That’s why it was a perfect fit for us when the Zero Day Initiative (ZDI) announced it would host a Pwn2Own competition targeting industrial control systems.

We had hoped to tell you the story about our successful entry. Instead, this is the story about how we failed — and what we learned.

This three-part series will help you understand our approach to discovering vulnerabilities in industrial control systems. In this first post, we give a high-level overview of the project. Part two will provide an in-depth analysis of the initial code execution vulnerability, while part three will explain the privilege escalation. (Note: At this point, the privilege escalation part is still a zero-day vulnerability. As a result, we will have to wait before publishing details about it.)

November 11:

It is important to Cognite that cybersecurity is a company-wide priority, and not something that only our security team cares about. That’s why we brought together three developers from three different teams for Pwn2Own: Fredrik Østrem in 3D, Emil Sandstø in Data Science Tooling, and Cim Stordal in Machine Learning and Security. On November 11, the three of us got together after work to take a stab at the problem we had identified. Based on our research, we decided to target Schneider Electric EcoStruxure Operator Terminal Expert, a human machine interface (HMI) configuration software, used to create and modify application for HMI products designed by Schneider Electric.

November 26:

Two weeks later, we had completed a full exploit chain using more than three memory corruption bugs and a logic bug to go from file opening to launching a command prompt with system-level privileges. The exploit chain was built without any Return Oriented Programming (ROP), making it easy to maintain.

Sounds like we were making good progress, right? Actually, this was the point where we started failing. Full disclosure: We managed to misread the rules. We assumed we needed a privilege escalation. That was not the case.

January 20:

Fast forward to the day before the competition. We had, as recently as the previous day, checked for any new updates and were certain that our bugs had survived. Everything was ready to go. Then, during the pre-meeting with ZDI, we learned that a new version of the software had dropped. We rushed back to our hotel room, and to our disbelief we saw that our initial vulnerability had been patched, forcing us to withdraw from the competition just minutes before it kicked off.

Later we learned that the bugs were patched on December 20 as part of a supplementary update that wasn’t published to the built-in update manager. Instead it had to be installed manually from Schneider Electric’s website.

Overview

The attack against the Schneider Electric EcoStruxure Operator Terminal Expert had to be launched by double clicking on a project file. This meant we were limited to only the initial load attack surface. Our goal was to craft our own project file that, when opened, would allow us to execute code as system-level user. To make this more difficult for us, EcoStruxure Operator Terminal Expert is a .NET binary with the latest security mitigations against code execution, such as ASLR and DEP, running on the latest version of Windows 10 with the most recent version of the .NET framework.

The first problem we had to solve was the file format: EcoStruxure Operator Terminal Expert uses its own file extension, “.vxdz,” for project files, and we had to be able to create our own files in that format to be loaded in the program. It’s relatively easy to see that the format is ZIP-based, based on the first few bytes, but attempting to unpack it gave us files containing encrypted data.

To be able to decrypt and encrypt the data, we had to look into the source code of the application with dnSpy, a nice little tool for disassembling and debugging .NET code. By looking through the source code, we found the PackageService class, which we could load and call in our own application to pack and unpack the project files.

DnSpy listing of the PackageService class
Unpacked “.vxdz,” project file found in temp directory

Once the project file was unpacked, we found it contained a number of .db files (see the image above for the full list of what is found in an unpacked project file). The .db files turned out to be SQLite database files that are read by the application when the project is loaded. When looking at the version of SQLite that Schneider Electric were using, we noticed they were using version 3.8.10.2, which has several known vulnerabilities that can be leveraged to obtain code execution. The exploitation of known vulnerabilities in SQLite has been a common theme lately, for example PHP and iOS.

When loading the database files, the application makes a SELECT query to a table. We constructed our own database file that would replace the application’s SELECT query with a malicious query. That allowed us to take advantage of the vulnerable SQLite version to trick it to call a private function that would load and run any DLL file we wanted, which we used to load a file from a network drive that we controlled. We’ll describe how this is done technically in part two.

The Competition

Team Claroty also targeted Schneider Electric at Pwn2Own. ZDI published a nice tweet detailing how they pulled it off.

However, we think the tweet contains too much information. After seeing the tweet, it took us 20 minutes to find the directory traversal bug they had exploited (which only adds to the insult that we somehow missed it during our original audit). It might be a different bug, but due to the quite limited attack surface of the EcoStruxure Operator Terminal Expert loading code, we highly suspect it is the same. Major props to Schneider Electric for reducing the attack surface!

A Matter of Time

Following the competition, a really interesting discussion broke out among the participants: How long do you spend on auditing versus exploitation? We were lucky (or perhaps unlucky) that we found the SQLite vulnerability almost immediately. Would we have been better off spending more time auditing the code, instead of exploiting the first bug we found?

These are difficult questions to answer, but really interesting ones. It might have been more beneficial for us to have spent more time auditing, so that we would have been more prepared for having one of our bugs killed. We would also have had more tools in our toolbelt when building our exploit chain. As the SQLite memory corruption exploit is quite a bit more complex than a directory traversal bug, and offers less stability, we probably would have opted for exploiting the directory traversal vulnerability.

Stay tuned for part two, in which we’ll cover in depth how we used the fact that we controlled the database, and that Schneider Electric used an outdated version of SQLite, to create a fully working exploit chain.

Want to Learn More?

We’ll be presenting our research on industrial control system product security at Ignite 2020, Europe’s largest conference dedicated to industrial digitalization, on June 10–11 in Oslo, Norway. We hope to see you there!

To learn more about Ignite, go to: https://www.cogniteignite.com/

--

--