Yesterday, Tenable™ released two plugins to detect macOS High Sierra installs which allow a local user to login as root without a password after several login attempts. Both plugins require authentication, however, there was one scenario where a user could log in over VNC protocol with the root account and no password if screen sharing was enabled. Today, we are releasing a plugin to remotely detect the vulnerability without authentication.
Confirming the Vulnerability
One of my colleagues initially reported that exploitation was possible remotely over VNC after trying against his personal laptop. To confirm the report, I fired up tightVNC (an open source VPN server/client) and tried to exploit the issue on an lab box with “Screen Sharing” enabled (see screenshot below). I ran into a problem were tightVNC couldn't connect to OSX (more on this later). I then tried another VNC client, realVNC, and was able to successfully exploit the issue. After two attempted logins with root and a blank password, the VNC client drops you to a desktop on the remote host, as root. Now it’s time to look into the VNC protocol, and figure out how we can write a remote check for this!
Delving into the VNC Protocol
Anytime you want to learn a new protocol, a good place to start is the RFC. The RFC for VNC can be found here: https://tools.ietf.org/html/rfc6143
The RFC refers to the protocol as RFB (remote frame buffer). In order to exploit the vulnerability, we need to figure out how to perform authentication over the VNC protocol. The first step is to connect to the host and receive a banner, which looks something like this:
RFB protocol version = 3.889
Next, we send a similar banner string to the server (ending in a new line), and receive a response for the server that contains the supported authentication types (response decoded below):
Server Auth Types: 30,33,36,35
The RFC doesn’t mention anything about these authentication types. I needed to figure out what these were, so I loaded up the debug log for realVNC and saw the following:
2017-11-30T00:45:25.175Z TNS5872L vncviewer: Child: 12148: CProtoPreV5: processing security types message 2017-11-30T00:45:25.175Z TNS5872L vncviewer: Child: 12148: CProtoPreV5: Server offers security type Ard(30) 2017-11-30T00:45:25.175Z TNS5872L vncviewer: Child: 12148: CProtoPreV5: Server offers security type [unknown secType 33](33) 2017-11-30T00:45:25.175Z TNS5872L vncviewer: Child: 12148: CProtoPreV5: Server offers security type [unknown secType 36](36) 2017-11-30T00:45:25.175Z TNS5872L vncviewer: Child: 12148: CProtoPreV5: Server offers security type [unknown secType 35](35)
So realVNC didn’t know what any of these types were, other than type 30, which they have labeled as Ard. A few Google searches later and we found that Ard stands for “Apple Remote Desktop”. No wonder tightVNC didn’t work, as the application only supports the RFC standard authentication types.
To use that type, we send security type 30 (0x1E) to the server, and extract the response which contains parameters for the authentication. Here is this response in wireshark:
Looks like Diffie Hellman! The generator value is always two bytes and is first in the packet. The key length is next and is a two byte integer. The prime modulus and public key follow and are the same size as the key length. So far, the debugging output of the plugin outputs this:
RFB protocol version = 3.889 Server Auth Types: 30,33,36,35 Doing apple auth! ARD Material: Generator : 0002 Key Length : 128 Prime Modulus : ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece65381ffffffffffffffff Public Key : a1ef2769ecfa51e2913751a3c51e3fabde0732466915fe0f65cf0aa61f468a929850717f4258a9449da3ba92e3a7ab07d12bb503ea34f079c98837c40dce8cfd123c3bf6ffbef49c6ea42abda1a80d317bd001dc6545da46d4697b5b90a26ef5f859983c2c0b4f09d29883344b05da3222ee268460687c2d8544df62cb2f49b5
So, the obvious thing we need to do is generate our own DH key-pair and calculate the shared secret. What’s next?
After a bit more Googling and wireshark analysis, we learn that the username and password are sent using AES encryption (using ECB mode), with the key being the MD5 value of the shared secret. The username/password are sent in a 128 byte blob. The first 64 bytes is for the username, the last 64 is for the password. The username and password are null terminated and the remaining space is padded with random bytes. Here is an example of the blob with username admin, password FooBar12 (before we encrypt the data):
To complete the authentication, we need to send the AES encrypted password blob, plus our public DH key and the server will send a four byte integer to indicate success/failure. 0 is success, 1 is failure. Now we can write our remote check!
Exploiting the Vulnerability
Exploiting the vulnerability is easy. We try to log in using root and a blank password multiple times (four times max) using the process described above. If we are successful logging in, then the remote host is vulnerable.
The Nessus® Plugin
Plugin 104885 was created to exploit the issue remotely over VNC. You must have “safe checks” disabled in order for this run to plugin. This is because successful exploitation will enable the root account, so there is a bit of cleanup involved afterward if you find any affected boxes with the plugin. You need to both disable the root account, and patch the underlying vulnerability.