I wanted to provide a write up of my research relating to the security vulnerabilities Johnson Controls published on August 15th in order to clear up some confusion and better describe the actual attack chain. These vulns has been assigned CVE-2019-7593 and 7594. Also tracked under the DHS ICSA number ICSA-19-227-01. I recommend reading the official notification provided by Johnson Controls here also (PDF).
First, a bit of housekeeping. This research was conducted by me alone and is not in anyway associated with my employer. The conclusions, opinions and views expressed here are mine and do not reflect those of my employer or any other group. All research was conducted on equipment I had authorization to access. At no time were any systems or cloud services I did not have authorization to access involved in this research.
Second, some history. I began looking into the Metasys around November of 2018. My original goal was to investigate whether I could convince the NAE to load a modified software image. As I started looking into the process the Metasys UI and the target system interact, my attention shifted to the login procedure and how it was secured. I disclosed my findings to the Johnson Controls security team around May of 2019. My findings were accepted and a 90 day embargo was requested to put the appropriate notice together etc. I found the JCI security team to be perfectly reasonable in their requests and professional in their dealings with me.
Now on to the fun stuff. I’ll explain briefly how the Metasys login process works, followed by a description of the 7593 and 7594 vulns and finally a proof of concept of how I exploit these vulns into an attack. It’s important to be familiar with how RSA public/private key encryption works. This information is relevant for Metasys version 9 and below only. Anything newer then that, your guess is as good as mine.
Metasys Login Process
The Metasys UI is a Java application (which I’ll refer to as the Client) that is downloaded directly from either the ADX, ADS or NAE (the Server) being connected to. Whether this is done directly or through the JCI Launcher is inconsequential. When the Java app is started, there is a process of initialization and initial resource loading. During that time the Client requests the Server’s public RSA key. Once the initialization is complete, the Client prompts the user for a username and password.
The password is hashed (using SHA1) and a credential package is constructed using the plaintext username and hashed password. This credential package is then encrypted using RC2. The encryption key for the RC2 payload is encrypted using the Server’s RSA public key. This forms the completed payload which is sent to the Server using an HTTP POST request.
I’m assuming at this point the Server decrypts the RC2 encryption key using its RSA private key, then decrypts the username / password with the RC2 key. The Server will compare the hash and username to a security file containing a list of valid users. If the login credentials are valid, the Server will respond with a Session Token encrypted with the same RC2 encryption key.
All subsequent requests between the Client and Server are accompanied by the same encrypted Session Token. Without a valid Session Token, requests are rejected.
Common RSA Keys CVE-2019-7593
As described above in the login process, it’s simple to request the public RSA key from the Server using a simple POST request. Ideally, the RSA keys should be unique for each Metasys device. However I was able to test 10 NAE, NCE and ADX devices and found they all returned the same RSA public key. Therefore the RSA key pairs seem to be common among these devices. If the RSA private key is compromised on one device, it is in effect compromised on all of these devices. An analogy to this would be having all cars of the same make/model being keyed with the same ignition key. This is not ideal. However due to the next vulnerability, this turns out to be pointless anyway.
Hardcoded RC2 Encryption Key CVE-2019-7594
The Metasys UI doesn’t seem to randomly generate the RC2 encryption key used to encrypt the login credentials and other sensitive information. The key is hardcoded in the Java application, which is downloaded to the users computer. It’s very simple to decompile the Java application using something like the Java-Decompiler (which is an awesome tool) and located the hardcoded RC2 encryption key. The fact that the RC2 key is hardcoded, easily available to the user and never changes renders the use of RSA public/private key encryption useless. This is because the RSA key is used to encrypt the RC2 key only, but we already know the RC2 key so we can skip the RSA problem entirely.
The acute reader will notice the hashing algorithm used for the password is problematic. SHA1 is known to be a cryptographically weak algorithm that is routinely broken. I have made liberal use of Crackstation in the past, which has proven very effective against SHA1 password hashes. However as I’ll show in the attack chain below, you don’t need to decode the SHA1 hash. Also, these hashes are not salted.
The exploit I used in this case is essentially a replay attack. Valid encrypted login credentials are captured using your favorite method and then used to login to the BAS system. Normally you cant login using a hash and would need to decode the password. However I can show below that you can replay the hash and login without being able to decode the password.
Capture Login Package
Capturing the valid login session takes a simple Man-In-The-Middle attack. This can be done a number of ways. Compromised router, public wifi, compromising the users computer etc. To simulate a MITM attack I used the fabulous and fantastic Lan Turtle from Hak5. This is a great tool that deserves it’s own post. So without going into detail, the Lan Turtle is acting as a MITM, specifically looking for the login packet and sending the details out to my Command and Control server.
With the encrypted login request in hand, we can use the hardcoded RC2 key to decrypt and recover the plaintext username and hashed password. If the password can be cracked using something like Crackstation, then the job is done. If the password is complex and cant be cracked, there’s one more step to finish the replay attack
Obviously you cannot just type the password hash into the login screen and presumably the password is complex enough to resists hash cracking.
My solution to this was to create a proxy relay server that I used to connect to to Metasys. This proxy would relay all traffic between the Client and Server, but would inspect the requests for the login request. When the login request is seen, the Proxy would replace the username/password I entered with the stolen username/password hash. This would be considered valid by the Server and I would be logged in under the stolen user account.
In this example, the Server is at 192.168.1.10, the Client is at 192.168.3.218 and the proxy relay at both 192.168.3.60 & 192.168.10.229.
The Proxy Relay listens for an incoming connection and relays traffic to the target Server, searching for the login request
In the Wireshark capture below you can see traffic flowing from the Client through the Proxy to the server. Notice the highlighted packets showing the incoming login request and the login request being sent to the Server. Notice the encrypted strings are different, despite using the same encryption keys. This is because the login credentials entered have been replaced by the captured credentials, resulting in a successful login under the compromised user account.
There are few occasions where hardcoded passwords/keys/secrets are a good idea. This is not one of those occasions. It should have been relatively easy for the Java application to randomly generate a new RC2 encryption key with each transmission (sending the key protected by the RSA encryption). Also, I don’t see why the RSA keys have to be common to each device. The RSA keys could be generated when the system is installed, or even on system reboot. These vulnerabilities go to show how important it is to lock down network access to a BAS system, drawing the ring of trust as tight as possible.
Hope this was helpful to anyone reading. If you have questions or would like more information, feel free to contact me.
Note: You’ll notice I’ve neglected to include any actual plaintext keys. This is intentional.