Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detection of anti-copy in MIFARE Classic badges #516

Open
TheDefender44 opened this issue Dec 19, 2024 · 2 comments
Open

Detection of anti-copy in MIFARE Classic badges #516

TheDefender44 opened this issue Dec 19, 2024 · 2 comments

Comments

@TheDefender44
Copy link

This issue was first raised two years ago on Flipper Devices by @quantum-x, and it would be great to see this feature added to the MIFARE Classic Tool (MCT) app. With the growing use of anti-copy mechanisms in RFID access systems, especially in Europe, adding detection features would significantly improve the tool and help users avoid desynchronization issues.

Background

In France, nearly all apartment buildings utilize RFID-based access control systems, predominantly relying on MIFARE Classic 1K badges. The decryption keys for these badges are widely available (e.g., included in the MIFARE Classic Tool MCT library), making them relatively easy to clone or emulate.

To counteract this vulnerability, many access control manufacturers in France have introduced anti-copy mechanisms embedded in the badge’s data. Although the specific implementations vary across brands, the overarching concept is consistent:
Each time a badge is scanned by the building’s access control reader, a counter within the badge’s contents is incremented. During the next scan, the reader verifies whether the counter matches the expected value. If the value is incorrect, the badge is rejected.

The Problem

When a badge equipped with anti-copy mechanisms is cloned or emulated, the original and the cloned/emulated badges inevitably become desynchronized. This desynchronization introduces several issues:

  • If a cloned badge is used, the original badge will no longer function, as its counter will be considered invalid.
  • In the case of badge emulation, neither the original badge nor the emulation will work after the first scan, as the counter on the emulation does not update during use.
  • Some manufacturers, such as Immotec, take this even further—when they detect cloned badges, they deactivate all badges associated with the same apartment.

While this issue is most prevalent in France, it is also observed in Belgium, the UK, Germany, and Australia. As devices like the MIFARE Classic Tool has become increasingly popular across Europe, it’s important to raise awareness to prevent users from unintentionally rendering their residential badges unusable or locking themselves out of their buildings.

Potential Solutions

Given the growing need to address this issue, a detection or prevention mechanism could be beneficial. Since the anti-copy algorithms have unique identifiers for each brand and model, it is possible to identify their presence. Implementations could include:

  1. Creation of an independent function e.g. “Check Anti-Copy” that would independently read the badge, verify the presence of an anti-copy mechanism and warn the user with a an AlertDialog box (the simplest and most effective method).

  2. A simple script for analysis: A standalone script added to the Tools section of the app could analyze saved MIFARE Classic badge dumps, checking for the presence of anti-copy mechanisms.

  3. Integration into the Read Tag function: An implementation could include a simple warning (AlertDialog) that appears after the badge has been read, notifying the user if any anti-copy protections are detected before proceeding with any further actions.

There are five brands using anti-copy:
• Intratone (COGELEC Brand)
• Hexact (COGELEC Brand)
• Comelit
• Noralsy
• Urmet

Each brand has different generations of algorithms.

The match function ingests an ASCII HEX representation of the binary dump of a badge.

Here is the code that can be translated into java :

Common method

        function hex_substring($hexDump,$startByte,$endByte){
            if($endByte<$startByte){
                return "";
            }
            return substr($hexDump,$startByte*2,($endByte-$startByte)*2+2);
        }

COGELEC Brands

Generic detection for COGELEC Brands

    public function match()
    {
        return
            $this->hex_substring($this->hex_dump, 0x03c0, 0x3c5) == "484558414354" && // 3c0-3c5 == HEXACT
            $this->hex_substring($this->hex_dump, 0x03c9, 0x3cf) == "434f47454c4543" && // 3c9-3cf == COGELEC
            $this->hex_substring($this->hex_dump, 0x02c0, 0x2cf) != "00000000000000000000000000000000" && // 2c* rolling counter block not empty
            $this->hex_substring($this->hex_dump, 0x02d0, 0x2df) != "00000000000000000000000000000000" && // 2d* rolling counter block not empty
            $this->hex_substring($this->hex_dump, 0x02e0, 0x2ef) != "00000000000000000000000000000000" && // 2e* rolling counter block not empty
            $this->hex_substring($this->hex_dump, 0x02c0, 0x2cf) != "ffffffffffffffffffffffffffffffff" && // 2c* rolling counter block not ff
            $this->hex_substring($this->hex_dump, 0x02d0, 0x2df) != "ffffffffffffffffffffffffffffffff" && // 2d* rolling counter block not ff
            $this->hex_substring($this->hex_dump, 0x02e0, 0x2ef) != "ffffffffffffffffffffffffffffffff"    // 2e* rolling counter block not ff
        ;
    }

