HowIHackedMyCar 2021款 现代IONIQ (四)Creating Custom Firmware
HowIHackedMyCar 2021款 现代IONIQ (五)CAN Bus分析
本合集共7部分,本篇为第六部分
来源:programmingwithstyle.com,感谢greenluigi1
The Same Car as Before
2022年7月下旬的某个时候,现代汽车移除了所有他们的DAudio2固件下载链接。只留下了一个消息说更新将在9月1日回归。如果我猜得不错的话,现代/摩比斯的某个人读了我的博客,他们想要修复他们的问题。我期待着这一点,我开始准备黑掉最新的固件。
9月1日过去了,页面上没有任何变化,没有新的固件更新可用。几乎一周后,发布了一个新的更新,但只是将日期推迟到9月26日。
然后,为了保险起见,他们再次推迟了日期。这次他们把日期推到了“2022年10月”左右。
但最终在2022年10月24日,他们发布了一组新的固件更新。
深入研究
我迅速下载了新的固件zip文件并提取了文件。
而不是正常的单一的enc_system_package_{version}.zip文件,这里有两个文件:
一个是以预期的名称“enc_system_package_134.100.220927.zip”,另一个的名称是“enc_d2vsystem_package_134.100.220927.zip”。
使用之前找到的zip密码和加密密钥,我能够解压并解密enc_system_package_134.100.220927.zip文件,但它们在另一个文件上不起作用。
好吧,如果我想安装最新的固件,我将不得不重新黑掉它。但我该怎么做呢?
我的第一反应是收集一些信息。这两个提供的zip似乎都是完整的系统更新,所以我决定弄清楚这两个之间的区别。
但是因为现代汽车更换了zip密码,我不得不破解它。使用bkcrack,我能够成功地使用普通zip中的文件解密“d2v”zip。
像往常一样,更新中的大多数文件都是加密的,但system.img文件不是。所以,我在两个system.img文件之间进行了比较。结果它们是相同的。
system.img文件包含IVI通常启动的整个系统映像。这意味着系统更新基本上除了使用的密钥之外是相同的。
我猜他们需要能够用最新的更新更新任何车辆的头部单元。由于仍有许多头部单元仍在运行旧固件,他们需要包括一个使用旧加密密钥的更新包,他们包括第二个文件是为了更新头部单元,这些头部单元已经被更新过,以使用新的密钥。
如果这是真的,那么新的zip密码和加密密钥仍然在我能够阅读的“旧”zip更新中。如果我能弄清楚,我就可以看到是否可以解密新的d2v更新,并像上次一样修改它。
Like a key in a haystack but I least I knew which haystack it was this time
根据我以前的经历,我知道更新逻辑在recovery image(updateboot.img)的/usr/bin/updateagent可执行文件中。提取updateagent后,我开始工作反编译它。
利用我以前对updateagent的了解,我很快找到了新的zip密码。
我没能使用““$09#$모비스98@!OTA$$””密码在Windows上提取d2v update zip,但在linux上使用unzip命令工作得非常好。
现在,我要找加密密钥/iv,以及用于验证固件更新签名的RSA公钥。
更新文件看起来和以前有点不同。现代汽车一定做了一些改变。
至少有一个变化是他们如何在calcAES128()函数中“存储”加密密钥和IV。
我在114/115行上寻找的值是“iv”和“key”。所以我开始逆向以找到它们。
代码首先创建了我所说的“userIV”。这个值是一些传入的值和在unk_86510中找到的数据的组合。
然后,代码将unk_8651C中的数据和相同的传入值结合起来,制造出我所说的“userKey”。
之后,调用了两次AESDecryptWithKey()函数。第一次调用使用先前制作的“userIV”作为密钥和IV,解密“byte_86528”中的值,然后将其存储在“iv”中。
第二次调用使用“userKey”作为密钥和IV,解密“byte_86538”中的值,然后将其存储在“key”中。
这确实有点让人困惑,我本可以使用更好的变量名,但至少现在我有了用于找出真正加密密钥和IV的逻辑链。唯一的未知数是传入的值是什么,它有助于制作“userIV”和“userKey”?
我查看了对calcAES128()函数的引用,看看传入了什么值。看来它是“variantDataStruct”值的前6个字节。
根据我以前的研究,我知道变体结构是用于存储有关车辆的各种信息的数据结构,比如:型号、它拥有什么样的后视摄像头,或者它是否支持HD Radio。
为了弄清楚是什么设置了variantDataStruct,我查找了它的引用。只有另一个receive_variant_data()函数使用了它,
现在这个函数没有很好地反编译,但从我所能收集到的信息来看,它以某种类型的缓冲值(命名为buf)输入,并将其30个字节移动到variantDataStruct。
所以我追查了buf的值,这让我进入了receive_pd()函数。在其中我看到了一篇日志,提到了从micom读取数据。
Micom? More like MiHaveSeenThisBefore.
根据我以前对车辆与CAN总线交互的研究,我知道micom是IVI和车辆CAN总线之间的桥梁。任何时候IVI想要与车辆的其他部分交互,它都必须通过它。
我也知道它的工作原理是通过读写这种格式的数据包:
–Always FF (Byte)
–SID (Byte) – Sender ID
–RID (Byte) – Receiver ID
–Type: (Byte)
–Function (Int16, Big Endian)
–Payload Length (Int16, Big Endian)
–Payload (Array of {Payload Length} Bytes)
所以updateagent必须向micom发送一个数据包请求变体数据,micom以最终进入variantDataStruct的数据做出响应。
通过向上移动调用链,我来到了request_variant_info()函数。在其中,它将一些数据保存到v4变量中,这似乎是一个数据包。(第11-12行)
由于端序的怪异,数据包的两节是相反的。所以数据包是:FF8A010101170001
分解后显示以下结构:
–Packet Start: FF
–Sender ID (SID): 8A
–Receiver ID (RID): 01
–Type: 01Function: 0117
–Payload Length: 0001
–Payload: 98 (Checksum byte)
所以如果我能向micom发送这个数据包并接收响应,我可以取它的前6个字节,并用它来解密加密密钥/IV!
因为在第4部分中,我制作了一个应用程序,我可以用它来与micom接口,所以我用它来发送上面的数据包。由于我是通过micom_mux套接字发送它,而不是直接发送到/dev/tcc_ipc,像更新代理那样,我没有添加校验字节,因为micomd进程会为我做这件事。
我发送了“FF8A010101170001”,收到了以下响应:“FF018A0181171642????????????????????????????????????????????????”(经过审查的有效载荷)一个成功的响应数据包,解码为:
–Sender ID (SID): 01
–Receiver ID (RID): 8A
–Type: 01
–Function: 8117
–Payload Length: 1642
–Payload: {0x1642 bytes}
现在我有了我的variantDataStruct值。
我回到了calcAES128(),找出userIV和userKey的值。
看起来足够简单,但我不信任自己,所以我直接把伪C复制并转换成C#。我填写了variantData,用get变体数据数据包的有效载荷运行它:
它给了我一个密钥和IV,我立即用它们来解密d2v zip中的文件。
我有些事情做错了。我回想起来,意识到每个车型模型的变体数据应该是相对独特的,因为每个车型模型都有不同的功能。如果变体数据在汽车之间不同,它们将有不同的计算出的密钥/IVs,因此会有不同的加密文件。
为了验证这是否正确,我下载了同一版本更新的伊兰特车型,并比较了更新中的一些加密文件与Ioniq车型的。
令我惊讶的是,我比较的所有文件都是相同的。这意味着变体数据结构的前6个字节必须在不同车型之间是相同的。
这次我尝试了很多事情,比如仔细检查我的代码,重新获取变体数据包,甚至尝试暴力破解缺失的6个字节。都没有成功。最终,我意识到变体数据结构不仅包含数据包的有效载荷,还包括数据包的头部。
解决这个问题的关键就在我眼前。响应数据包的头部的开头是我要找的6个字节。
通过将变体数据设置为“FF018A0181171642”并运行代码,得到了以下密钥和IV:
密钥:F4DA05A5E848309EE8377464CF4254A3 IV: 763AC2AFAC9E8EE379788B6CC08612B0
它奏效了!
我现在可以解密整个系统更新。有了这个,我终于验证了两个更新文件(普通的和d2v的)实际上确实是相同的。
我还花时间寻找用于验证固件签名的RSA公钥。
最终我找到了一个我称之为decryptPublicKey()的函数。这个函数像加密密钥一样调用calcAES128(),但它通过函数的不同分支。这第二个分支创建了用作密钥和IV的字节,用于解密“/usr/share/pub_key/updatePublic.info”文件到实际公钥。
我再次复制了反编译的代码,将其转换为C#,运行它,得到了密钥/IV:FF018A018117D0AD830AD5A7DABEDA72
很好,尽管我不确定他们为什么要费心去加密公钥(毕竟它是公钥,公开知道也没关系)。
然后我用找到的密钥/IV解密了公钥:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApTCnVgqDkrE4VrnuEI5GslnmP6GmO03Pg0RuW1wzWsP9BybsNX4GuULSCqr3MsZWOquSO3ftEM9c59LSuTli9LE57zsfohoieeeFaE1mFW5fLio+AQhwpeEVIeOLzt/gX/bcq0c+JKcwEawpOxM0GhMenNdA+jaODjlhK1cqjrTdBQUPcQNBguAbNU6VVufjmAsNTwT7hUVEvdZIllIoRE8c3lQqRy7eV1nQxxQXiGSK/YkcAfjgmaSuLamdgBUcbc8s8wMs6xoXbJqxFXW12zDMXPD98g/mysGZgzfQPfr2w1r4b5q7wvC0kbhrcZFuJ9mmf1cufzB2NgMvzZJ6qQIDAQAB
-----END PUBLIC KEY-----
我也搜索了这个密钥,但看起来它是唯一生成的,与上次不同。
RSA公钥实际上是唯一的。这意味着我不知道私钥是什么,我也不能为自定义固件更新签名。
Rant for Hyundai/Mobis
这一切都表明,现代/摩比斯在这里实践的通过隐藏来保护安全是完全没有必要的。隐藏密钥有什么用,如果你有一个合适的安全设置。即使zip密码、加密密钥/IV和RSA公钥公开发布,他们的固件更新仍然安全,因为他们使用了唯一生成的RSA公钥/私钥对。所有这些只是使原始代码更难阅读和维护。它甚至可能使它更容易错过一些安全问题,因为它更难阅读。想想程序员吧!
Rant over but seriously lets hack it
我对updateAgent的反编译发现了一些非常有趣的事情。
固件更新不必加密。
在我称之为“unzipUpdate”的函数中有两个逻辑分支:
一个分支使用我发现的密码在USB驱动器上解压缩zip,另一个分支则不使用。这是updateAgent处理固件更新的一般逻辑:
对外行人来说,这可能看起来没问题,但对我的来说,我可以看到潜在的漏洞。
其中一个分支根本没有安全检查。
如果我能让系统接受一个未加密的更新文件(system_update.zip),我就可以根据需要修改它。
第一步是制作一个未加密的固件更新。
从一开始,我决定创建一个C#实用程序来帮助自动化创建未加密固件更新的过程。
我创造性地称它为“HyundaiFirmwareDecrypter”。源代码下载
我设计它有独立的“步骤”。每个步骤都是制作解密更新zip的过程之一。
可用的步骤是:
1 提取 – 将d2v格式的zip提取到临时目录。
2 解密 – 如果需要,解密所有提取的文件。(同时将名称以“enc_”开头的文件夹重命名,使其不再有该前缀)
3 安装ADB后门 – 启用Android调试桥TCP服务器,可用于访问root shell。(需要以root/sudo身份运行)
4 计算哈希 – 计算更新中每个文件的SHA512哈希,并将它们编译成update.list文件
5 创建Zip – 压缩所有文件,创建system_update.zip更新文件。
还有一个易于使用的“-a”参数,它运行所有步骤,以制作一个带有ADB后门的未加密固件文件。
我把编译好的x64 linux二进制文件放在我的Kali VM上的一个新文件夹里。然后我把最新的IVI固件更新zip(enc_d2vsystem_package_134.100.220927.zip)转移到同一个文件夹。
然后,我运行了以下命令创建一个带有后门的固件更新:
sudo ./HyundaiFirmwareDecrypter enc_d2vsystem_package_134.100.220927.zip -a
等了一分钟,我手头就有了一个带有后门的、未加密的固件更新。我所要做的就是弄清楚如何刷入它。
I should really stop using the term “all I had to do”
我的头部单元运行的是098.152.211130版本,这是我之前发现的问题被现代汽车修复之前的最后一个更新。
因为它是在他们的安全审计之前,它对可以用来刷非授权更新的功能更加宽松。
在Engineering Mode中有一个选项叫做“USB Image”
“Update ALL”按钮将使IVI重新启动到恢复模式,并从闪存驱动器上刷入system_update.zip文件,按照我上面制作的美丽流程图。
经过一键按压和一点等待,我的IVI就刷入了更新,同时保留了我的root访问权限。(我绝对没有多次搞砸哈希或zip格式,导致无数次调试会议和在汽车和工作站之间来回奔波,哈哈,当然不是。)
Yay?
这对我来说很棒,我的车载主机仍然有后门,并且正在运行最新和最好的固件。但其他人呢?
我决定研究最新的固件,看看它是否可以被黑客攻击,别担心,我找到了另一种方法。
通过我研究车载主机工作原理的努力,我已经反编译了大约47个应用程序中的无数个函数。
其中反编译最多的是工程模式应用程序。它拥有整个系统中一些最有趣的功能。
在代码中,我多次看到在应用程序的变体编码部分提到了一个“Force Update”弹出窗口。根据我反编译的代码,很清楚它的作用。它会尝试在USB驱动器上找到任何看起来像系统更新的zip文件,并重新启动到恢复模式,将该文件作为选定的更新文件。
我甚至看到它有一些密码屏幕可以访问它。(密码是基于MD5 hash的3600)
在尝试弄清楚SetupApp的所有秘密后,我回到了工程模式应用程序,并决定弄清楚如何进入这个屏幕。
基于我在二进制文件中找到的许多字符串,Forced Updat以某种方式与Variant Coding相关联。
因为没有与系统更新相关的条目在Variant Coding界面上可见,我知道有两种可能性:一种是该功能在生产IVIs上被禁用,或者有一些秘密的方式进入界面,就像进入工程模式或经销商模式一样。
我深入研究。我没有找到构建Variant Coding UI的界面,所以它不仅仅是开发专用单元的菜单选项。我也没有太多运气找到像进入工程模式时使用的秘密按钮。所以,我开始寻找使用按键的函数,就像经销商模式一样。
要进入经销商模式,您必须:
– 将音量调至7,然后按下调谐旋钮,
– 将音量调至3,然后按下调谐旋钮,
–将音量调至1,然后按下调谐旋钮
最后一次按下调谐旋钮时,会显示密码屏幕。输入密码(2400)后,将启动经销商模式应用程序。
有趣的是,当我浏览与VariantCodingDisplay相关的函数时,我发现了一个在按下调谐旋钮时触发的函数。
这个函数碰巧相当容易阅读。它似乎遵循与经销商模式相同的模式。
这次组合是7、5、3,而不是7、3、1。
因为此事件直接在VariantCodingDisplay上,我猜测如果我在Variant Coding界面上使用音量和调谐旋钮输入该代码,将显示强制更新密码输入。
我走到我的车那里。我进入工程模式,发现他们更新了密码(而且使它的长度加倍了!)。幸运的是,密码列表很容易在工程模式应用程序中找到,也可以在网上找到。
对我有效的工程模式密码是26031236。
Variant Coding有自己的密码输入,它也得到了一个崭新的、更长的密码。使用上面链接的相同列表,我弄清楚了代码是23060663。
一旦我进入了变体编码菜单,我拨打了秘密的音量代码。
7, *tune knob*, 5, *tune knob*, 3, *tune knob*…
然后瞧:
输入之前找到的密码“3600”后,它快速扫描了我的闪存驱动器,并建议用我的未加密更新进行刷机。
选择“yes”后,车载主机重新启动,并成功重新安装了我的更新文件。这也意味着它没有进行任何版本检查,因为版本是相同的。这意味着这个“强制更新”功能可以用来降级车载主机,如果需要的话。
感谢阅读。
-greenluigi1
之前公众号推广发的都是知识星球,其实还有一部分师傅会去看知识大陆,我们也是一直有运营的,二者发布内容一样,各位师傅哪个渠道看的多自由选择。
第一次推广知识大陆,先推出特价活动:
帮会已有内容
面经方面
汽车数据安全相关
车联网安全测评相关
OTA相关
帮会已更新221条内容
后续帮会持续更新车联网安全相关内容
原文始发于微信公众号(安全脉脉):HowIHackedMyCar 2021款 现代IONIQ (六)How I Hacked my Car Again