Research, development and trades concerning the powerful Proxmark3 device.
Remember; sharing is caring. Bring something back to the community.
"Learn the tools of the trade the hard way." +Fravia
You are not logged in.
Time changes and with it the technology
Proxmark3 @ discord
Users of this forum, please be aware that information stored on this site is not private.
Pages: 1
Hello Everyone!
First, I want to thank Iceman, mosci, jason, and ikarus, for all the time and work they've invested in Proxmark firmware and scripts.
I've learned so much new in the last few weeks and months.
I've got some LegicPrime-Tags used for a car park and try to understand all data stored on this tags.
After much research I know almost all bytes, CRC calculations and how they are used, but not all.
(Sources: http://sar.informatik.hu-berlin.de/rese … 11-03_.pdf, https://fahrplan.events.ccc.de/congress … slides.pdf, Mosci's Legic-Lua-Scripts and legic-threads of this forum)
Here's a Dump of a Tag:
[usb] pm3 --> hf legic info
[+] Reading full tag memory of 256 bytes...
CDF: System Area
------------------------------------------------------
MCD: 79, MSN: 03 ee 7e, MCC: df OK
DCF: 60000 (60 ea), Token Type = IM-S (OLE = 0)
WRP = 15, WRC = 1, RD = 1, SSC = FF
Remaining Header Area
00 00 00 11 01 06 80 00 00 4C 50 00 00
------------------------------------------------------
ADF: User Area
------------------------------------------------------
Segment | 01
raw header | 0x74 0xC0 0x07 0x40
Segment len | 116, Flag: 0xC (valid:1, last:1)
| WRP: 07, WRC: 04, RD: 0, CRC: 0x85 (OK )
WRC protected area: (I 27 | K 0| WRC 4)
row | data
-----+------------------------------------------------
[00] | 50 38 0B 00
-----+------------------------------------------------
Remaining write protected area: (I 31 | K 31 | WRC 4 | WRP 7 WRP_LEN 3)
row | data
-----+------------------------------------------------
[00] | 03 B3 22
-----+------------------------------------------------
Remaining segment payload: (I 34 | K 34 | Remain LEN 104)
row | data
-----+------------------------------------------------
[00] | FF 92 00 00 03 B3 22 00 00 00 00 00 00 00 00 00
[01] | 00 00 00 FF FF FF FF FF FF FF 57 02 85 26 FC 01
[02] | 01 00 00 64 00 00 00 00 00 85 26 3C 04 F4 01 00
[03] | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[04] | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[05] | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
[06] | FF FF FF FF FF FF FF FF
-----+------------------------------------------------
...and Raw-Data, I like more
ADR |0x0|0x1|0x2|0x3|0x4|0x5|0x6|0x7|0x8|0x9|0xA|0xB|0xC|0xD|0xE|0xF
-----+---------------------------------------------------------------
0x00 | 79 03 EE 7E DF 60 EA 9F FF 00 00 00 11 01 06 80
0x10 | 00 00 4C 50 00 00 74 C0 07 40 85 50 38 0B 00 03
0x20 | B3 22 FF 92 00 00 03 B3 22 00 00 00 00 00 00 00
0x30 | 00 00 00 00 00 FF FF FF FF FF FF FF 57 02 85 26
0x40 | FC 01 01 00 00 64 00 00 00 00 00 85 26 3C 04 F4
0x50 | 01 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0x60 | ...
What I know atm:
UID:
MCD: Byte 0x00 => 79
MSN: Byte 0x01..0x03 => 03 EE 7E
UID-CRC: Byte 0x04 = CRC8 over Byte 0x00..0x03 => DF
DecrementalField DCF:
OLE (Organization Level Enabled-Flag): DCF-Lo-Byte 0x05 Bit 7 => binary 0
TokenType: DCF-Lo-Byte 0x05 Bits 0..6 => binary 1000000 = 0x60
(0x00-0x2f = IAM, 0x30-0x6f = SAM, 0x70-0x7f = GAM)
Organization Level: DCF-Hi-Byte = Byte 0x06 => EA
0xEA 0x60 => so it's a SAM-Token
Byte 0x07:
"FlagByte":
WRP (WriteProtection): Bit 0..3 => binary 1111 = decimal 15 = 0xF
WRC (WriteControll): Bit 4..6 => binary 001 = decimal 1 = 0x1
RD (Read Disabled): Bit 7 => binary 0 = decimal 1 = 0x1
Bytes 0x08..0x0C are unkown, but important???
Bytes 0x0D..0x13:
Segment-Header-Backup:
According to http://sar.informatik.hu-berlin.de/rese … 11-03_.pdf i was able to identify how to calculate the CRC on Byte 0x13. The CRC-Calculation must be done with DIRT-Flag-Set:
So CRC-Calcutlation is done over:
[MCD][MSN0][MSN1][MSN2][MSN3] + Byte 0x0D but BIT7 (=DIRT-Bit) MUST SET FOR CALCULATION = 8, BIT 0-6 is Seg-Nr (max Seg = 127) + Bytes 0x0E..0x12
[offline] pm3 --> hf legic crc d 7903ee7e81068000004c
[+] Legic crc8: 50 <=== Byte 0x13
Bytes 0x14..0x15:
Timestamp (Week/Year) for Token but not used on SAM-Tokens
Bytes 0x16..0x1A:
Segment Header with CRC:
CRC calculated over [MCD][MSN0][MSN1][MSN2][MSN3]+0x16..0x19
[offline] pm3 --> hf legic crc d 7903ee7e74c00740
[+] Legic crc8: 85 <=== Byte 0x1A
Segment Header contains following informations:
Segment-length (including Header):
Byte 0x16 = Lower Byte of SegLength => 0x74
Byte 0x17 Bits 0..3 = High Nibble of SegLenght => 0x0
so SegmentLeght = 0x074 = 116 Byte
Valid-Flag (if not set, segment has been deleted):
Byte 0x17 Bit 6 => 1
Last-Segment-Flag (if bit is set, no more segments are following):
Byte 0x17 Bit 7 => 1
WRP (WriteProtection, amount of bytes of WriteProtected Area???):
Byte 0x18 => 0x07
WRC (WriteControl, amount of Bytes of WriteControlled Area???):
Byte 0x19 Bits 4..6 => Byte 0x19 => 0x40 = binary 010000000 so WRC = 100 = 4
RD (ReadDisabled):
Byte 0x19 Bit 7 => 0
So far, all these bytes are known and have been discussed several times in the forum.
Now the fun-stuff:
Bytes 0x1B..0x1E:
WRC-controled area = Stamp
Bytes 0x1F..0x21:
Remaining Bytes of WRP-protected area.
I figured out that byte 0x21 is the LowerByte and 0x20 is HigherByte of cardnumber printend on this tag.
0x22B3 = decimal 8883
Printed Number on Tag: 8883
Byte 0x1F may could also belong to this cardnumber...
I'm sure that these bytes contain the card number, since on another card the bytes contains exactly the printed number.
So i think it isn't a default Kaba-Header, cause there's no more space left in WRP-Area for the khCRC.
And now the really interesting stuff, the payload:
Byte 0x22 0xFF on all tags
Byte 0x23 different on other tags, maybe a CRC, but i can't figure out which bytes are included in calculation
Bytes 0x24..0x25 0x00 on all Ttags
Bytes 0x26..0x28 are the same value as bytes 0x1F..0x21 on every tag. It's the card number again.
Bytes 0x29..0x34 0x00 on all tags
Bytes 0x35..0x3B 0xFF on all tags
All Bytes before 0x3A are never changed when tag was used.
Byte 0x3C unknown, changed every time the tag is used randomly(?)
Byte 0x3D unknown, but i think it's the number/type of reader, last used to change data on tag. If I used the Cash-Upload-Terminal, this Byte is set to 0x03, if I entered the car park, it was set to 0x01 and when i've leaved, it was set to 0x02.
Byte 0x3E unknown, but it's icremented every day. On 2019-10-04 it was 0x7e, on 2019-10-09 => 0x83, 2019-10-10 => 0x84, 2019-10-11 => 0x85
Byte 0x3F unknown, maybe it belongs to byte 0x3F and is a part of the "timestamp"
I think these are the number of days since 1992-10-11. It's calculated by byte 0x3F as hiByte and 0x3E as lobyte. (Why the 1992-10-11? Legic introduced the Legic Prime Standard in 1992)
Bytes 0x40..0x41 Minutes of Day, HiByte = 0x41, LoByte = 0x40 => 0x01FC = 508/60 = 8.46 => 0.46*60 ~ 27.99 => entering car park at 08:28 , maybe this Bytes are part of the "timestamp"
Byte 0x42 unknown, think it would set if the tag is used the first time entring the car park, because it's 0x00 after initial money-upload and set to 0x01 after first time the car park was entered
Byte 0x43 is set to 0x08 after entering the parking lot, after leaving it is 0x00
Byte 0x44 unkown , maybe it's the HiByte of cash?
Bytes 0x45..0x46 Balance of the cash, HiByte = 0x45 , LoByte = 0x44 => 0x0064 = 100 = 1.00€
Bytes 0x47..0x4A 0x00 on all tags
Bytes 0x4B..0x51 0x00 after initial cash-upload and after entring the car park, so there is NO CRC to check the cash-balance. Beat me if I am wrong, but a checksum must always be behind the values to be tested, right???
After leaving car park bytes are set to:
Bytes 0x4B..0x4C: same structure like bytes 0x3E..0x3F, days since 1992-10-11
Bytes 0x4D..0x4E: Minutes of day, same like Bytes 0x40..0x41, but for leaving car park => 0x043c = 1084/60 = 18.0667 => 0.0667*60 ~ 4 => leaving car park at 18:04
Byte 0x4F: unknown, 0xF4 on all tags
Byte 0x50: unknown, 0x01 on all tags
Byte 0x51: 0x00 on all tags
...and that's it...
Some Questions about this Tags:
- They do not contain a typical Kaba header, or am I wrong?
- Are these tags a kind of Legic Cash? I do not think so. I suspect it is more of a proprietary development of the provider.
- I have absolutely no idea what byte 0x3C might mean, maybe bytes 0x3C and 0x3D belong together?
Thomas
Offline
Nice research.
The user segments on a Legic is multiple and most implementations uses their own. Some was documented in the awesome legic.lua script.
Offline
0xEA 0x60 => so it's a SAM-Token
Thats not right. See the code changes I done a long time ago: DCF must be greater than 60000 to be any kind of Authorisation-Media (IAM, SAM, GAM etc.). That the reason no one could change/revert a IM/IM-S card back to a - lets say - IAM type of card. The DCF field is 60000 for IM/IM-S at minimum and could not be incremented again.
This is a normal type of IM-S media, or lets say: A normal user card.
- They do not contain a typical Kaba header, or am I wrong?
Right. The KABA header definition (KGH) also includes the definition of a BCD encoded card number in its header with 3 bytes (6 digits). The shown header couln't be a KGH. It is a user/integrator defined type of data structure.
- Are these tags a kind of Legic Cash? I do not think so. I suspect it is more of a proprietary development of the provider.
No, it isn't. Either the data structure nor the data size fits in any case. This is a data structure defined by the system developer. I never saw such a data structure, so I don't know who developed this. I also don't know the owner of the 0x50 Legic license. I also newer saw this before. Maybe this is sub-licensed...
- I have absolutely no idea what byte 0x3C might mean, maybe bytes 0x3C and 0x3D belong together?
Most likely this is some kind of checksum. A checksum, a question you asked before, does not have to be on the end of "something". It could be placed everywhere. Also this does not have to be generated by the Legic reader module API itself. You could write whatever you want. Sometimes I prefer to place checksums in front of data blocks, especially if any kind of encryption is envolved. This "randomizes" the data stream itself in a very handy way. So minior changes to the data do result in massive changes in the data block, no matter what kind of encryption is used.
Over all the data is constructed in a strange way. Especially the fact the card amount seems to be unique in card. This is some how stupid, because data corruption blows up everything. There should be a kind of paging/mirroring.
The best way to check if there's any checksum involved is to save the current content of the card, modifying the credit amount to somewhat else and check if this system still accepts the card (if not restore the saved data).
Maybe the "randomly" changing byte is a kind of "rolling code", to ensure the card content could not be reverted to an earlier value. Maybe all the stored data is just a "fail save" storage, because the system continuesly stores all data online and use the card data only in "offline state" as a fail-save mode. In most cases card parking systems act this way.
As this looks like a very bad design, I don't think this is "save" in any way. The only show stopper might be the fact the data on card is just a fail-save storagem and normally the systems always stores everything "online".
Last edited by Jason (2019-12-09 18:24:38)
Offline
Thx @Jason for the answers/informations!
I've tested a little more ...
Apparently only the data of the card are used and no "online" data.
If the date or credit amount on the card is changed, this data is also used by the control system.
So it's a very stupid and insecure system ...
Offline
Pages: 1