Intratone

    public $regexp = '^[0-9A-F]{1412}(?:(?!00|FF)[0-9A-F]{2}).+?684578616374202D20434F47454C4543';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Hexact

Hexact A

    public $regexp = '484558414354787788008FA1D601D0A2(?!0000000000000000000000000000000000000000000000000000000000000000).+?000000000000000000000000000000004845584143547877880089347350BD36';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Hexact B

    public $regexp = '^[0-9A-F]{1376}484558414354787788008FA1D601D0A204D26';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Hexact C

    public $regexp = '^[0-9A-F]{1376}484558414354787788008FA1D601D0A2^[0-9A-F]{4}6';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Comelit

Comelit A

    public $regexp = '[0-9A-F]{2}0000014A6352684677';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Comelit B

    public $regexp = '^[0-9A-F]{216}(?:(?!0000)[0-9A-F]{4})*?[0-9A-F]{4}4A63526846777877';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Comelit C

    public $regexp = '[^00]{3}[0-9]{2}00[0-9]{2}4A6352684677';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Noralsy

Noralsy A

    public $regexp = '675A3241377078778800395244733978';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Urmet

Urmet A

    public $regexp = '^[0-9A-F]{1888}8829DA9DAF767F[0-9A-F]{109}[5|A]';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

Generic

    public $regexp = '^[0-9A-F]{1888}050908080008FF078069FFFFFFFFFFFF[0-9A-F]{8}20202020';

    public function match()
    {
        return
            preg_match('`' . $this->regexp . '`mis', $this->hex_dump);
    }

I can provide more dumps for testing if needed. As noted by @quantum-x, please note that some badges matching A/C rules don’t have anti-copy enabled, but the false positive rate is typically no more than ~2%.

I would like to express my sincere gratitude to the incredible contributors @quantum-x, @danielebruneo, and @triplesprawl from Parklink Development Limited. Your open-source code and invaluable support have been instrumental in advancing the project and helping the NFC community make significant strides. A special thank you also goes to @ikarus23 for the fantastic MCT tool. Your dedication and hard work are deeply appreciated—thank you all!

@ikarus23
Copy link
Owner

Holy hell! What an fantastic write up!

I did know the issue, but I was not aware that it is present in so many solutions. And I was far from knowing that it had been researched this good!

With all this information available it should not be that hard to implement. I will look into it (but can't promise on a timeline yet).

Personally I would opt for solution 3. The first one looks like the cleanest but in my experience with this app, inexperienced users will not press an extra "Check Anti-Copy". They don't even know what that is. I get a lot of mail and reviews complaining about something like "why can it not read my tag?" when using e.g. a NTAG. Event though MIFARE Classic is right in the name :) Anyways, showing a popup after reading should be safest way to always inform the user. An option could be added to disable the "Auto Check Anti-Copy". And of corse an extra menu entry in the editor could be added to check for anti-copy manually.

@TheDefender44
Copy link
Author

Thank you so much for your thoughtful response :) I’m glad the write-up was helpful in highlighting the broader scope of the issue.

It’s fantastic to hear you’re considering implementing this enhancement as it could be a real game-changer for many users in Europe. While coding isn’t my strong suit, gathering and sharing information about projects I care deeply about is where I shine, so I’m happy to help contribute in this way.

I completely understand your preference for solution 3. Displaying a popup immediately after reading the tag is a much more dependable way to ensure users are informed. Combining this with an option to disable “Auto Check Anti-Copy” and including a manual menu entry for advanced users creates a great balance between ease of use and flexibility.

Honestly, the timeline isn’t really an issue. What’s most important is that this is implemented thoughtfully and effectively.
Please don’t hesitate to reach out if you need any help with anything related to this enhancement.

In the meantime, wishing you a very Merry Christmas and a wonderful holiday season. And again thank you for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants