RFID HACK 之学校饭卡任意金额修改
0x00前言
自从成功破解完学校饭卡后就一直想抽个时间记录一下,可能过段时间会很少碰电脑,所以今天在这里写下来,希望可以与各位分享自己的思路。
0x01基础知识
1.M1卡介绍
Mifare Classic card提供1k-4k的容量,我们经常见到的是Mifare Classic 1k(S50),也就是所谓的M1卡。M1卡有从0到15共16个扇区,并且每个扇区都有独立的密码,每个扇区配备了从0到3共4个段,每个段可以保存16字节的内容,反正从0开始数 就对了(和数组下标为0开始一样)。
每个扇区的第4段呢是用来保存KeyA,KeyB和控制位的,每张卡还有一个唯一标识的UID号。
参考:乌云知识库
2.使用工具
可以说使用工具是大大地多,当然作为初学者读卡器还是推荐使用ACR122U(某宝可以买到)
先来上一张ACR122U的图吧
嗯…差不多就是这个样纸,可能有的颜色会不一样哦,价格在180作用
至于读卡软件网上也是有很多,这里就不细说了。
3.常用攻击方法
1)暴力破解
爆破对于M1卡的破解来说比较有效,因为M1卡是被动卡,需要读卡器来供能,切断供能后卡的临时数据就丢失了,也就是说不会存在输入过多错误密码后造成的锁死之类的情况 FFFFFFFFFFFF、A0B0C0D0E0F0等等都是M1白卡的默认密码,所以当我们使用mfoc这样的工具来爆破的时候基本上都是用这些默认密码来填充剩余扇区的密码。
2)重放攻击
刚刚我们说了M1卡是被动卡,当它被供能的时候会产生随机数列,切断供能后数据不会保存,再次供能又会产生一模一样的数列,然后就可以控制切断,再次供能的时间计算出这个数列,进行重放攻击来达到修改数据的目的。
3)克隆卡片(卡复制)
M1卡的扇区可以保存数据,所以大部分的卡片会选择加密扇区后保存数据,我们可以用 uid卡来进行复制,每张M1卡在0扇区第1段都有一个唯一标识,而且是保护无法修改的,uid 卡就是没有设定0扇区保护的卡,所以你可以随意的修改你想要的uid,这样我们就可以克隆出一张连uid都相同的卡片了。(但是要注意不要把00扇区弄 坏,之前测试的时候就未知原因写坏了00扇区无法读入了)。
4)嗅探攻击
这里要用到PM3这个神器,在卡和机器数据交换的时候嗅探数据,进行攻击,利用XOR算key工具就可以把扇区的密钥计算出来
参考:乌云知识库
0x02测试记录
先得说说,Ruilin在进行取样分析的时候也是历经曲折,饭卡并没有对金额进行常见的进制转换和取反,可是却多出了一个校验码,这不得不让Ruilin刷了好几次饭(虽然用的是同学的卡…在这里非常感谢)
先用两张不同的饭卡进行的刷卡,回家后用读卡器dump下来数据
用winhex打开dump的数据
OK,这是两张卡的数据,其中前两个是同一张卡刷两次后每一次的数据
抛去最前面卡UID的不同(图里未截)我们可以看到用红框框住的两个字符 84/16 由于是每一张卡独有的字符且一直不发生变化,所以我们可以基本无视他,猜测他可能为每一张卡的编号
其次我们开始对比下面的数据
第一张
1 2 3 4 5 |
2 扇区 0区块:81151234567800007684000000000000 1区块:4B506400092800060000115700000000 2区块:9C006300100800015000192500000000 3区块:010203040A0CFF078069FFFFFFFFFFFF |
第二张
1 2 3 4 5 |
2 扇区 0区块:81151234567800007684000000000000 1区块:4B506400092800060000115700000000 2区块:3F507000092500070000120900000000 3区块:010203040A0CFF078069FFFFFFFFFFFF |
第三张
1 2 3 4 5 |
2 扇区 0区块:81151234567800007616000000000000 1区块:AE401100033000077000115100000000 2区块:8E700100033000174000115100000000 3区块:010203040A0CFF078069FFFFFFFFFFFF |
我们可以看到2扇区第1区块和第2区块的数据有所不同
其中通过反复对比和思考后发现(以卡一为例)
1 2 |
1区块:4B506400092800060000115700000000 2区块:9C006300100800015000192500000000 |
首先5064/0063 消费金额和剩余金额
使用前金额为64.5元,Ruilin买了个花卷花了1.5元(真的挺好吃的)就剩下63.0元(数学功底似乎还可以)
所以数据记录方式为两位一组反向记录 即5064/0063,这里你会发现数据后面还有两个0,Ruilin猜那肯定就是千位和万位,或者是分割的标志既然不重要,那就无视了.
然后0928/1008 恩,低下头想了想,这不是我刷卡的日期吗,这样就很容易了
后面的1157/1925则是刷卡时间
当然你会发现前面有一个4B/9C 这是什么鬼,每次都不一样,Ruilin也是晕了,俗话说的好,xxxxx,xxxxxx(我也不知道我要说什么)于是,我抱着试一试的心态和同学又去刷了几回,事情好像出现了转机
对比数据是一件痛苦的事情,可是想想以后或许能免费吃饭,瞬间动力就上来了
如图ABCD四次刷卡,可以看到前面的两个值应该是用来表示不同金额,也就是所谓的金额校验码,Ruilin尝试了许多种进制和取反也没有得出有效的结果,既然这样那只能来一次找规律了
于是进行了许多组数据的对比,终于运气很好的在一组数据中看到了希望
1 2 3 |
7E 70 11 AE 40 11 8E 70 01 |
(怎么说呢,当时为了吃饭脑洞真大,我现在写的时候已经忘了当时怎么想的了,于是看了一下当时的记录)
校验码 | 金额(反) | |
3F | 50 | 70 |
3D | 60 | 62 |
9C | 00 | 63 |
4B | 50 | 64 |
7E | 70 | 11 |
AE | 40 | 11 |
8E | 70 | 01 |
这是几次刷卡的记录,我们来看后三个
EEE三个E应该是代表着同一个值,所以既然是金额取反,我们把它们三个反过来再看看
1 2 3 |
11 70 11 40 01 70 |
11/11/01 最后一位都有1
70/40/70 最后一位都有0
1+0=1 会不会E代表着1
那么再找找看记录数据里有没有校验码第一位
前两个数据3F/3D
5+7=12 6+6=12 (Ruilin瞬间觉得数学没有白学)
既然这样,事情似乎有了点眉目,Ruilin默默的看了看记录下的算法,自己试着列一遍吧
校验码 | 解释算法: | 如 | 3D | 60 | 62 |
金额第一位:6/6 金额第二位:0/2 | |||||
X(第一位):3->12 Y(第二位):D->2 | A(金额第一位相加):6+6=12 B(金额第二位相加):0+2=2 | ||||
效验码: X(第一位)=A(第一位相加) Y(第二位)=B(第二位相加) |
校验码对应值 | ||
X(Y) | A(B) | |
0 | 15 | |
1 | 14 | |
2 | 13 | |
3 | 12 | |
4 | 11 | |
5 | 10 | |
6 | 9 | |
7 | 8 | |
8 | 7 | |
9 | 6 | |
A | 5 | |
B | 4 | |
C | 3 | |
D | 2 | |
E | 1 | |
F | 0 |
这样来看显然是16进制反了过来
匹配了下上面dump的数据,成功了!
然之后问题又来了,这样的算法只能生成和为15内的的金额,那大于15怎么办
于是之后等机会又试了几次,这次终于和超过了15
而效果是这样
1 2 3 4 5 |
2 扇区 0区块:81 15 12 34 56 78 00 00 76 87 00 00 00 00 00 00 1区块:FA 80 84 01 02 27 00 16 00 00 18 11 00 00 00 00 2区块:ED 80 91 01 02 27 00 09 00 00 18 10 00 00 00 00 3区块:01 02 03 04 0A 0C FF 07 80 69 FF FF FF FF FF FF |
即16=F 17=E
那这样怎么更方便的表示呢
我抽了个闲的时间很智障的推了一遍
也就是31减去那个数再转16进制
这样转成代码就是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#erwaicardcheckcode money=input() maxx=10000 minn=0 if money<maxx and money>=minn: fee=format(float(money),'07.2f') list = [] for i in fee: list.append(i) a=int(list[0]) b=int(list[1]) c=int(list[2]) d=int(list[3]) e=int(list[5]) f=int(list[6]) x=a+c+e y=b+d+f if x<16: first=hex(15-x) else: first=hex(31-x) if y<16: second=hex(15-y) else: second=hex(31-y) print "%s%s"%(first[2:],second[2:]) else: print "error" |
//更新 很久之后我再次看时才发现原来有更简单的理解方法 其实就是三个两位十六进制相加然后转二进制取补码再转回十六进制 唉还是当初太年轻呀hhh//
大功告成,用一张白卡写入数据,Ruilin带着好基友一起大摇大摆的冲进了食堂,刷了一下
99元 校验码是66
Ruilin悠闲的嚼着花卷,走出了食堂
0x03总结
分析数据可以首先针对金额,消费日期进行判断各个部分的含义,最后复杂部分要通过多次对比取样和一个大开的脑洞去猜测算法,算法一般是可逆算法,字节运算一般主要考虑基本运算/异或运算.
最后还有一句送给大家
改卡有风险,入坑需谨慎.
近期评论