Skip to content

Detection of anti-copy in MIFARE Classic badges #516

@TheDefender44

Description

@TheDefender44

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!

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions