车联网入门第一题--D3ctf-d3car
周末和烧卖激情的打了D3ctf,逆向没啥特别想记录的,主要是这次是本人第一次接触车机类的题目,主要时间在和师傅们一起做这个,感觉非常有意思,学到很多,但是最后还是只日出来一个flag,后面俩没日出来。不过日的时候疯狂搜索各种相关知识,也算小小的入了一点点门了?之后再找点相关
发现环境已经寄了,只能靠回忆来复现了
分三部分,相关知识介绍,自己做的部分和复现的部分
相关知识
mqtt
简述
MQTT 是一种基于标准的消息传递协议或规则集,用于机器对机器的通信。智能传感器、可穿戴设备和其他物联网(IoT)设备通常必须通过带宽有限的资源受限网络传输和接收数据。这些物联网设备使用 MQTT 进行数据传输,因为它易于实施,并且可以有效地传输物联网数据。MQTT 支持设备到云端和云端到设备之间的消息传递。
原理
mqtt是基于发布/订阅模型工作的协议,和普通的网络通信协议不一样,一般的网络通信协议是服务端和客户端相互通信,但是mqtt是引入了一个代理,每个客户端(用mqtt通信的设备都可以叫mqtt客户端)都可以是发布者和订阅者。如果是订阅消息,就是客户端向代理发送SUBSCRIBE,后接一个表示订阅目标的参数,然后就可以接收到指定目标的消息。如果是发布消息,设备就需要向代理发送publish的消息,然后代理对发送过来的消息进行筛选,把它转发到订阅了这个消息的订阅者。
如果说通俗一点,就相当于b站订阅up主。视频up主和普通观众就是mqtt客户端,b站就是代理。up主发布视频就是mqtt publish,b站收到up主发布视频的信息后将视频发布的信息转发给订阅了这个up主的观众(也是mqtt客户端)。
工作流程
- mqtt客户端和mqtt代理建立连接
- mqtt客户端执行订阅消息或者发布消息操作
- mqtt代理收到消息后将其转发到订阅这个消息的对应的客户端
UDS
UDS协议(Unified Diagnostic Services,统一诊断服务)是诊断服务的规范化标准,比如读取故障码应该向ecu发什么指令,读数据流又发什么指令。
ISO14229-1协议定义了6类功能,26种服务,分别是:
- 1)诊断和通信管理功能单元,包括10,11,27,28,3E,83,84,85,86,87共10种服务;
- 2)数据传输功能单元,包括22,23,24,2A,2C,2E,3D共7种服务;
- 3)存储数据传输功能单元,包括14,19共2种服务;
- 4)输入输出控制功能单元,包括2F服务;
- 5)例行程序功能单元,包括31服务;
- 6)上传下载控制功能单元,包括34,35,36,37,38共5种服务。
不过咱们这个题只用到了一个服务,27的SecurityAccess(安全访问)
frp
(因为我不熟所以顺便学了)
frp是一个体积轻量的反向代理软件,可以使处于内网或防火墙后的设备对外界提供服务,支持 TCP、UDP、HTTP、HTTPS 等多种协议。将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
实现原理
frp 主要由客户端(frpc)和服务端(frps)组成,服务端通常部署在具有公网 IP 的机器上,客户端通常部署在需要穿透的内网服务所在的机器上。内网服务由于没有公网 IP,不能被非局域网内的其他用户访问。隐藏用户通过访问服务端的 frps,由 frp 负责根据请求的端口或其他信息将请求路由到对应的内网机器,从而实现通信。
工作原理
自己做的
题目给了俩靶机端口,一个是连接远程的安卓模拟器,如果模拟器启动了就可以用adb连接另一个端口,也就是车机的系统
flag1
第一个flag其实比较简单,但是我们想复杂了。当时随便找了一圈没东西,以为是要打cve提权。找到了linux内核版本和安卓版本在网上找了一圈编译了一圈,但是一个成功的都没有。当时是用adbwebkit直接查看到了所有的包,才看到有一个奇怪的包把apk dump出来的。
也可以用安卓命令行的命令查看所有包名和包名的apk的路径
1 | pm list packages -f |
都可以。没啥日车的经验,这种指令应该最开始就敲的。
总而言之找到这个com.d3car.factory包后dump出apk,拖进jadx后在layout.xml里找到了第一个flag
非常easy的第一个flag
flag2-未完成
apk拖入jadx后是没有dex代码的,只有资源文件。从manifest中可以得知有三个activity
因为这里上面俩都没设置export=false,所以我们直接am调用就可以调到对应的activity。这里我们直接调用这个pwdAuthActivity。
忘记说了,这里我们用了scrcpy实现了车机窗口的可视化,不然看不到车机显示的东西
1 | am start -n com.d3car.factory/.PwdAuthActivity |
scrcpy中弹出这个界面
随便输入发现会弹出错误提示,这证明肯定是有验证的地方的。
重新找到apk的目录,发现目录下还有一个odex和vdex。用工具vdexExtractor把vdex还原成dex后得到逻辑
非常简单的异或,注意这里的key是pwd界面的上面那个十六进制,而不是这里的20240419
异或得到1dacdfma34560rDa,本以为是flag,但是发现似乎只是能走到下一个tcpdump的流程
这个password的用处就是把这个isChecked过了,因为如果我们直接去am拉起这个tcpdump的activity会失败,只有这样输入后才能正常走到这个页面。
根据activity的代码可知,开启tcpdump后会执行这个指令
1 | /system/xbin/tcpdump -i any -s 0 -w /sdcard/tcpdumplog.pcap -W 1 |
会将log存到/sdcard/tcpdumplog.pcap,运行一会后dump出来pcap文件用wireshark看看流量
发现了很多mqtt协议的流量(当时的副本)
多dump几次可以发现规律。它首先是进行一个connect请求,可以看到client ID,user name,password都给出来了
然后一个connect的ack表示连接确认,这个上面相关知识部分已经介绍过了。随后就是两个publish的操作,对应的订阅的对象是can/514/write,write的数据是固定的。
1 | 022701ffffffffff |
由27 01和27 02和他订阅对象名字的can/514/write容易得到,这是用mqtt传输的uds协议,而27 01和27 02是uds的安全访问的经典的数据
我们将其拆分一下
1 | 长度 服务请求 子功能 密钥 填充 |
01代表的是request seed,02代表的是send key。所以可以得知2dcf28是对应的密钥。
既然都这样了,用户名密码key都有,肯定是需要订阅然后发送信息的。所以接下来的操作首先需要找到方法在远程adb环境中执行mqtt的订阅与publish操作,其次是构造mqtt数据发送给代理来监听其他的mqtt客户端publish的消息。
和代理交互
比赛时我们用的方法是github找能进行mqtt交互的arm架构的elf文件。找了老半天csome✌找到了一个有现成二进制的项目。
https://github.com/rainu/mqtt-shell/releases/tag/v2.3.0
然后兴冲冲的push到经典的/data/local/tmp进行交互,不过很后面的时候发现他pub的输入解析比较奇怪,后面csome✌又用重写了输入的parse然后编译了一份新的,当然是后话。
然后我们这样交互了半天突然有师傅指出可以用adb forward转发出端口来然后在上面跑frps和frpc搞内网穿透,就可以直接用python的mqtt库了。原以为远程adb shell不出网搞不了,但是搞成了,非常尴尬。
构造数据
总而言之能交互了,我们最开始随便输入订阅和publish代码,结果发现什么回显都没有。后面发现是得正确构造数据才能有回应,随便构造的数据不符合uds的数据格式也没法回显。
用csome✌的脚本可以写python和车机的mqtt交互
1 | from paho.mqtt import client as mqtt_client |
按照它正确的格式pub就可以有返回的消息,我们最后也是成功过了验证,但是环境拖的时间太久了最后还是没能继续分析就结束了。
构造数据就是
颅内复现
接下来就是看看wp的做法了,后面的内容是我根据几个wp理解而来的。有不对的地方希望大家能指正。
flag2
首先切换session到扩展会话模式。为什么要切换扩展会话模式,因为从前面dump的pcap包可知服务器一直在发三种包
‘022701ffffffffff’和’0527022dcf28ffff’是鉴权请求,’037f277fffffffff’是服务器反馈当前session模式下鉴权失败,返回的7f是错误代码
我个人理解是有mqtt客户端在一直请求一个需要高一些权限的服务,但是当前的权限不够,所以会返回7f,也就是错误代码。
所以接下来就应该切换session到扩展会话模式
然后就可以获得成功反馈。证明确实是权限不够。
push fscan进去扫描端口可以发现80端口,访问可以下载多个seed2key.dll。socat转发端口后下载可以获取所有的dll。只获得dll没用,我们这里需要获取车机的ECU软件版本号,找到版本号后定位对应版本的seed2key.dll再去逆向获得seed。
获取车机ECU版本号是通过22服务,22服务可以通过id去读取数据,而车机ECU版本号是对应的0xf189
这里发送的顺序如下
1 | 02 01 03 拓展会话 |
返回的key是通过交互返回值来判断的,114514(经典)
中间两步原理如下
发送 22 f1 89后可以获得版本号11.405.10.4.233,找到seed2key.dll后逆向找到seed,然后挨个试31功能(例程控制)去鉴权就行了。
服务器实现的功能看起来是找出来的,由于我没有确切复现而是颅内复现,就先默认是找到31功能了
代码都在wm的wp里,放在参考了有需要的可以直接去看(
最后效果是这样
flag3
这个就有点没想到了,大家看到桌面的where am i located都以为是要拉起pwd验证,没人往gps方向想,不然说不定有可能解出来的
首先find / -name “D3*”可以找到一个证书D3CA.cer和一个奇怪的数据文件D3CALib
尝试安装apk的话会报错INSTALL_PARSE_FAILED_INVALID_APP_STORE_CERTIFICATE,发现是车机在PackageManagerService里面添加了自定义的签名认证。
官方wp意思是爆破D3CALib这个keystore得到密码d^3ctf就可以用这个keystone安装获取定位的apk从而获取gps,但是我不是很理解怎么看出这个是keystore的而且怎么往爆破想的。
还是wm师傅的wp帅。
因为adb指令中
1 | adb shell dumpsys location |
可以获取GPS定位信息,但是只有程序定位过一次后才可以看到,不能主动触发。而misc经典的从拍照照片中找到地址信息的经验告诉我们,如果相机定位权限和加入位置信息都开了的话,我们就可以通过一次拍照获取gps,随之使用dumpsys location获取GPS。
分别给相机开定位权限和加入照片的位置信息后成功用dumpsys location获取经纬度信息,然后去高德搜定位后在评论区就可以找到一个用户,简介就是flag
总结
真可惜没有环境了,后面颅内复现的部分可能比较抽象和流程有些问题,或者说整篇复现的文章都是一路写下来的比较意识流,希望师傅们看到有错误的地方后能批评指正一下。
总的来说很好玩,特别是最后的wm师傅做的flag3,太精彩了。队友们也非常给力,陪我熬夜到了4点多。烧卖yyds!
参考
https://aws.amazon.com/cn/what-is/mqtt/
https://blog.csdn.net/huihuige092/article/details/105287912
https://zhuanlan.zhihu.com/p/146410014
https://zhuanlan.zhihu.com/p/403361038