CAN总线CTF题目
CAN总线CTF题目
2020网鼎杯 青龙组 teslaaaaa
附件需改为 zip,wp:https://blog.csdn.net/Breeze_CAT/article/details/106156567
给了一段 CAN 报文,通过报文数据可以很明显看出来是进行 UDS 诊断的,那就一点一点梳理一下整个流程
4.000621 1 7DF Tx d 8 02 3E 80 00 00 00 00 00 会话保持
9.498709 1 7DF Tx d 8 02 10 02 AA AA AA AA AA 切换编程会话
9.740585 1 730 Tx d 8 02 27 05 AA AA AA AA AA 请求安全访问种子
9.741697 1 7B0 Rx d 8 06 67 05 11 22 33 44 00 返回安全访问种子
9.782739 1 730 Tx d 8 06 27 06 EE DD CC BB AA 发送安全访问密钥
9.783703 1 7B0 Rx d 8 02 67 06 00 00 00 00 00 通过安全访问
接下来是用 31 服务,来指定擦除的参数有地址和大小,因为一条 CAN 总线指令不够用,因此是用了流控帧,实际需要将两条指令连起来
9.788131 1 730 Tx d 8 10 0D 31 01 FF 00 44 08
9.788431 1 7B0 Rx d 8 30 08 00 00 00 00 00 00 流控帧
9.788947 1 730 Tx d 8 21 00 00 00 00 00 20 00
9.789707 1 7B0 Rx d 8 05 71 01 FF 00 00 00 00
31 01 FF 00 44 08 00 00 00 00 00 20 00
其中 31 表示 RoutineControl 用来执行一些已经定义好的步骤序列也叫例程,他有三个子功能:01 表示开始,02 表示停止,03 表示获取结果;FF00 表示擦除 flash,44 表示后面两个参数的长度(第7至第4位: memorySize参数的长度,第3至第0位: memoryAddress参数的长度),也就是地址为:0x08000000,长度为:0x00002000
请求下载 34,这个服务没有子功能,后面的 00 有厂商自定义,高半字节指定压缩法,低半字节指定加密法;再后面的 44 和上面一样是参数长度;请求下载地址为:0x08000000,长度为:0x00002000
9.791765 1 730 Tx d 8 10 0B 34 00 44 08 00 00
9.792061 1 7B0 Rx d 8 30 08 00 00 00 00 00 00
9.792625 1 730 Tx d 8 21 00 00 00 20 00 AA AA
9.793715 1 7B0 Rx d 8 04 74 20 01 02 00 00 00
然后就是用 36 服务具体传输了,36 后面的 01 是 blockSequenceCounter 这个 block 计数器满了之后再从 0x00 开始
9.795696 1 730 Tx d 8 10 82 36 01 28 04 00 20
9.795987 1 7B0 Rx d 8 30 08 00 00 00 00 00 00
9.796548 1 730 Tx d 8 21 45 01 00 08 21 03 00
9.796790 1 730 Tx d 8 22 08 23 03 00 08 27 03
中间刷写具体刷了啥先不看,这样也没法看,到后面刷写完成
10.312946 1 7B0 Rx d 8 02 76 40 00 00 00 00 00 刷写完成
10.314499 1 730 Tx d 8 02 37 01 AA AA AA AA AA 退出传输
10.318529 1 730 Tx d 8 04 31 01 DF FF AA AA AA
10.322633 1 730 Tx d 8 04 31 01 FF 01 AA AA AA 这两条应该是后续的检查
10.325697 1 7DF Tx d 8 02 11 01 AA AA AA AA AA ECU复位
那就把这些过程性的东西删一删,然后把所有 RX 的数据也删掉,剩下的就是纯发数据的了
用 python 过滤一下没用的字符和控制字段,输出为纯二进制文件
f = open('ecu_can_log.asc', 'r')
f1 = open('result', 'wb')
list1 = f.readlines()
data=[]
count=1
for i in list1:
if(count%19==0): #19行是数据传输的最后一行,后面有两个AA是不要的
data=i[43:57]
elif(count%19==1): #每次传输的第一行要去掉传输协议头
data=i[52:63]
else:
data=i[43:63] #其他中间传输的报文,直接去掉UDS网络层即可
data = bytes.fromhex(data)
f1.write(data)
count+=1
然后用 IDA 打开,通过字符串就可以定位到假的 flag 了... 离谱,但是逻辑很简单,基本复制代码就能解出来
fakeflag = "canoecr7-zd9h-1emi-or8m-f8vm2od81nfk"
a1 = []
for i in fakeflag:
a1.append(ord(i))
a1[2] -= 13;
a1[11] -= 5;
a1[15] -= 44;
a1[3] -= 11;
a1[5] -= 48;
a1[7] += 43;
a1[28] += 50;
a1[31] += 46;
a1[19] -= 13;
a1[20] -= 66;
a1[1] += 3;
a1[29] -= 55;
a1[24] -= 51;
a1[9] -= 23;
a1[25] -= 6;
a1[27] -= 60;
a1[4] -= 52;
a1[6] -= 14;
a1[30] -= 52;
a1[22] -= 58;
a1[12] -= 48;
a1[16] -= 56;
a1[34] -= 53;
a1[0] -= 48;
a1[14] += 3;
a1[17] -= 5;
a1[33] -= 55;
a1[35] -= 56;
a1[10] -= 2;
a1[26] -= 67;
a1[21] -= 6
print("".join(chr(x) for x in a1))
2023工业信息安全技能大赛-车联网安全锦标赛-线上赛
感谢 g0at 师傅提供题目附件