Santiago Chile Feb 2019
Bertin b @bertinjoseb
The BACNet protocol is UDP based and contains 3 main headers the BVLC , NPDU and APDU the interesting thing here is that BACNet has read and write capability to modify data in the BACNet data structure running usually in building automation devices like controllers protocol converters etc etc .
Usually those devices comes with a web server running in popular ports in order to monitor , perform configuration changes , read data and other stuff.
The web application typically use the BACNet data structure in order to print information in the web application about the BACNet device status/name/location and other settings.
The cool thing here is you’re going to trigger a stored XSS without touching the web application at all, for this attack/technique you just need to understand the BACNet protocol fundamentals and have access to the specific scenario with all requirements in order to trigger the stored XSS using the BACNet write properties.
Important things you need to consider in order recreate this scenario :
- A BACNet port open UDP 47808
- A web application printing BACNet data ,sometimes you have Bacnet open port but no web app :-(
- Web application research, sometimes you need to break tags in order to trigger the XSS (you know what i mean….).
- Write capability in location and device name, sometimes there is no write permissions
With all that explained you’re ready to go, for this time i will share a exotic vulnerable device connected to the internet that i found with all requirements explained previously , the first time i try this technique was in demo environment, this time is a real device, s**t got real.
Our lab :
Fire up your favorite search engine
In my case zoomeye works fine
Looks for “pCOBCM@”, zoomeye currently is indexing port 47808 and also retrieving the bacnet data .
This is a McQuay device, “bacnet controller”, related to chillers and stuff like that.
As you can see there is nothing in the web application, just visually , i mean, there is no access at all , there is no info but…..check the source code and you will find gold .
Well, ignore the reset password, is not related to our purposes today ,was just fun , but as you can see our target is running bacnet and also is printing bacnet sysinfo string values , in theory properties of the device object in bacnet .
In order to write values you must know first how to read them and how bacnet works.
So, it’s really important to read the BACNet documentation in order to understand how the protocol works and all that stuff, as i said before there are 3 main headers , the following could become complex in terms of the protocol implementation and development and could mislead the perspective of readers / BACNet experts and the people who wants to understand how the exploit works , i will try to cover the most relevant parts and theory of the protocol in order to exploit the web application , so many things are going to be straight forward and in case you want to explore more in depth i think this is a good point to start.
The firts one is called : BVLC (BACNet virtual link control) , basically 4 bytes
The firts byte defines the type , in this case bacnet/ip 0x81 , the second one defines the function 0x0a , and the last 2 bytes defines the length of the whole packet, for this example our packet is 17 bytes.
The second header is called NPDU (Network layer protocol data unit)
According bacnetwiki this is the representation, but be careful here, in practice the headers is just 2 bytes, the version and the control byte
The last header is the APDU , BACnet APDUs carry the Application Layer parameters with a variable length, the length of the payload also must be specified , be careful with that , otherwise you will face malformed packet issues .
The magic is in the APDU header that allows you to read or WRITE values in the BACNet data structure.
Bacnet service choice
In order to read / write you need to set up correctly the service choice byte, at this point probably you’re taking a look to the 0x14 byte (reinitialize device) but we will cover that other day , so in order to read would be 0x0c , for write would be 0x0f .
In order to read , to need to use the byte 0x0c (12 Dec)
The values that we need to read and write will be the BACNet objects.
BACNet Object (bacnetwiki):
Unique in a given device. Not necessarily internetwork wide. The Object Identifier of an object in combination with the Object Identifier of the Device is what makes an object unique. Value of 4194303 is not allowed. A value of 4194303 used anywhere as an Object Identifier means that the containing object is not initialized.
The object identifier contains the property identifier , basically the string values representing human readable strings like location, device model, device name and so on, in few words ..our targets today.
There are several tools available you can use in order to read and write with BACNet, but the purpose of this post is go in depth and interact with the device using no tools. Any language that feel comfortable is useful in this scenario, python or C helps a lot.
Now we know the components and how the read property works, let’s try our first attempt to read some BACNet string values, in order to do that we can go back to our BACNet device from zoomeye.
The byte structure in order to read BACNet values (object identifier)would be the following :
Why the object identifier ? well.. because in order to write later you will need to object identifier .
Let’s check the response , if everything is fine at this point you will notice the communication with the BACNet device is successful , also the response back contains the specific value for the object identifier , keep this value in mind, we will use it later in order to write.
Now we know how to read specific values form the BACNet data structure, but the cool thing is that we can modify those values, and also the same values are being taken from the application to represent sting values, in theory, if we modify the string values for Javscript code instead we could be able execute Javscript in browser.
The write property is tricky, sometime some objects are restricted to be modified, but most the times the property value location is able to be modified from the bacnet protocol. Also in order to write you need the device id (mandatory), remember that one in the read packet above? yes that one.
Open up the source code ,check again the value of the location tag in the device, our goal is the following : the string “Unknown” will be replaced for JS code, the result will be a stored XSS in the device web application , in <td></td> there is no need for special XSS payload, a single alert in script tags is enough to trigger the XSS.
This is an example about how the write packet should looks like, you clearly see the payload in location property and the service choice is set to 15 , which means writeproperty , if everything goes well the server should reply with an simple ACK like this one :
The payload has been wrote successfully in the BACNet data structure, in good theory now our payload should be executed in the browser .
in this particular scenario we don’t need credentials or access to the web application at all , just inject the payload and say goodbye.
Several protocols are also affected by this kind of vector, example : SNMP.
Several BACNet devices with the port 47808 are vulnerable to be modified remotely using the BACnet protocol .
The Number of current BACnet devices affected by this technique is unknow at least for me.
Close your BACNet port 47808 in the internet otherwise i will inject you stuff… or others actors.
There are several tools to read and write, i decided to explains in depth becacuse we need to understand how exactly the BACnet protocol works.
If you experience issues trying to reproduce the vulnerability this way , check your packet lengths, or operation codes.