If you haven’t read the earlier parts, please do so.

The Same Car as Before

Two summers ago I bought a 2021 Hyundai Ioniq SEL. It is a nice fuel-efficient hybrid with a decent amount of features like wireless Android Auto/Apple CarPlay, wireless phone charging, heated seats, & a sunroof.

One thing I particularly liked about this vehicle was the In-Vehicle Infotainment (IVI) system. As I mentioned before it had wireless Android Auto which seemed to be uncommon in this price range, and it had pretty nice, smooth animations in its menus which told me the CPU/GPU in it wasn’t completely underpowered, or at least the software it was running wasn’t super bloated.

As with many new gadgets I get, I wanted to play around with it and ultimately see what I could do with it.

And I did, which lead to the adventure that is the original How I Hacked my Car Series.

But why does this post exist if I already hacked it?

Because Hyundai

Sometime in late July 2022, Hyundai removed the links to all of their DAudio2 firmware downloads. Leaving only a message that said the updates would return on Sepember 1st. If I had to guess, someone at Hyundai/Mobis read my blog and they wanted to fix their issues. In anticipation of that I started to gear up to hack the latest firmware. D-Audio2V Update Downtime

September 1st came and went with no changes to the page, no new firmware updates available. After almost a week a new update was posted, but only to push back the date to September 26th. D-Audio2V Update Downtime Again

Then for good measure they did it once more. This time they pushed the date back to “~October 2022”. D-Audio2V Update Downtime Again Again

But finally on October 24th, 2022 they released a new set of firmware updates.

Looking Inside

I quickly downloaded the new firmware zip and extracted the files.

Wait file(s)?

Instead of the normal, singular enc_system_package_{version}.zip file where were two files: Multiple Firmware Zip Files

One with the expected name of “enc_system_package_134.100.220927.zip” and another one with the name “enc_d2vsystem_package_134.100.220927.zip”.

Using the previously found zip password and encryption keys I was able to unzip and decrypt the enc_system_package_134.100.220927.zip file, but they didn’t work on the other file.

Hyundai changed the keys.

Same Game, New Round

Ok, if I wanted the latest and greatest firmware, I would have to rehack it. But how could I do that?

My first instinct was to gather some information. Both of the provided zips appeared to be full system updates, so I decided to figure out the difference between the two.

But since Hyundai changed the zip password I had to crack it. Using bkcrack (The tool I learned in Part 1) I was able to successfully decrypt the “d2v” zip using the files from the normal zip.

Like usual, most files in the update were encrypted, but the system.img file wasn’t. So, I ran a comparison between the two system.img files. They turned out to be identical.

The system.img file contains the whole normal system image which the IVI usually boots from. This means the system updates were basically identical except for the keys used.

I guess they need to be able to update any vehicle’s head unit with the latest update. Since there are many head units that are still running the old firmware, they need to include an update package that uses the old encryption keys, and they include the second file to update head units that have already been updated previously to use the new keys.

If this was true, the new zip password and encryption keys would still be in the “old” zip update that I could read. If I could figure those out I could see if I can decrypt the new d2v update and potentially modify it like I did last time.

Like a key in a haystack but I least I knew which haystack it was this time

From my previous adventures I knew the update logic was in the /usr/bin/updateagent executable in the recovery image (updateboot.img). After extracting updateagent I got to work decompiling it.

Using my previous knowledge of updateagent I quickly figured out the new zip password. New Zip Password

I wasn’t able to extract the d2v update zip using the “$09#$모비스98@!OTA$$” password on Windows, but using the unzip command in linux worked flawlessly.

Now to find the encryption key/iv, as well as the RSA public key used to verify the firmware update’s signature.

Quick Maths

The update agent binary looked a bit different compared to before. Hyundai must have made some changes.

At least one of the changes was how they “stored” the encryption Key and IV in the calcAES128() function. The key in updateagent

The values I was looking for were “iv” and “key” on lines 114/115. So I started reverse engineering the code to find them.

The code started by creating what I called “userIV”. This value is a combination of some passed in value and the data found in unk_86510. How userIV is calculated in updateagent

Then the code combines the data from unk_8651C and the same passed in value to make what I called “userKey”. How userKey is calculated in updateagent

After that, the AESDecryptWithKey() function is called twice. The first call decrypts the value in “byte_86528” using the previously made “userIV” as the Key and IV and then stores it into “iv”.

