KNX, %s and a backdoor

TL;DR

Several devices manufactured by WAGO contain an undocumented account with administrative privileges. The password for this account is device dependent, but easily brute-forcable.

CERT@VDE was kind enough to publish an Advisory about this issue.

KNX

A while ago, as part of an internal research project at Recurity Labs, I got my hands on a WAGO 750-889 KNX/IP Controller in order to familiarize with the KNX standard, which is used for building automation technology.

While briefly messing around with the KNX protocol, I came across the devices’ management Web interface and was taken down some rabbit holes called READPI and WRITEPI. On the device those two paths (e.g. http://my-750-889/READPI) allow read and write access to variables used for the Programmable Logic Controller (PLC) functionality within the device. They roughly work as follows: The http://my-750-889/READPI?ADR=MD666&FORMAT=%x request would read a double-word (denoted by D) from a variable (denoted by M) number 666 and displays it as a hexadecimal (%x) value. Respectively would http://my-750-889/WRITEPI?ADR1=MD666&VALUE1=cafecafe&FORMAT1=%x store the hexadecimal value cafecafe in the variable number 666.

%s

As the firmware for the device is not freely available, but can be requested on the vendor’s website, it seemed a worthwhile task to extract the firmware from the device by (ab)using the WRITEPI and READPI Web APIs. This was done by utilizing the %s format specifier. As %s denotes a pointer to a string, it was possible to use for example WRITEPI?ADR1=MD666&VALUE1=cafecafe&FORMAT1=%x to set variable 666 to contain the value 0xcafecafe. A subsequent read operation READPI?ADR=MD666&FORMAT=%.1s allows to read the byte at this memory address using the precision modifier .1 within the format string. With this rather arcane technique, it was possible to dump the firmware of the device via the Web interface.

And a Backdoor!

What followed was a short disassembler session, which led to the discovery of a full-blown vendor backdoor.

We would be happy to state the technical details and proceedings now, but, during the disclosure process, temperatures were running a little high on one side of the table, as some critical infrastructure systems are possibly affected. In order to allow the affected entities to apply patches to mitigate this issue, Recurity Labs was requested to withhold the full details of the inner workings of this backdoor for the time being.

However, since it is our strong opinion that spotting the backdoor does not pose much of a challenge, we want to stress the fact that affected systems should be patched immediately.

Disclosure Process

As telling a vendor about such an issue is a delicate thing to do, the disclosure process was started by submitting the detail to CERT-Bund. This was, considering the above, a smooth experience and a process professionally handled by the involved parties, namely CERT-Bund, CERT@VDE and WAGO.

Conclusions

Besides

Hidden backdoors are bad, mhhhhkay!

there is a bit more to think about here.

Obviously, a lot of luck was involved. Later firmware versions than the one on the test device had the %s format specifier removed. The release notes state:

4.3.1 [FIXED] WAT13171: “ip_address/READPI?ADR=MD2&FORMAT=%s” resulted in a system crash.

This initial distraction - “WTF!? I can put format strings in the URL?!” - kick-started the whole firmware analysis process; a quite entertaining ride. It shows nicely how different areas of IT (Security) can work hand in hand in order to be able to find interesting issues. Especially the combination of seemingly unrelated things, namely Web APIs and format strings, enabled the identification of the backdoor account.