FingerTec is a company that offers time attendance and door access hardware and solutions. MWR identified vulnerabilities in their access control biometric devices that can be abused to achieve the following:
All of the above can be achieved by an adversary who can communicate directly with the device over a TCP/IP network. The communications are sent using unencrypted UDP datagrams using an undocumented proprietary messaging protocol.
An adversary could leverage the issues to gain access to restricted areas in buildings protected by such a system and/or affect the integrity of any audit trails.
With most traditional access control systems, the readers; whether they are RFID, biometric, PIN pad, etc, connect via a serial connection to a master controller in a centralized system. The controller however does all of the processing, and controls unlocking and locking doors and also keeps an audit trail.
The FingerTec system works differently, the logic and the processing is performed by the device. When a user is validated and authorized for access on the FingerTec device, a signal is sent directly to the power supply, which then unlocks the door. The FingerTec device downloads the authorized user database from a central server and stores it locally.
On January 12th, 2016 (Packet Storm) I published an advisory that disclosed the hard coded default root password for the device. This was identified during my initial research into its attack surface. At the same time I also reverse engineered the user database (user.dat) format. The database contains user names, IDs, PINs, and RFID numbers. At the time I was unable to reverse the proprietary UDP protocol in use for network communications.
I've since had time to revisit this research and this blog post details the approach taken, what was achieved, and the issues identified.
To approach the challenge of understanding the protocol, I set about methodically executing functionality in order to trigger network communications that could be recorded in a full packet capture.
The server was found to attempt a connection with the controller device over TCP port 4370. However on the devices assessed, this service was not listening, so the connection would predictably fail. It then reverts to communicating with the device using UDP sending datagrams to port 4370. The server always sends the same 8 byte initialization packet:
The response from the device was similar, but different every time. After comparing several different packet captures, I saw that the first 8 bytes of the packet were the command header, and anything occurring after was either data or arguments.
Request Made Response From Reader e80317fc00000000 d007c2016df60000 4c0445056df60100 d00751a66df60100 0b00b3b96df60200 d007bd746df60200 e80317fc00000000 d0073466fb910000 4c04b769fb910100 d007c40afb910100 0b00261efb910200 d0072fd9fb910200
After receiving an initialization message from the server, the device replies with a response code, checksum, session ID, and a sequence number. Some examples are below.
Initialization string e803 17fc 0000 0000 Response Checksum Session ID Sequence d007 c201 6df6 0000 Command Checksum Session ID Sequence 4c04 4505 6df6 0100 Response Checksum Session ID Sequence d007 51a6 6df6 0100
It took me way longer to figure out that the second set of numbers was a checksum. I got lucky and found someone had posted the source code for the ZK Software API on GitHub, which also suspiciously communicated over port UDP 4370. After reading through it, I determined that it was the same protocol that the FingerTec devices were using, and it contained the source code on how to calculate the checksum.
I wrote a Proof of Concept (PoC) tool that allowed me to generate basic communications in order to allow me to begin mapping out the various commands. However the packet captures were not easy to work with. The raw UDP data was ugly, making it difficult to identify any meaning to the giant mass of packets. The capture also contained multiple simultaneous sessions. Visually this made it difficult to track packet order. In order to make my life easier, I wrote a Python script to parse the PCAP files, sort the packets by session and then print everything out in a much more readable format. It was then much easier to map out some of the basic commands.
The following capture was obtained when using the software to copy a user to a device:
Having previously reverse engineered the database format, the big chunk of binary data looked familiar to me. These are database rows in binary data.
Here's the layout:
If we ignore the third row, since it was malformed, the data is laid out like this:
Replaying the exact same packet sequence creates the same user again. The user name and PIN values can be modified and the message re-sent, to insert new users (unauthenticated).
The image below shows a capture generated when a list of users is requested from the device. The server receives a complete user database containing user names, user IDs, PINs, and RFID numbers.
As can be seen above, after the first 4 bytes of data, the message is a byte by byte dump of the entire database. Our understanding of the protocol and ability to craft messages to be sent to the devices in such a network means that we are able to:
These issues were disclosed to the vendor. During the disclosure communications MWR was informed that FingerTec does not have any control over most of the software stack on the device. The device is manufactured and branded for FingerTec by a company called ZKTeco. Therefore the issues discovered are not isolated to FingerTec, but could potentially affect all ZKTeco and ZKTeco based TCP/IP enabled devices.
It was also found that the devices do support authentication. While the device manuals recommend against setting the code, the software manual recommends setting a 5 digit number.
This is referred to as the Comm Key. It can be set with a number between 0 and 999999. When set to anything other than 0, every request is met with a response code of 2005, which translates as 'Authentication Required'.
A packet capture containing multiple successful authentications with a Comm Key was analyzed. It was found that the authentication codes were unique with each authentication response. Since the Comm Key was the same (the number "2"), the only difference between the two was the session key.
After searching around and not finding anything about how the actual code is generated, and after trying various 32-bit checksum/hashing functions with various permutations of "2"+session_key without success, a new approach was needed. The server software was loaded into x86dbg and an attempt made to identify the function responsible for hashing the Comm Key.
A number of DLLs were dynamically loaded and unloaded when executing the target function. The DLL 'comms.dll' was found to be a good place to start. After several runs and different breakpoints being set and analyzed, the function that generated the password was identified.
The function does the following:
The end result is the actual pass code:
Which was verified in Wireshark:
Steps 5 and 6 are just meant to add a little randomness to the hash, and are completely optional. You can just not do step 5, and replace position 3 with 00 and it works just as well.
The hashing mechanism is very weak is due to the very limited keyspace. It has at most 3 bytes of password space, which works out to 2^24, or a maximum possible 16,777,216 passwords. On top of that, the software allows a maximum number of 999,999. That then means 10^6, or approx. a million passwords to enumerate. There is also no brute force protection or rate limiting.
Understanding the algorithm and the proprietary protocol in use allows us to automate a brute force of the key space. A tool was crafted with the following capabilities:
Going sequentially, it takes a little under 3 days to exhaust the entire available keyspace for the AC900s, so an average brute force attempt should be half of that. Multithreaded brute forcing locks up the device. The R2s are a little more powerful, and should be faster to brute force, however in the course of other research avenues, those devices have been bricked, so we are unable to test.
According to Shodan, there are over 4000 of these devices exposed on the internet. These devices are mainly concentrated in the US and China, but have a wide spread over the rest of the world.
According to ZKTeco:
"More than 220 million people use ZKTeco products in approximately 180 countries/regions every day. ZKTeco has become a well-recognized, respected and sought after brand in the security and biometric industries."
The disclosure timeline was as follows:
Note the nature of the fix was not disclosed to MWR and a device with the implemented fix has yet to be analyzed. Therefore the effectiveness of the fix is as of yet, unknown.