The second call decrypts the value in “byte_86538” using “userKey” as the Key and IV and stores it into “key”. How iv and key are calculated in updateagent

This is a bit confusing for sure, and I could’ve used better variable names, but at least I now had the chain of logic used to figure out the real encryption Key and IV. The only unknown left was what value was passed in that helps make “userIV” and “userKey”?

I looked at the references to the calcAES128() function to see what value was passed in. It appears that it was the first 6 bytes of the value of “variantDataStruct”. calcAES128 function call getVariantStruct function call

Based on my previous research I knew the variant struct was a data structure used to store various information about the vehicle like: the model, what type of rear-view camera it has, or if it supports HD Radio.

To figure out exactly was was setting variantDataStruct I looked for references to it. There was only 1 other function which used it, receive_variant_data(). receive_variant_data function

Now this function wasn’t decompiled the nicest but from what I could gather it took in some type of buffer value (named buf) and moved 30 bytes of it to variantDataStruct.

So I chased the value of buf, which lead me to the function receive_pd(). In it I saw a log that referred to reading data from the micom. receive_pd function

Micom? More like MiHaveSeenThisBefore.

Based on my previous research into the vehicle interactions with the CAN bus I knew the micom was the bridge between the IVI and the vehicle’s CAN bus. Any time the IVI wants to interact with the rest of the vehicle, it has to go through it.

I also knew that it worked by reading and writing packets in this format:

  • 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)

So updateagent must send a packet to the micom requesting the variant data and the micom responds with the data that eventually ends up in variantDataStruct.

By moving up the call chain I came to the request_variant_info() function. In it, it saves some data to the v4 variable which appears to be a packet. (Lines 11-12) request_variant_info function

Due to endian weirdness, the two sections of the packet are reversed. So the packet is: FF8A010101170001

Splitting it out reveals the following structure:

  • Packet Start: FF
  • Sender ID (SID): 8A
  • Receiver ID (RID): 01
  • Type: 01
  • Function: 0117
  • Payload Length: 0001
  • Payload: 98 (Checksum byte)

So if I could send this packet to the micom and receive the response, I could take the first 6 bytes of it and use it to decrypt the encryption key/IV!

Since in Part 4 I made an app which I could use to interface with the micom, I used to to send the above packet. Since I was sending it through the micom_mux socket and not directly to /dev/tcc_ipc like how update agent does it, I did not add the checksum byte since the micomd process would do that for me.

I sent “FF8A010101170001” through and received the following: “FF018A0181171642??????????????????????????????????????????…” (censored payload) a successful response packet which is decoded to:

  • Packet Start: FF
  • Sender ID (SID): 01
  • Receiver ID (RID): 8A
  • Type: 01
  • Function: 8117
  • Payload Length: 1642
  • Payload: {0x1642 bytes}

I now had my value of variantDataStruct.

I went back to the calcAES128() to figure out the values of userIV and userKey. The key in updateagent

It looked simple enough but I did not trust myself, so I just copied the pseudo C and converted it to C#. I filled in variantData with the payload of the get variant data packet and ran it: CalculateSystemAESKey function

It gave me a key and IV which I prompty used to decrypt the files from the d2v zip. Bad Decryption Results

