As pointed out by previous article “BACnet MSTP frame lost synchronization” , BACnet MSTP has a design flaw on frame synchronization, but how to utilize it to perform attack and strictly obey the standard at the same time?
We make some assumptions here:
- There are at least 3 devices on the bus with MAC address 1, 8,10. The device 1 is carefully designed to perform attack. Device 8 and 10 are innocent.
- Device 1 supports extended frame, device 8 and 10 are not.
- The timers of 3 devices is precise enough.
The work flow of device 1 is:
- When get token, send out frame A
- Pass token to MAC address 2
- When get token again, send out frame B
- Pass token to MAC address 2
- goto step 1 again.
Frame A is a valid proprietary frame (hexadecimal);
55 ff 80 ff 01 00 1d a3 02 2b 72 fe 55 ff 03 08 01 00 11 a0 ff 55 ff 21 01 08 00 09 ce d4 f3 55 ff 00 01 08 00 00 bf
Frame B is also a valid proprietary frame as:
55 ff 80 ff 01 00 1d a3 02 2b fe dc 55 ff 03 0a 01 00 11 b1 ff 55 ff 21 01 0a 00 09 fd 8a 51 55 ff 00 01 0a 00 00 8c
Every thing will go well if there is no frame desynchronization, but after hours running, if device 8 losses synchronization with frame A header (It has same effect if device 10 losses synchronization when device 1 sends frame B) , device 8 find another frame when scan Frame A’s data portion:
55 ff 03 08 01 00 11 a0 ff 55 ff 21 01 08 00 09 ce d4 f3 55 ff 00 01 08 00 00 bf
It’s a Test-Request frame send to device 8, so device 8 try to reply it after Tturnaround with a Test-Response frame:
55 ff 04 01 08 00 11 ae ff 55 ff 21 01 08 00 09 ce d4 f3 55 ff 00 01 08 00 00 bf
but at the same time, device 1 passes token by sending:
55 ff 00 02 01 00 00 73
So the first 8 bytes of two frames are collided, so device 10 drop invalid header, find data as below:
55 ff 21 01 08 00 09 ce d4 f3 55 ff 00 01 08 00 00 bf
When device 1 finishs sending, it starts receiving data and get the same as:
55 ff 21 01 08 00 09 ce d4 f3 55 ff 00 01 08 00 00 bf
For device 10, it get a valid Not-For-Us frame header, so it enter SKIP-DATA state, there is not enough data to skip, so device 10 will wait until Tframe_abort.
For device 1, it’s a BACnet-Extended-Not-Expecting-Reply frame header, because it support extended frame, so it validate header by procedure described in Addendum 135-2012an. Because the data length is too short, so it abort the frame enter IDLE state again, then find another frame:
55 ff 00 01 08 00 00 bf
It’s a token frame passing token to device 1, so device 1 get token then sending Frame B just after Tturnaround:
55 ff 80 ff 01 00 1d a3 02 2b fe dc 55 ff 03 0a 01 00 11 b1 ff 55 ff 21 01 0a 00 09 fd 8a 51 55 ff 00 01 0a 00 00 8c
As mentioned above, Device 10 still wait 1 byte to skip previous frame (because Tturnaround < Tframe_abort), so it miss this frame header. get wrong frame as:
55 ff 03 0a 01 00 11 b1 ff 55 ff 21 01 0a 00 09 fd 8a 51 55 ff 00 01 0a 00 00 8c
It’s another Test-Request frame send to device 10, thing repeats.
From above, every devices strictly obey standard, but once frame desynchronization occurs, the whole MSTP bus is stalled forever.
Read more on Solution to mstp frame desynchronization