If you’ve ever been inside an airport, university campus, hospital, government complex, or office building, you’ve probably seen one of HID’s brand of card readers standing guard over a restricted area. HID is one of the world’s largest manufacturers of access control systems and has become a ubiquitous part of many large companies’ physical security posture. Each one of those card readers is attached to a door controller behind the scenes, which is a device that controls all the functions of the door including locking and unlocking, schedules, alarms, etc.

In recent years, these door controllers have been given network interfaces so that they can be managed remotely. It is very handy for pushing out card database updates and schedules, but as with everything else on the network, there is a risk of remotely exploitable vulnerabilities. And in the case of physical security systems, that risk is more tangible than usual.

HID’s two flagship lines of door controllers are their VertX and Edge platforms. In order for these controllers to be easily integrated into existing access control setups, they have a discoveryd service that responds to a particular UDP packet. A remote management system can broadcast a “discover” probe to port 4070, and all the door controllers on the network will respond with information such as their mac address, device type, firmware version, and even a common name (like “North Exterior Door”). That is the only purpose of this service as far as I can tell. However, it is not the only function of this service. For some reason, discoveryd also contains functionality for changing the blinking pattern of the status LED on the controller. This is accomplished by sending a “command_blink_on” packet to the discoveryd service with the number of times for the LED to blink. Discoveryd then builds up a path to /mnt/apps/bin/blink and calls system() to run the blink program with that number as an argument.

And you can probably guess what comes next.

A command injection vulnerability exists in this function due to a lack of any sanitization on the user-supplied input that is fed to the system() call. Instead of a number of times to blink the LED, if we send a Linux command wrapped in backticks, like `id`, it will get executed by the Linux shell on the device. To make matters worse, the discovery service runs as root, so whatever command we send it will also be run as root, effectively giving us complete control over the device. Since the device in this case is a door controller, having complete control includes all of the alarm and locking functionality. This means that with a few simple UDP packets and no authentication whatsoever, you can permanently unlock any door connected to the controller. And you can do this in a way that makes it impossible for a remote management system to relock it. On top of that, because the discoveryd service responds to broadcast UDP packets, you can do this to every single door on the network at the same time!

Needless to say, this is a potentially devastating bug. The Zero Day Initiative team worked with HID to see that it got fixed, and a patch is reportedly available now through HID’s partner portal, but I have not been able to verify that fix personally. It also remains to be seen just how quickly that patch will trickle down into customer deployments. TippingPoint customers have been protected ahead of a patch for this vulnerability since September 22, 2015 with Digital Vaccine filter 20820.