0x00 十年过去了

距离 M1 卡的加密验证缺陷被公开已经过去了十年左右,然而大量基于M1卡储存的系统仍然没有对安全问题进行有效的处理…

而离广大少年黑客最近的,便是学校的各种充值卡系统了,在一些学校里,饭卡水卡的余额储存仍然是完全依赖于 M1 卡本身的储存来实现的,当然我们学校的水卡也不例外,否则也不会有这篇文章了。

破解离线水卡系统的文章千千万,基本思路都是用现成的工具,通过 Nested Authentication Attack 获取 Key,然后就可以对卡的扇区进行读写操作了,但是与开发者所额外添加的一些安全措施进行对抗的这一过程,仍然是有趣且值得实践一番的。

0x01 简单的记录

  1. ACR122U 一台,用于读取 M1 卡
  2. mfoc,kali linux 自带工具,用于进行 Nested Authentication 攻击,获取扇区的 KeyA 和 KeyB
  3. Mifare Classic Tool(MCT),Android APP,可以利用手机的 NFC 对卡进行读写,需要手机支持 NFC NXP 协议
  4. M1 有 16 个扇区,除了 0 扇区存着卡的 UID 和各种厂商信息之外,其他的扇区持有 KeyA KeyB 就可以任意的读写

0x02 简单的重放攻击

通过 mfoc 破解出 Key,将关键数据的扇区数据使用 MCT Dump 下来,等水卡没钱了直接写进去就好了,似乎没有什么好写的。

0x03 与额外的安全措施对抗

因为 M1 卡的弱点,导致数据很容易被读出来,而受制于成本,系统开发者又不能随意的引入服务器来对数据进行验证。于是各种千奇百怪的数据加密和混淆方法出现在了各种系统上。

最开始把注意力放在数据和 UID 的关系上,但是发现这种思路没有太大意义,因为UID没有规律。 于是转移到“每刷一次卡就 dump 一次数据,基于金额的变化来推测”的这种思路,又多找了几张卡来交叉对比,防止引入了 UID 作为变量来共同计算数据。

而在半年的时间内不间断的尝试,我也幸运的找到了我们学校水卡数据的储存方式…. 只需要把第 14 扇区的前16个字节与固定的 table 进行 xor 就可以了,使用了其中2个字节以分为单位来储存金额,也就是说最大金额为 655.35 元 并且金额数据后的两个字节作为校验位,也是将 UID 的最后的一个字节和固定的数据进行 xor,令人不解的是,校验位引入 UID 和金额两个变量明显会更有效,然而开发者并没有这么做。

然而距离成功一步之遥的时候,校验位的第二位却不按套路出牌,找不到规律了。不过反正只有一个字节,256个可能性直接爆破一下就好了。于是最后参考了一下NFC-Utils的读写代码,写了一个全自动爆破 Key,通过滑条方便的修改余额的 APP。用户体验自我感觉相当良好(呸)

至此,强度并不高的混淆算法宣告破解。

watercard.png