a.k.a. NJU 地图校园卡副本单刷记录, 相关数据已脱敏, 仅供参考学习.
副本介绍(?)
现有的 M1 NFC 卡片通常安全性堪忧 - 非全扇区加密的卡大部分有验证漏洞, 可以直接离线破解出所有扇区密钥; 全加密或无漏洞的卡也可以用 Proxmark3 之类的工具轻松嗅探出全扇区密钥和数据.
然而, SAK20 卡片 (纯 CPU 卡) 及 SAK28 卡片 (CPU+模拟 M1 混合卡) 的 CPU 部分暂未发现广为流传的漏洞, 破解相关资料很少, 通常被认为是无法破解的.
果真如此吗?
副本Boss - [NJU 校园卡]
阶段 1 - 平平无奇漏洞卡
原本, 我和你南校园卡的斗争过程应该平平无奇: 我在来到 NJU 的第一天晚上就掏出了我的 PN532 破解出了全扇区密钥并复制了我的校园卡.
当时还发了一条动态
它是一张有漏洞, 非全扇区加密的 SAK18 类型卡片, 使用 PN532 破解密钥并复制这类卡片非常 trivial, 网上有大量的工具和教程. 并且即使这张卡片大小是不太常见的 4KB, 但数据全集中在前 1KB 的扇区中, 可以容易的将其写入有 M1 卡模拟功能的手机(或 UID/CUID 等空白卡)中使用其完整功能.
我就如此轻松的开始了出门不用带卡的潇洒生活…
阶段 2 - 扇区密钥计算器
后来的某一天, Sakiyary 找我帮他复制他的校园卡到手机, 我才发现 22 年及以后 NJU 发出的新校园卡已经变成了 SAK28 类型.
这类卡片有 CPU 部分和 M1 部分两个部分, CPU 部分使用了不同的协议, 我初步猜测为了兼容性, 数据仍存在 M1 部分中.
这种卡没有漏洞, 不能直接用 PN532 破解全扇区密钥, 虽然还是可以用 Proxmark3 嗅探出密钥, 但我当时并没有 PM3, 我决定找个一劳永逸的办法: 逆向扇区密钥生成规则.
除非你有极其惊人的注意力以及大量样本, 要逆向扇区密钥生成方法光有卡肯定是不够的.
于是… 我通过某些不便细说的近源渗透手段搞到了一份读卡器的程序. (小声: 其实这才是关键x)
我们已经知道 M1 卡在完成扇区密钥认证前能得到的信息只有其前 16-bytes, 其中最重要和最常用的是前 4-bytes 的 UID, 我们需要在读卡器程序中找到如何通过 4bytes 的 UID 生成 A 和 B 两个每个 6bytes 共 12bytes 的扇区密钥.
你南的读卡器程序是一个用了 SpringBoot 写的 Java 程序以及几个附带的 dll, 由于程序几乎没有混淆, 可以容易的找到密钥计算的逻辑, 密钥实际的计算逻辑在 dll 里, 实际上是一长串手搓的位运算, 并有一个所谓”密钥”参与运算, 不过我在程序明文 log 中直接找到了保存的密钥.
甚至不需要二进制逆向, 写一段 python 脚本调用 dll 计算就行
顺手我又通过瞪眼观察法逆向出了卡内大部分扇区数据的含义:
数据还挺丰富, 包含了一些消费记录, 姓名, 学号等信息... 不过显然全是联网的改了也没用
至此我都不需要 PN532, 只需要一个有 NFC 功能的安卓手机下载个 MifareClassicTool 就能解析和复制南大的新校园卡.
阶段 3 - Final - 纯 CPU 部分破解
我本以为我已经彻底分析完了你南校园卡, 故事该在此结束, 这篇博客本也会因乏善可陈而不会出现, 直到… 我在测试过程中发现南大居然还有第三种校园卡类型. (其实后来发现如果按数据内容分类远不止三种, 甚至同一时间开出的卡数据内容都能完全不一样, tmd你南是怎么做到这么乱的)
发现部分更加近期的校园卡 M1 部分全部使用全 FF 默认密钥且扇区全空, 但仍能正常使用.
那就只剩下了一种可能 - 这种卡把所有数据都保存到了 SAK28 卡片的 CPU 部分, 完全没有使用 M1 部分, 这种用法就相当于使用了 SAK20 的纯 CPU 卡.
RFID 届的瑞士军刀 - Proxmark3
理论上我可以对着读卡器的代码和 CPU 卡相关的文档直接开搞, 完全静态的分析出卡片的通讯方式和密钥, 不过现在资料较为缺乏, 这样分析过于折磨了.
我们上面提到的 SAK08/18/28 卡片均遵循 ISO/IEC 14443-A 协议, 工作在 13.56 MHz 的频段下, Proxmark3 就可以嗅探读卡机和卡片直接的通讯数据并 dump 出来供分析. (最流行的 NFC 嗅探硬件就是 Proxmark3, 且除嗅探外还有很多其他功能和优秀的开源支持, 但你可以用任何此频段的无线接收设备, 甚至是一个 RTL-SDR 来进行嗅探)
于是我当场根据选购建议([1] [2])去🐟买了一个 Proxmark3 RDV3.0 Easy 512kB 版本(二手~150元). 卖家一同打包卖了一些测试卡和一个非常可疑的读卡软件, 我看了下这个软件除了能自动化分析破解部分电梯卡别无是处, 我们这里不需要用到. (如果不需要 GUI 软件的话, 1688 上也有一些便宜的新 PM3 出售)
我这个 PM3 到手时的固件是一个很旧的版本, 版本号里还塞着商家自己的淘宝店广告, 也无法正常使用开源版本的配套软件, 需要先刷机一遍. 所以购买前最好和卖家确认对应的 PM3 版本支持开源固件.
下载固件后 Windows 上无需驱动, 直接执行 pm3-flash-bootrom.bat
和 pm3-flash-fullimage.bat
即可, 随后可以使用官方提供的 pm3
命令行工具控制 PM3.
嗅探通讯
在命令行中输入 hf 14a sniff
开始嗅探, 把卡贴在 PM3 读卡区上, 再将卡和 PM3 一起贴到读卡机上刷一次卡, 按侧键结束嗅探, 此时通过 hf 14a list
可以列出嗅探到的数据.
成功嗅探到通讯且 CRC 校验通过 (我测试时小概率会 CRC 错误或缺数据, 多试几次就行)
嗅探到的数据如何分析呢? 你南(和它的外包乙方)显然没那个实力自己发明一种 NFC 卡片, 肯定是买的其他厂商现成的卡片, 使用 auto
或者 hf mf info
命令就识别出卡片类型和厂商:
1 | [usb] pm3 --> hf mf info |
后者直接识别出了这张 SAK28 的卡片是 Fudan FM1208-10
, 直接 Google 一下就能找到复旦微电子官网的介绍, 然后我们直接就能下载对应的产品文档 然后这个破官网的下载链接失效了, 不过互联网上搜索一下也不难找到公开的文档, 我这里也传了一份副本.
CPU 卡结构
其实国内使用的这种被俗称为 “CPU 卡” 的 NFC 卡片, 大概率就是指复旦微电子生产的 FM1208
或相近型号的卡 (淘宝上搜 cpu卡
也只能买到这种). 所以本段的结论目前适用于市面上大部分被称为 “CPU 卡” 的卡片.
根据文档第 4 章节 “FMCOS 文件结构”, 这种卡的内部存储结构和 M1 的扇区存储不同, 是一个类似文件系统的树状结构:
FMCOS IC 卡的基本文件系统是由主文件 MF(Master File)、目录文件 DF(Directory File)和基本文件 EF(Element File)组成。主文件 MF 在 IC 卡中唯一存在,在 MF 下可以有多个目录文件 DF 和基本文件 EF,每一个 MF 目录下的 DF 可以存放多个基本文件 EF 和多个下级目录文件 DF, 在这里我们称包含下级目录的目录文件为 DDF,不含下级目录的目录文件为 ADF。
从终端角度来看,与支付系统应用相关的支付系统环境文件呈一种可通过目录结构访问的树形结构。 树的每一分支是一个应用数据文件 ADF。一个 ADF 是一个或多个应用基本文件 EF 的入口点。一个 ADF 及其相关数据文件处于树的同一分支上。
…(后面还有好长的详细介绍,请自行点开文档看吧)
这个卡的存储结构大致是: 有个 MF 作为整个卡的根目录, DF 就是文件夹, EF 是文件夹里的文件; EF 有多种类型: 二进制文件, 记录文件(其实就是二进制列表), 钱包文件, KEY 文件等.
每个文件进行操作时会有权限控制, 每个文件夹下有个 KEY 文件存有若干密钥, 读卡器需要知道 KEY 文件中的密钥, 并完成一个 challenge-response 认证以提升权限等级, “解锁”这个文件夹下的更多操作. (详见文档第 5 章节 “FMCOS 独特的安全体系”)
文件标识符是文件的标识代码,用 2 个字节来表示,在选择文件时只要指出该文件的标识代码, FMCOS 就可以找到相应文件,同一目录下的文件标识符必须是唯一的。MF 的文件标识符是 3F00, 默认文件名为 1PAY.SYS.DDF01。 所有文件都可以通过文件标识符用 SELECT 命令进行选择,目录文件 DF 还可以通过目录名称进行选择。
每个文件或目录都有一个 2bytes 长的编号用于找到这个文件, 文件夹还可以使用一个不定长 bytes 串作为名字进行选择.
另外, 通讯过程中, 读卡器可以可选的对通讯进行 MAC 消息认证或者进行 DES/3DES 加密, 防止消息被篡改或嗅探.
你南的卡只用到了二进制文件和记录文件两种文件, 所以在此不再展开更多内容, 其他文件类型文档中也有详细的说明 (比如钱包类型文件就有很多复杂的存取操作).
分析你南的 CPU 卡
有了对卡的基本了解, 还有文档和捕获的数据, 终于可以开始正式分析这张卡了.
我们先看一次宿舍热水消费时捕获到的卡和读卡器的通讯(肉眼对着文档解析的累死了… 一开始就该写个脚本的):
1 | # 此处每个包的前两 byte 为协议头, 最后两 byte 为 CRC, 中间才是 APDU 的实际内容 |
第一次看到这个通讯的时候我也很头痛, 明明是联网消费, 怎么一会儿读一会儿写的, 还涉及了三个不同的文件, 读写的内容也看不出明显规律, 选择的目录还有个意义不明的文件名 D156000001BDF0CACBB4EFD6A7B8B6
.
不过还有好消息是: 整个通讯过程中读取文件前没有做任何认证, 意味着至少这里的三个文件不需要任何权限就能读取. 同时读卡器和卡的交流过程没有做加密, 所有读写的明文数据都被捕获到了.
由于已经找到了三个没有密钥保护的文件, 我决定先来看看这张卡里到底有哪些数据.
虽然我们现在没有现成的用于读写 FM1208 的上位机程序, 但 PM3 提供的 hf 14a apdu
指令可以直接将一个 16 进制的 APDU 负载发送给卡, 并从卡读取响应. 这张卡遵循了 ISO/IEC 7816-4 中的 APDU 格式, 所以我们可以直接用 pm3 命令行给卡片发送命令并观察卡片行为.
1 | # 尝试发送一个 select MF 给卡, 可以收到卡片的成功回应, 现在我们就能模拟读卡器了! |
能直接与卡片交互后, 我们就可以尝试主动探索卡片内容了. 上面提到, 我们可以使用一个文件名或者一个 2 bytes 长的文件编号来选中一个文件, 但 FM1208 卡片没有提供一种列出卡内文件或文件夹的方式, 我直接写了一个脚本, 暴力穷举 0x0000-0xFFFF 所有的文件编号, 列出了卡片中的文件夹结构.
穷举一层目录耗时大约 20-30 分钟, 并不算太慢, 考虑到整张卡片的存储不超过 8kb, 所以并不太需要担心文件多的穷举不完.
顺手, 我通过 READ 指令尝试读取其中的内容, 确定了文件类型, 并做了一些简单的分析如下.
1 | 3F00 (根, 名字 1PAY.SYS.DDF01) |
考虑到你南内部的大部分系统都混乱不堪, 我选择暂时不去纠结每个数据的具体含义. 之前的 M1 卡里面洋洋洒洒有一堆数据, 但实际上根据我测试, 只需要 UID 4bytes + 扇区密钥 6bytes + 另外 3bytes 数据就能完整实现校园卡功能… 剩下的几十 bytes 数据 tmd 都是写了好玩的, 有和没有一样.
我们现在已经基本读取出卡片的大部分内容, 并且我们通过嗅探知道没读取到的部分对于正常消费流程不是必须的; 而对于联网消费的卡片, 篡改内容的意义也不大, 现在可以想想如何复制卡片了.
复制以及模拟
复制 M1 卡时, 我们需要一张有后门的 UID/CUID 卡片用于写入 UID 和扇区. 复制 CPU 卡时, 因为正常的 FM1208 UID 无法修改, 我们也需要一张这样的”后门”卡片.
淘宝上搜索 CPU复制卡
/TID卡
/SID卡
可以找到类似的卡片以及配套软件(e.g. 这个和这个). 根据商家的描述, 我们可以使用专用软件随意修改这种卡的目录结构以及 UID, 只要修改成和上述扫描分析出的原卡一致, 就成功复制出了卡.
不过… 你南丢一张校园卡补办才收 20 块, 复制卡卖 40 块, 我还不如老老实实去补办.
我们先尝试下用 PM3 进行模拟卡, PM3 本身并不支持 FM1208 这套指令, 我们需要直接魔改一下固件. PM3 有 FPGA 和 ARM 两块芯片, 阅读一下代码, 发现现在 PM3 的实现会对所有的模拟卡响应返回 90 00
, 我们只要 patch 一下相关代码, 直接返回和嗅探到的卡片一样的响应就行. (还好我们要魔改的只是 ARM 部分的 C 代码, Verilog 我是真不会.)
刷入 DIY 固件后运行 hf 14a sim -t 11
, 直接成功模拟:
复制走同学的卡, 并在本人面前炫耀(x)
上面我就提到”暂时不去纠结每个数据的具体含义”, 现在看来是正确的. 因为在模拟时我发现我只要对 SELECT DF03
这条指令返回原卡的响应, 其他请求全部返回数据为空的 90 00
成功, 这张模拟卡就能完全正常工作, 上面的 patch 代码简单到就寥寥几行.
又是一个你南十分经典的功能复杂设计混乱的💩山系统.
顺便, 由于读卡器缺少对卡的认证, 无法区分模拟卡和原卡, 我们显然可以在模拟卡时顺手修改数据, 变相实现修改卡的内容. 这也为进一步的分析逆向提供了很方便的工具 - 可以修改某个不理解含义的值, 观察读卡系统的反应, 反推值的作用.
最后, 我们还有一个终极目标就是, 是否能把这张卡模拟到安卓手机中, 实现手机直接当卡刷呢?
原生安卓从 4.4 开始就支持了 HCE(主机卡模拟)功能, 我们发现它虽然不支持 M1, 但恰好支持的就是 FM1208 所使用的 ISO 7816-4 和 ISO 14443-4 标准. 我甚至找到了之前有人用 HCE 模拟 FM1208 的代码仓库, 并成功使用 PM3 读取出手机模拟的卡, 不过模拟卡在南大的读卡器上并不工作.
经过我的长达两小时的研究和实验, 以及各种翻阅安卓源码, 我发现这里还有最后两个问题:
- 安卓 HCE 模拟出的卡 UID 和原卡不一致, 部分读卡器不认
- 安卓 HCE 要求第一条 SELECT 指令必须是 SELECT_AID, 即
00A404
打头的按名称 SELECT, 不过南大的读卡器第一条指令会 SELECT MF (00A400
), 此时安卓返回错误码, 读卡器会停止后续流程. (相关 Stackoverflow 问题)
我开始尝试了 Hook NfcNci 中的 onHostEmulationData 方法, 但发现 00A400
指令根本没有进入 Host Emulation 的逻辑, 在更上层的 NfcService 中就提前返回了错误码, Native 层的 Hook 难度更高且可能和硬件相关, 我本来都想放弃, 突然进一步搜索到了 nfcgate 这个项目.
经过我在 Sony Xperia 5 II 上的实机测试, 我发现它实现了 Native 层的 Hook, 同时解决了上面无法修改 UID 和无法截获 SELECT MF 的两个问题, 甚至实现了通过网络 relay NFC 通讯的功能.
在 nfcgate 的代码上小幅修改, 我们就能直接在 Java 中收到读卡器发送的 APDU, 并直接构造出响应 APDU, 实现和 PM3 同等的模拟功能, 完美实现把校园卡放进手机刷.
运行修改后的 nfcgate, 可以用 PM3 直接读取出与原卡完全一致的信息, 也可以自定义 SELECT 的响应
由于手机太厚塞不进宿舍的读卡器, 我选择了另一个读卡终端进行测试, 这个读卡终端比起宿舍热水消费终端要实现稍微多一点的指令才能正常工作:
安卓模拟的卡完美识别, 手机 Log 能看到读卡器的请求
考虑到手机在存储空间和处理性能方面比 PM3 有着高得多的灵活性, 同时卡本身没有读取密码, 甚至可以实现一个 app, 做到用手机刷一下任何南大校园卡就能读取并保存在手机中(or 网络传输), 然后直接模拟保存的卡信息, 无需其他读卡设备.
不过我自己其实没有手机模拟 CPU 卡的实际需求 (我的老卡一直是 M1), 况且我马上要毕业跑路了, 所以这种思路记录在此, 留给有需要的后人实现吧.
副本经验总结(x)
复旦微电子这款 FM1208 CPU 卡确实暂时还没有公开的漏洞, 但并不意味着其设计是安全的, 更不意味着各种厂商的实现是安全的.
你南的校园卡就是一个经典的错误示范:
- 卡内文件的权限被设置为无需认证可读取
- 所有读卡器都没有使用加密和 MAC
- 读卡器缺少对卡的认证
在叠了这么多 buff 后, CPU 卡的数据和 M1 一样可以被随意读取复制和修改, 你南甚至做到了让成本更高的 CPU 卡比旧 M1 卡更不安全 - M1 卡至少需要用 PN532 花个十来分钟破解扇区密钥, 但随便一个有 NFC 的安卓手机就能读取走 CPU 卡的内容
如果我一开始就有更多经验的话, 我甚至可以只用 PN532 或安卓手机完成整个破解过程.
并且 FM1208 这张卡片本身的协议设计也有明显的安全缺陷, 就算不提官方文档用 “高安全性是以降低速度,增加实现难度来换取的,所以并不是安全性越高越好” 这种描述怂恿开发者不开启加密, 即使开发者启用全部的安全措施 (3DES 密钥, 线路加密, 线路保护MAC, 卡片的外部认证和内部认证), 这张 FM1208 卡片也是不能抵御稍复杂的中间人攻击和重放攻击的.
设想这样一个攻击场景: 我作为中间人转发卡片和读卡器的通讯, 在读卡器恰好完成外部认证时停止转发通讯, 随后就可以得到一张已经认证过的卡片, 向其随意发送需要权限的指令.
或者: 一个读卡器如果以固定的方式向卡片发送指令, 我完全可以嗅探卡片和读卡器的通讯, 记录下来之后通过模拟卡实现复制卡片.
更别提这张卡片的 MAC 只支持 DES 而不是 3DES, 只要开发者没使用 3DES 密钥进行线路加密, 嗅探到的通讯可以被用于暴力破解密钥. 在 2025 年, 使用一张 RTX 4090 就能以 146.6 GH/s 的速度进行搜索, 在 5 天内穷尽 56-bit 的密钥空间找出密钥.
就算这个文档语焉不详, 前后不一致, 实物还与文档各种不一致的卡片在将来不发现任何安全漏洞, 这些一堆设计缺陷也够将来的开发者头痛很长一段时间了.
呼… 好长的博客, 终于写完了! 终于满足了我对传说中的 “CPU 卡” 的好奇心. 也不过如此, 已经祛魅了.
本文所有用到的代码已经整理放在了 FM1208_scripts 仓库中, 所有公开的数据均来自一张真实但已被挂失的南大校园卡, 演示效果时使用的是另一张状态正常的校园卡. 建议不要随意公开自己正在使用的校园卡数据以防遭到滥用.
本文采用 CC BY-NC-SA 4.0 许可协议发布.
作者: lyc8503, 文章链接: https://blog.lyc8503.net/post/reverse-nju-cpu-card/
如果本文给你带来了帮助或让你觉得有趣, 可以考虑赞助我¬_¬