Introduction
Smartair is a access control system made by an Assa Abloy subsidiary based out of Spain called Tesa
It's primarly meant to be an offline system where the communication between the door and the database is intermitten and done through credentials and programming tools only.
Making it look something like this.
+---------------+ +----------+ +----------+
|DATABASE SERVER| |CONTROLLER| |CONTROLLER|
+---------------+ +----------+ +----------+
^ ^ ^
| | |
| | |
| | |
v v v
+---------------+ +------+ +------+
| ENCODER | |DOOR 1| |DOOR 2|
+---------------+ +------+ +------+
This is a collection of the research we have conducted. These vulnerabilities was reported in 2021 and a upgrade path to patch these vulnerabilites have been available from Tesa since early 2022.
The intentions of this repo is to document all the research that was done to increase the awareness and interest by both product users/owners but also other researchers.
Smartair card formats
The installation gets programmed with an "Entry point" to the User sector in the card using the programming tool.
The rest is defined by the data in the card.
User sector format
| X | 0xX0 | 0xX1 | 0xX2 | 0xX3 | 0xX4 | 0xX5 | 0xX6 | 0xX7 | 0xX8 | 0xX9 | 0xXA | 0xXB | 0xXC | 0xXD | 0xXE | 0xXF |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x0X | Card Config | LICSENSE? | SITE ID | .. | .. | .. | ? | User ID | .. | 00 | Encoding Time | .. | .. | .. | 00 | Valid From |
| 0x1X | .. | .. | Valid Until | .. | .. | Pin code | .. | .. | UserSettings | UOC Block ADDR | .. | .. | Grants | .. | .. | .. |
| 0x2X | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
Decoded
CardConfig:
[Enchanced encryptage info](Enhanced Encryptage.md)
0: Enhanced encryptage
1: ?
2: ?
3: ?
4: ?
5: ?
6: ?
7: ?
Licsense? We don't know but we assume it's config based on the licsense given from Tesa
SiteID: The site id which is embedded in the licsense from Tesa
UserID: A 2 Byte sequential increasing number which increases for each cardholder added in the TS1000 software
Encoding time 4 Bytes with 1 minute accuracy
Valid from/until: 3 Byte dates with 10 minute resolution
The time format is roughly like this but we do have some unknowns in the extended times used for encoding time.
Time formats
Year = 2000 + uint7
Month = uint4
Day = uint5
Hour = uint5
Minutes can be with 10 minute resolution used for valid from/to or seconds(A bit unsure exactly how much resolution) resolution used for encoding time.
Minutes *10 = uint3
# These are off so need more research into how these are calculated
Minutes = uint6
Seconds = uint5
Fork('\\n','\\n',false)
From_Hex('Auto')
To_Binary('None',8)
Find_/_Replace({'option':'Regex','string':'^([0|1]{7})([0|1]{4})([0|1]{5})([0|1]{5})(([0|1]{3})|(([0|1]{5})([0|1]{6})))$'},'$1\\n$2\\n$3\\n$4\\n$6\\n$8\\n$9',true,false,true,false)
Fork('\\n',',',false)
From_Binary('Space',8)
To_Decimal('Space',false)
Merge(true)
Find_/_Replace({'option':'Regex','string':'^(.*)$'},'Year,Month,Day,Hour,Minute*10,Minute*1,Seconds\\n$1',true,false,false,true)
To_Table(',','\\r\\n',false,'ASCII')
PinCode: 3 bytes with the users pin code terminated with FF if the pin is only 4 digits
UserSettings(0x18): Seems to act somewhat like "User flags" or similar
0: ?
1: Disabled user(Extended opening time)
2: ?
3: Audit 1?
4: ?
5: ?
6: Audit 2?
7: ?
Audit 1/2 are assumptions they seem to be related to the "Cardholder collects audit trail" and "Do not open if audit is full"
UOC Block:
Reversed endianess of the location where the Update on card config block is located
00 c0 = 00 0c = 12 = Block 0 Sector 3
Grants: Each grant is assigned a bit position so a reader configured to accept grants just checks expirations of the card and that the bit configured has been set on the card presented used for things like Pool access, Elevator floors or other generic functions you want to allow many people with a credential to do without keeping a big locking plan in the lock
UOC Sector
This sector tells the lock where to look for access plan changes and where to write log entries
| X | 0xX0 | 0xX1 | 0xX2 | 0xX3 | 0xX4 | 0xX5 | 0xX6 | 0xX7 | 0xX8 | 0xX9 | 0xXA | 0xXB | 0xXC | 0xXD | 0xXE | 0xXF |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x0X | ? | ? | ? | ? | ? | ? | ? | Door Block Start | .. | Number of entries | .. | ? | ? | ? | ? | ? |
| 0x1X | ? | ? | ? | ? | ? | ? | ? | Door Block Start | .. | Number of entries | .. | ? | ? | ? | ? | ? |
| 0x2X | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | ? | ? | ? | ? | 00 | 00 | 00 |
Door Block start is in reverse endianes again
For some reason the first block is duplicate we don't know why
Door sector (Access plan)
| X | 0xX0 | 0xX1 | 0xX2 | 0xX3 | 0xX4 | 0xX5 | 0xX6 | 0xX7 | 0xX8 | 0xX9 | 0xXA | 0xXB | 0xXC | 0xXD | 0xXE | 0xXF |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0x0X | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate |
| 0x0X | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate |
| 0x0X | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate | DoorID | .. | Schedule | ShouldUpdate |
DoorID: Just a sequential number of what door this access is for
Schedule: Split into 2 parts First nibble is what Schedule this access should be valid for 0-F where 0 = Never and F = Always 1-E are globally configured in the access control software and loaded into the lock using a programming tool
The second nibble is bit bools with extra grants like the ability to leave a door unlocked by tapping the card twice or override privacy
Should Update is 0x01 when the encoder adds a new permission and gets set to 0x00 when a lock "Reads" the change and adds it to it's local memory.
Mifare KEYS
For mifare classic the cards use a Site Specific key as keyA the last 4 bytes of the key is the SiteID and the readers auth with KeyA making it possible to mint a valid credential by using mfkey32 on a reader.
key B is static and the same across all sites E7 31 68 53 E7 31