“bad decrypt”? :(

I had something wrong. I thought back on it and realized that the variant data for each vehicle model should be relatively unique, since each vehicle model has different capabilities. And if the variant data was different between cars, they would have different calculated keys/ivs and therefor different encrypted files.

To check if this was true I downloaded the same update version for the Elantra and compared some of the encrypted files in the update to the one for the Ioniq.

To my suprise, all of the files I compared were identical. This meant that the first 6 bytes from the variantData Struct must be identical between models.

After this I attempted many things like double checking my code, re-getting the variant data packet, even trying to brute force the missing 6 bytes. All with no success. Eventually, I realized that the variantDataStruct contained not only the payload of the packet but the header of the packet as well.

The key to solving this was right in my face. The start of the header of the response packet was the 6 bytes I was looking for.

By setting the variant data to “FF018A0181171642” and running the code, it gave me the following Key and IV:

Key: F4DA05A5E848309EE8377464CF4254A3

IV: 763AC2AFAC9E8EE379788B6CC08612B0

And it worked! Good Decryption Results

This was all very necessary for the security of the IVI and not at all convoluted.

66.6% Completed

I now could decrypt the entire system update. With that, I finally verified that the two update files (the normal one and the d2v one) were in fact identical.

I also spent time looking for the RSA Public Key which is used to verify the firmware signature.

I eventually found a function which I called decryptPublicKey(). This function called calcAES128() just like the encryption keys, but it instead goes through a different branch of the function. This second branch creates the bytes which are used as the key and IV to decrypt the “/usr/share/pub_key/updatePublic.info” file into the actual public key. calcAES128 Public Key Branch decryptPublicKey function

I once again copied the decompiled code, turned it into C#, ran it and it gave me the key/iv: FF018A018117D0AD830AD5A7DABEDA72

Nice, although I am not sure why they even bothered to try to encrypt the public key in the first place (It is a Public Key after all, it is fine if it is publicly known.).

I then used the found key/iv to decrypt the public key:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApTCnVgqDkrE4VrnuEI5G
slnmP6GmO03Pg0RuW1wzWsP9BybsNX4GuULSCqr3MsZWOquSO3ftEM9c59LSuTli
9LE57zsfohoieeeFaE1mFW5fLio+AQhwpeEVIeOLzt/gX/bcq0c+JKcwEawpOxM0
GhMenNdA+jaODjlhK1cqjrTdBQUPcQNBguAbNU6VVufjmAsNTwT7hUVEvdZIllIo
RE8c3lQqRy7eV1nQxxQXiGSK/YkcAfjgmaSuLamdgBUcbc8s8wMs6xoXbJqxFXW1
2zDMXPD98g/mysGZgzfQPfr2w1r4b5q7wvC0kbhrcZFuJ9mmf1cufzB2NgMvzZJ6
qQIDAQAB
-----END PUBLIC KEY-----

I also googled the key but it appears it was uniquely generated, unlike last time.

0% Completed?

The RSA public key was actually unique. This meant I had no idea what the private key is and I wouldn’t be able to sign custom firmware updates.

Rant for Hyundai/Mobis

This all goes to show that the security by obscurity that Hyundai/Mobis is practicing here is completely unnecessary. Hiding the keys doesn’t matter if you have a proper security setup. Even if the zip password, encryption key/iv, and the RSA Public Key are publically published, their firmware updates would still be secure since they used a uniquely generated RSA Public/Private key pair. All this does is make the original code harder to read and maintain. It may even make it easier to miss something security wise because it is harder to read. Think of the programmers!

Rant over but seriously lets hack it

So nice job Hyundai/Mobis on security, anyways… Through my decompiling of updateAgent I found something very interesting.

Firmware updates do not have to be encrypted.

In the function that I called “unzipUpdate” there are two logical branches: unzipUpdate function

One branch unzips the zip on the USB drive with the password I found, and the other unzips it without one. Here is the general logic of how updateAgent handles firmware updates: Firmware Update Flowchart

To the untrained eye this might seem fine, but to my expertly trained Security Eye™️ I could see a potential vulnerability.

One of those branches just doesn’t have any security checks.

If I could make the system take an unencrypted update file (system_update.zip) I could modify it however I wanted.

95%?

The first step to flashing an unencrypted firmware update is of course to make an unencrypted firmware update.

From the get-go I decided to make a C# utility to help with automating the process of creating an unencrypted firmware update.

I creatively called it “HyundaiFirmwareDecrypter”. Source Download

I engineered it to have discrete “steps”. Each step is one, well step in the process to make the decrypted update zip.

The available steps are:

  1. Extract - Extracts a d2v formatted zip to a temporary directory.
  2. Decrypt - Decrypts all of the extracted files if necessary. (Also renames the folders that start with “enc_” to not have that)
  3. Install ADB Backdoor - Enables the Android Debug Bridge TCP server which can be used to access a root shell. (Needs to be run as root/sudo)
  4. Calculate Hashes - Calculates the SHA512 hashes for every file in the update and compiles them into the update.list file
  5. Create Zip - Zips up all of the files, creating the system_update.zip update file.

The utility was made to run only the steps specified. This makes it easy to do custom modifications as you can run the first two steps; Extract and Decrypt. Then make the changes you want to the system files, followed by running the Calculate Hashes and Create Zip steps which will compile your changes into the firmware update file.

Or if you want a more “one-click-solution”, you run the “Install ADB Backdoor” (-i) step, which will enable the ADB TCP server in the firmware update. This means you can simply connect to the head unit’s Wi-Fi or use an ethernet connection and the adb tool to get a root shell. For more information see How I Hacked my Car Guides: Creating Custom Firmware

There is also an easy to use “-a” argument which runs through every step to make an unencrypted firmware file with the ADB backdoor.

I took the compiled x64 linux binary and threw it in a new folder on my Kali VM. I then transfered over the latest firmware update zip for my IVI (enc_d2vsystem_package_134.100.220927.zip) into the same folder.

HyundaiFirmwareDecrypter Folder Setup

I then ran the following command which create a backdoored firmware update:

sudo ./HyundaiFirmwareDecrypter enc_d2vsystem_package_134.100.220927.zip -a

After a minute of waiting I had a backdoored, unencrypted firmware update in hand. All I had to do was figure out how to flash it.

I should really stop using the term “all I had to do”

Ok, ok I am being a little dramatic with the subtitle here. Flashing the update on my car was actually pretty easy.

My head unit was running version 098.152.211130, the last update before Hyundai fixed the issues I previously found.

Because it was before their security audit, it was more lax with the features that could be used to flash unauthorized updates.

In the Engineering Mode there was an option called “USB Image” Engineering Mode USB Image Update Screen

The “Update ALL” button would make the IVI reboot into recovery mode and flash the system_update.zip file from the flash drive, following the beautiful flowchart I made above.

After one button press and a little bit of waiting I had my IVI flashed with the latest and greatest update all while keeping my root access. (I definitely did not mess up the hashing or zip format multiple times leading to countless debugging sessions and trips back and forward between the car and my workstation haha of course not.)

Yay?

This is great for me, I still have my head unit backdoored and it is running the latest and greatest firmware. But what about everyone else?

I decided to look into the latest firmware and see if it could be hacked, and don’t worry, I found another way in.

Through my endevors into researching how the head unit works I have decompiled countless functions in countless (~47) applications.

One of the most decompiled is the Engineering Mode app. For good reason too, it has some of the most interesting functionality in the whole system.

And throughout the code I saw many mentions of a “Force Update” popup in the app’s Variant Coding section. Based on the code I decompiled it was clear what it did. It would try to find any system update-looking zip file in the usb drive and reboot into recovery mode with that as the selected update file.

I even saw that it had some kind of password screen to access it. (And the password was 3600 based on the MD5 hash) isPasswordCorrect function on ForcedUpdatePasswordDisplay

After a small adventure/tangent in trying to figure out all of the SetupApp’s secrets I came back to the Engineering Mode app and decided to figure out how to get to this screen.

Based on many strings I found in the binary, the Forced Update screen was somehow linked to the Variant Coding screen. onForcedUpdatePopupYesPressed function on VariantCodingDisplay

Because no entries related to system updates were visible on the Variant Coding screens I knew there were to possibilities: One was that the feature was disabled on production IVIs, or there was some secret way to get into the screen, like how Engineering Mode or Dealer Mode are entered.

I looked deeper. I did not find the screen mentioned where the Variant Coding UI was built, so it wasn’t just a menu option for development-only units. I also did not have much luck in finding hidden buttons like the ones used to get into Engineering Mode. So, I started looking for functions that used key presses like Dealer Mode.

To get into Dealer Mode you have to:

  • Set the volume to 7, the press in the tune knob,
  • Set the volume to 3, the press in the tune knob,
  • Set the volume to 1, the press in the tune knob

One the tune knob is pressed for the final time, a password screen is displayed. Once the password (2400) is entered the Dealer Mode app is started.

Funnily enough, while looking through the functions related to VariantCodingDisplay, I found one that fired when the tune knob was pressed. onTuneEnterBtnReleased function on VariantCodingDisplay

This function happened to be fairly easy to read. It seemed to follow the same pattern that Dealer Mode used.

This time the combination was 7, 5, 3 instead of 7, 3, 1.

Because this event was on VariantCodingDisplay directly I guessed that if I were to enter in that code using the volume and tuner knobs while on the Variant Coding screen, the Forced Update password input would show.

To my car I went. I entered Engineering mode only to find they updated the password (And made it twice as long!). Luckily the password list is easily found in the Engineering Mode app and is also listed in many places online.

The Engineering Mode password that worked for me was 26031236.

Variant Coding has its own password entry and that also got a shiny, new, longer password. Using the same list I linked above I figured out the code was 23060663.

Once I was in the Variant Coding menu, I dialed in the secret volume code.

7, *tune knob*, 5, *tune knob*, 3, *tune knob*…

And voilà: Forced Update Password Entry

After entering the previously found password “3600” it quickly scanned my flash drive and suggested to flash my unencrypted update. Forced Update Password Confirmation Modal

After selecting “Yes” the head unit rebooted and successfully reinstalled my update file. This also means it did not do any version checks as the versions were the same. This means this “Force Update” feature could be used to downgrade the head unit if need be.

The Result

D-Audio 2V based head units are now once again open for tinkering and modifications.

But wait, has this all happened just for it to be Doomed in the end?

A Little Message to Hyundai/Mobis

I know I have very little sway in your company’s business decisions, and I know you already have big plans for ccOS, but I would appreciate it if you would listen nonetheless.

I think this platform you developed is really cool and has a lot of potential. But I believe having this platform locked down is a mistake.

Sometime in the future, maybe not today but not too far from now, you will stop supporting these head units. Whether it be just not adding new features (I think we are already past this), not fixing bugs, or just not supporting the platform in general, there will be a time where these neat gadgets will no longer be supported.

The head unit is where remote start, remote climate controls, and remote monitoring are done. Not to mention where navigation, and music playback occurs.

If BlueLink just stops working one day, if Android Auto, Apple CarPlay, or simple music playback just starts getting glitchy, or if a bug like the Year 2038 problem occurs. The value, longevity, and usefulness of our vehicles will be severely degraded.

But unlike most other parts of the vehicle that we can just replace when they break, we won’t be able to do this for the head unit. Don’t get me wrong, I am aware aftermarket head units exists, but due to the proprietary nature of them nothing will actually be able to replace the original head unit in terms of its functionality, especially the tight vehicle integration.

This leads to my suggestion. If you were to open up the platform, vehicle owners, or even other businesses could pick up where you left off and support it for much longer.

By “opening up the platform” I mean make the system support externally developed applications and/or externally developed system updates. You would not need to open source all of your code. Although that would be nice, I understand the want or need to protect the company’s (Or other company’s) intellectual property.

I also understand everything is not black and white, I know there are also considerations like reducing legal liability. But then again, any idiot can duct tape a tablet to their car, plug in a OBD2 dongle and be just as dangerous (If not more so). If the platform is opened, ccOS and other available APIs can in fact be a safe guard against anything that would negatively impact the vehicle. Making developing apps for the head units the safer choice compared to trying to play around with the CAN bus directly. Because of all of this and more, I recommend opening up the platform.

Here is a little layout of what I think would need to be done to make the platform “open”:

  • Provide the system header files so applications can be developed.
    • This can be as simple as once again including header files in the system updates, like you use to do.
  • Provide a way for applications/firmware update to be installed.
    • Simplest option is re-enabling the “Update ALL” button in Engineering Mode to allow for unencrypted updates. (Or keep the current Forced Update feature.)
    • I also should say that this should not require express Hyundai/Mobis permission. Like requiring a specific key from Hyundai/Mobis to “unlock” the head unit. As this would still require you to maintain this feature and it wouldn’t help people who need to unlock it after you stopped giving out keys.

There is also a number of things that could be done that would be nice and help with opening up the platform, but would not be necessary by any means.

  • Making the apps list dynamic and allow for new apps to be added and launched from the home screen.
    • This could be easily done by loading the available apps from a .json file.
  • Making ccOS documentation publicly available to help in the development of ccOS integrated applications.
  • Continued development of ccOS and ccOS APIs for existing vehicles.
  • Publicly available documentation on how to setup a development environment for D-Audio 2V applications.
  • Making some of/parts of the built in apps open source to serve as examples.
  • Making some kind of installer system to make adding or updating apps easier.
    • This may be necessary to develop in the future to allow for live OTA updates anyways.
  • A custom, Hyundai/Mobis run marketplace for applications, including approved apps made by other developers (Like an App Store)
  • Have a tier in BlueLink or otherwise to pay for just SIM data (+ a small surcharge?). It would allow applications to be developed that use the internet and also give you a small but reliable continued revenue stream, even after BlueLink shuts down.

In a time where companies are locking down their platforms and reducing functionality, you could be the company that breaks the mold. Hyundai has the chance to be a leader here and guide the industry to be more open as a whole, benefiting everyone involved.

Of course, time will always continue marching onward and some day the head units will become outdated and will no longer be serviceable by anyone but until then, please allow the community to support these devices.

Thanks for reading,

-greenluigi1