motivations and disclamation
With friends' consent I acquired dozens of campus card data, together with my own historical data, I can finally pull off some analysis into how data are stored within.
The investigation is out of pure interest, no private or public insterest is gained/damaged during the process.
For secuity concern, no real data will be disclosed.
the uid card structure
A typical uid card has 16 sectors, each with 64 bytes data. Sector 0, or the manufacture block, is usually read-only after initial writing (but there are ones special ones made rewritable). The last 16 bytes of each sector are used to store keys and permissions, thus the first 48 bytes of last 15 sectors are for data storage.
offline door access
The campus dorm, and possibliy all hotel doors, due to a lack of internet access, needs to verify access in an offline manner. One can guess what information are stored within the card. A permission check mechanism will verify if the card has required permission, and a time check mechanism will verify if the card is not expired.
Being offline should not become a security concern as encryption technology is quite developed nowadays.
Well, this turned out not the case. A simple and cheap PN532 controller can hack into a NFC card in several minutes. This should not become a security either, as encryption can still be applied to the application layer.
Well, the engineers developed my campus dorm security system, did put into some encryption, but turned out quite simple. Within several hours, I guessed out the encrytionn algorithms and successifully modified my card expriation datetime.
how?
A naive starting step would be a simple diff between two states of the card, with only datetime modified.
Some simple obervation shows only certain bytes in sector 8 changed (5 consecutive bytes and 1 additional byte) in all my cards. These bytes shows several important features:
- the position of the changing bytes is fixed, meaning no global hash algorithm is employed, and the encryption preserve data order, like a xor algorithm
- the 5 consecutive changing bytes shows certain continuity across changes, but does not guarantee mono increase
- the 1 additional byte shows few continuity
- all data in sector 8 are completely different for different users
- sector 8 and 9 has certain persistant consecutive 16 bytes of exact same value, although completely different for different users
Now consider the funciton to store datetime. The best idea seemed to me, is a unix timestamp, requiring a int32, quite close to the changing 5 bytes. However this is not immediately verifiable.
We know the lock need to have a way to decode the data. Should every lock has a different key (that is known only to the lock and the management system), I will never be able to guess the key. But it turned out that I am lucky. A simple bitwise xor of certain bytes yields highly similar results between users. This means the exact value of sector 8 and 9 are served as encryption keys, only to disclose due to xor 0, or is simply stored there with a lack of security concern. Anyway, the first layer of encryption is revealed.
Now to observe the decrypted 5 conseutive bytes. Now these data still shows certain continuity across changes, but does not guarantee mono increase over time.
With observation, some patterns begin to reveal.
- the first byte has only 3 possible values, '0x1b', '0x1a', '0x1d', remarkably consistant with the general year of expiration (in a strange hex form), i.e. '0x22', '0x23', '0x24'.
It turns out this is just another xor encryption but with a hidden key '0x39'.
I was wondering what could the remaining key be, and was already trying to analyze the data (see, the month will have certain patterns, that most will start with '0' with just Oct, Nov, Dec as exception, same applies for date), but I soon discovered that the entire datetime encryption key is simply '0x3939393939'.
Now the final 1 mysterious byte. Due to its complete randomness, I was wondering if it could be a checksum.
With large amount of data, I discoverd that, the same sum of date time will always yield the same 1 byte value (for the same user, and not exact value, but this is enough). So it may be a checksum after all.
A later test proved my guess, that vilating the checksum will cause problems, not otherwise.
some advice
- use salted hash as a security measure