 Hello everyone, my name is Christopher Wade and today I'm going to be talking about breaking secure bootloaders. The purpose of this talk is to outline how smartphones use signature verification mechanisms to protect their firmware, both the core chipset and the peripheral hardware. This is implemented at the bootloader level, which provides facilities for firmware updates as well, and often other interfaces for management of the device. We're going to be out loading two unices and two different chips used by smartphones, which can be exploited to bypass these signature protections. A bit about me before we start, my name is Christopher Wade and I'm a security consultant at Pentest Partners, where I mainly work in hardware and IoT testing. So the first project we'll be discussing is the Qualcomm Snapdragon 660 bootloader. I purchased an Android phone to do some mobile research, which comes into similar chip to this, and I realised that I needed root access in order to use all of my testing tools. Now root access on an Android smartphone can only really be achieved in a meaningful way by unlocking the bootloader, which disables signature verification mechanisms and allows you to modify the Android boot image in order to add your own functionality. This required an unlock tool from the manufacturer in this case, which had some different limitations on it. So manufacturers often add protections to the bootloader on top of what Qualcomm provides in order to require custom tooling provided by them to allow for bootloader unlocking or to remove the ability to unlock the bootloader entirely. So this often requires creating a user account for that particular brand and waiting for a period of time between 7 to 28 days. Unlocks are performed using custom USB fastboot commands, fastboot being the USB protocol used for Android bootloaders, which we'll be discussing in a second, and there are a few reasons why they would do this, such as preventing inexperienced users from being tricked into deliberately deploying malicious software to their phones, stopping third parties from deploying malicious software to their phones as part of the supply chain, or allowing the manufacturer to track who is unlocking their bootloader in the first place. So in my case, I found that the manufacturer had implemented bootloader unlocking protection in a very standard way, which was to add a signature verification mechanisms. So using the USB P cap tool, which allows you to man in the middle of the USB from a Windows host to USB devices, I had analyzed how this was performed and I identified that what happened was using the tool, it would send fastboot commands, which included a 256 byte signature downloaded from the manufacturer's servers after the timeout, which was found to be matched against some internal data on the phone. When this was verified, it unlocked the bootloader and allowed the device to be used. I decided, because it had this timeout, it would be very interesting to see if I could bypass this restriction and not unlock the bootloader before the timeout. So as this was seven days, I decided to set myself a challenge to break the bootloader on an older smartphone in this series before the end of the seven-day waiting period. So the target device I went for was a mid-range phone released in 2017, which used the Qualcomm extractor and 660 chipset with the ARM64 architecture. I'd previously unlocked the bootloader on this phone by the same manner, but because I could lock it again for the project just using fastboot commands, I decided to use it as a target. The bootloader had been modified with the same functionality as the phone that I started with. So fastboot is a command interface used by most Android bootloaders. It's a basic USB interface which sends raw text commands to the phone and gets raw text commands back in general, apart from very specific circumstances. These include commands like reboot, flashdata, writedata, and some specific OEM functionality that can be added by OEMs just by the virtue of the fact that the bootloaders are open sourcing and being modified by them. It can be implemented very simply using standard USB libraries. You can send bulk requests, including the ASCII commands, and get human-readable responses back via asynchronous reads via the bulk interface. There are libraries that exist for this purpose, but most of them are unnecessary. The ABL bootloader provides this fastboot USB interface and verifies and executes the Android operating system as well. So it's part of a multi-stage boot process which starts with the phone booting, loading this bootloader into RAM, which then verifies the Android boot image and gets things going. It can be accessed via ADB, doing ADB reboot bootloader, or using button combinations on boot, which is usually the volume down button and power. It's stored in the ABL partition on the device. Qualcomm's base bootloader for this has source code available, which can be analyzed, but because vendors often modify this, this can only be used as a reference rather than building from scratch. So the bootloader itself is stored as an elf in the ABL partition as outlined, but contains no executable code. Instead, it contains a UEFI file system. Using the tool UEFI firmware parser, I could find a portable executable called Linuxloader, which could be loaded directly into IDA for analysis. FastCube commands in this were stored on a table for function call-max. So it'd be a text command followed by a function call-back just in order, which allowed me to analyze every single command that was available on the phone and identifying custom ones, hidden ones, or any non-standard functionality that would be in the functions that were already there. Because this is quite of a boast bootloader, which presents a lot of strings back, it was really easy to analyze what each part of the bootloader was doing just by analyzing what the strings were saying. I'd identified that the flash command, which usually only allows for flashing of data partitions on the Android phone, had been modified so that on this particular phone, it could flash a specific extra bootloader, a specific extra partition called CRCList. These partitions were handled differently and did some parsing and all sorts of things on string data sent to it and were not standard as directly created by Qualcomm. As such, I thought there'd be potential for some kind of memory corruption or partition overwrite in this custom functionality, so I decided to start my focus there. I'd made some assumptions when I was building my fast boot tool, which meant that my command sequence wasn't exactly correct. What's meant to happen is that you send a download command with a payload size, then send a raw binary of the payload and then the flash and then the partition you want to send to. I didn't quite understand this because I hadn't read the documentation at this point, so what I tried was sending the flash command and the partition I wanted to go to and then sending a payload. Also, in my code, I'd left an incorrect flash command after that payload in the command sequence. This resulted in the bootloader crashing after sending the second flash command. The lack of a download command was deemed to be the reason for this, meaning that it was likely that because I was sending a huge buffer without any data previous to that, it was not handling it as data and was actually handling it as a very large command. Analysis of the crash showed that the USB connectivity on the phone stopped functioning entirely. I could unplug the phone and plug it back in again, and it wouldn't enumerate over USB, and it required a hard reset back to the bootloader, so holding down power for 10 seconds. I sent a smaller payload size in the same manner, but this didn't crash the phone. What I attempted to do was provide a binary search approach where I'd take a large payload and a small payload and get the size in between of those and send it and try and find the maximum size that would be sent without a crash. By rebooting and sending sizes between a minimum and maximum value, I found that the maximum size would be 0x11bae0. Because of this unusual memory size, I assumed that this was going to be a buffer overflow, which I could probably exploit to bypass some of the functionality on the bootloader or add my own functionality. However, with no debugging functionality available on the phone, identifying what memory region was being written to or anything like that would be very difficult. The bootloader was found to also use stacking areas which could be identified using the strings in the code and looking at the source code of the bootloader, which could be potentially triggered by this. I decided to manually increment the next byte, so sending 0x11bae1 bytes of data and setting that last byte to different bytes of data to see what happened. And I found that the last byte that was meant to be 0xff. So I looped through until I got to ff and I found that that didn't crash the phone when I sent that payload, or at least didn't crash it immediately. By constantly power-cycling, incrementing that byte volume, moving to the next byte when I found a valid one that didn't crash the phone, I could probably create a reasonable facsimile of the memory that was in the bootloader at that point. It wouldn't be the exact memory necessarily, but it would be enough not to crash the bootloader and I could probably use that to find what kind of data it was and possibly modify it to gain code execution. But I required a way of automating this process rather than doing it by hand by manually taking the power on and off. It was suggested by my colleague that we could take the battery out of the phone and use a USB relay to turn the power on and off while automating power-cycling on the phone. However, this would require removing glue from the case to access the battery, and that hardware attack slide that was something I just didn't want to go for at this point. Instead, I wrapped a hair tie around the power and volume download buttons of the phone, which caused the boot loop, so it would constantly reboot the phone while allowing enough time for the USB to be enumerated and for a payload to be sent. So with sufficient time to test whether the overload flow was valid. My custom fastboot tool was modified to do this, so it would loop around, wait for the thing to crash, and then start again, and made it verify two key events. First, I wanted it to send a flashing-failed response, so because I sent an incorrect partition as my last command, which was causing the crash, it would tell me that flashing was not allowed on a locked bootloader. I also checked whether the bootloader would crash after this. Each iteration of this took about 10 to 30 seconds on reboots, but eventually, this would allow me to create some kind of payload. So I left the phone overnight before this loop, and by the end of the morning the next day, I got 0x34 bytes of data, which didn't crash the phone. Because there were repeated word values and the lack of any default stack canary, which I identified in the code, I thought that this would probably not likely to be the stack. However, I did notice that all of the 32-bit words that were outlined were actually valid ARM64 upcodes. Most upcodes, while valid operations, would probably not be the same as the bootloader, just due to the fact that things like stack handling in general and management of how the code is running doesn't necessarily need to be an exactly right. For instance, if there's things relating to stacks that's not actually handled, or if there's registers that are set but never read from, they could be whatever we want it to be, and it wouldn't really cause a problem. However, pushing the stack pointer down and any branch and link operations would probably have to be pretty accurate. I decided to search for those in IDA using the values that have been generated by my code. There's a few reasons why this search didn't work, however. So there's often unused bits that can be flipped on operations which don't alter functionality or only alter it superficially. Also, registers can be accessed in both 32 and 64-bit mode, meaning that if a register was being used as a 32-bit register but accessed in the code as a 64-bit register, accidentally setting it as a 32-bit one wouldn't cause any problems and everything would work as intended. Also, branch instructions can have conditions for jumping, like zero, not zero, et cetera. And if these were identified but it didn't cause any issues, then also this would mean that text searching for these opcodes just wouldn't work. So I decided to identify similar instructions. I decided to use the branch and link instruction because it was most likely to be unique in the code. Because stack pushing is often very accurate but also about the same on most functions that are compiled in ARM64, I thought a branch and link instruction which would jump to specific relative addresses would probably be easier to note. I performed a text search while removing the first nibble from the opcode to see if I could find any branches that were in a similar relative address space to it. For instance, if there were two string comparison functions that were next to each other and I was jumping to one and not the other. I identified a single value instruction with this branch and link without the first nibble being the same. And this was in the parser for the CRC list partition, which was the partition that we were going to access at the start of this whole process. And I found that the opcodes were very similar in both the start and end, and pretty much everything in between, apart from a few very superficial things. So analysis of the op sets in IDA compared to the buffer overflow I performed found that it was overwriting that entire bootloader at 1.0.1.0.0.0, which was possible because the bootloader was executed from RAM as demonstrated by the overflow. Because I could overwrite the code as it was running, it meant that I was probably overwriting memory that could be modified as needed. I took the original bootloader binary, which I found in the ABR partition and extracted for this purpose, and could overwrite it over the partition that was already in, or rather the RAM that was already there. I mean that could perform the buffer overflow without actually breaking the bootloader and keeping everything working. Because of this, I was essentially overwriting it with unsigned code and could modify any aspect of the bootloader to run anything I wanted to. So I wanted to unlock the bootloader, which was the entire purpose of this project. So what I first did was looked into how it was performing the unlock. So what it would do was verify the RSA signature as outlined at the start, and then send a fast boot unlock command. What I wanted to do was make it so it jumped past the RSA check as part of the signature verification and just jumped straight into that unlock. I could use a simple branch instruction, even from the CRC list code that I was in, to jump to the relative address of the bootloader unlock function. Online ARM64 assemblers, such as Shellstorm, can be used to do this and generate very quick, rapid code that will just allow you to jump from one piece of code to another. It would be difficult to debug this, but if I had achieved unlocking the bootloader, I'd know fairly simply because the phone would reboot, and then it would tell me that the partitions had corrupted because I was going from a locked bootloader to an unlocked one, and we'll be discussing that again soon. So here's a quick outline of this attack going. So it's just a piece of C code that runs and send the buffer overflow, send the flash CRC list command, and then send an OK response. Now that OK response was actually from the unlock command and not from the flash CRC list command because I'd essentially jumped from one function to another. And then because I hadn't handled the stack properly at this point, I also got a big chunk of data, which was invalid, just due to the fact that this was very quickly and rapidly implemented from what it has. So because I could read the phone, I could deploy custom recovery images to it and read the phone itself. However, because Qualcomm chips can encrypt the user data partition using an internal keys, if I went from a locked and unlocked bootloader, I would not actually be able to access my old data. So essentially I'd have to erase my entire phone in order to unlock it and then use root access. I did find that with a few code changes, I could dump RAM off the phone and some other memory address spaces. However, I couldn't use this for things like a cold boot attack because I couldn't access any RAM outside of what is specifically partitioned for the second stage bootloader we were attacking. I achieved this entire process over four days, which was just short of the seven days that would be required for this bootloader and locking using the tool. But also, I attempted to replicate its vulnerability on the newer phone, which used an SDM665, so just the next chip up from what I had. However, these weren't effective, meaning that this vulnerability was probably only workable on the phone I had. However, I was able to procure a special second smartphone, which also used the SDM660. This was from a different manufacturer and released at a different time and had actually had all bootloader and locking functionality disabled completely. So even though you could access fastboot, you couldn't do fastboot unlock with any tools available. I found that it was using a similar signature verification mechanism to the original phone. However, the keys and all the tools for this were not available publicly. Using an OTA image to this phone, however, I could analyze the bootloader again, so rather than get it from the partition, I could download it from the Android zip image that was used for updates and just extract it that way. This meant that I could find the code which blocked the bootloader unlock and it just had a response that said cannot unlock bootloader without having the correct signature. And I also found that there were no hidden bootloader commands identifying the device, meaning I couldn't just unlock the bootloader via anything secret. So initially, I tried the old crash attempt, so sending the same data I had sent before to see if it crashed. However, the device still functioned, implying that the vulnerability may not be present. However, I then sent a much larger payload size, and it just crashed the phone, implying that the vulnerability was still there, but the memory layout may have been different to the original. I did some manual analysis, as I did before, using high-smodern larger payload sizes, and found that it was overwritten after 40300 bytes rather than the 10100 bytes used by the original phone. With this, I could really quickly create a bootloader unlock tool. So a single branch instruction was identified which sent an error response when knocking the bootloader, saying whether you couldn't verify the signature or whether you could just unlock the bootloader. I could replace this with a single knock instruction in the bootloader and bypass this check. I could then route the phone using Magisk and have full access to all of its capabilities. Because this vulnerability was found on two different phones from different manufacturers using the same Qualcomm chip, I disclosed it directly to Qualcomm because it was likely to be on every phone that used the SDM660. Now, bootloader access isn't required for users in contexts where unlocking isn't permitted. A lot of phone manufacturers nowadays just don't allow you to unlock your bootloader at all, just because they don't want you to or they don't want you to do research. It's possible to just disable fastboot access because it is open-source and they can modify it as they want to to bypass all the USB interfaces or prevent attacks against it. What some manufacturers now do is allow it to be reactivated via signed engineering apps which have system access within the Android operating system via the fastboot. However, without that you can't get back into the bootloader. Manufacturers who disable bootloader and unlocking functionality often use this approach just because it's much quicker to implement. You can just cut out all the USB functionality until you want it to be accessible. One of the fun things that I mentioned earlier is that I wanted to read back memory. The download command which is used to send data to the phone could be patched to parse the hex value used for reading data for setting payload sizes rather to make it read data. I could use this to read back the bootloader code, the stack, memory and everything for a bit of minor debugging but I couldn't read any arbitrary memory. This really restricted the ability for any cold boot attacks and meant that memory was protected even if I had this buffer overflow. However, what I really wanted to do at this point because I've gotten so far is bypass the encryption on the user data protection on the phone. Qualcomm's chips encrypt to the user data partition which contains all the data users have available and is under raced on factory resets even when there's no passwords or pins used using an internal security mechanism on the chip. This prevents forensic analysis of the chip and means that if you unlock your bootloader on a phone you either get told that the partition is corrupted or it just deletes the partition itself on reboot. By passing this protection meant that we could access user data via physical access but also mume could do other few fun things. Using Qualcomm's source code I decided to see how this encryption process could be implemented while protecting the device and found that the encryption keys are intentionally inaccessible even with this code execution. I also found that I used an internal API which I couldn't access with this code. It could jump to the code and execute certain things but I couldn't modify it with my exploit. This very API was found to verify whether the phone was unlocked and whether the image was appropriately signed before decrypting the partition for user data. So the boot fastboot command loads and executes android images that are deployed via USB. So in fastboot you can say try and boot this image. Now if the phone is locked then it tells you it can't do that but if it's unlocked it will start booting the phone. I noted that in this code the verification functionality and the booting functionality were two separate functions separated by quite a lot of different code. So there's a high likelihood that I'd be able to swap two images assigned one and un-side one in order to bypass these protections. So the boot command receives the full android boot image of our fastboot download command which is then loaded into RAM verified and executed. By patching it I could then alter it to perform this time of check to time of use attack and instead of sending one image I could send two. So what I did is I started to modify the tool I'd already written to perform this functionality. So what I did is instead of sending just one boot image I configured it to send three pieces of data a four byte offset to an unsigned image, the signed image and then the unsigned malicious image. What this would do is mean that I'd have the functionality to then if I modified the bootloader to swap between a signed and unsigned image. So as I said before the boot command does not function on locked bootloader so the first thing I had to do was bypass that check by patching the code. So the check for the lock state was replaced by an operation which moved from the pointer, image pointer from the offset to the unsigned image to the signed image. This meant that the verification could then be formed without anything really changing about the functionality. I then noted that the function calls that occurred between the verification and the booting were unnecessary for actual functionality. So if I knocked these out the device would still boot and reboot as needed and do everything else it needed to. They're pretty much there for just cleaning up everything before starting the Linux image or the Android image. Because I could cut these out I got about five spare instructions I could use to then swap around the two images as needed. So I need four additional instructions for this on top of the first instruction which moved the pointer. The first thing I had to do was move the pointer back from the signed image back to the offset at the start of the data I had sent. Read the offset value, jump to that offset value and then point this pointer in the structure that's sent for booting to the new unsigned malicious code. This would be sufficient to swap between the signed and unsigned image and meant that I could facilitate this check to time abuse attack. I found this to be effective allowing me to run unsigned Android images without unlocking the bootloader at all. So there's a few fun things you can do with this. The first one is running tethered routes. So unlocking the bootloader obviously wipes all the user data and permanently rooting the phone exposes it to quite a lot of risk. A device being permanently rooted is not something that's necessary unless you're constantly using it for research, especially for most phone users. If you want to play around with the internals for your phone, that's fine. But if you want to go back to the phone functioning normally, that's something you can't really do without then locking the bootloader and doing a lot of other things. But by deploying a rooted Android image using Magisk or any other routing tool you like, but Magisk is definitely the best one, you can use this time of check to time use attack and then boot into an image with root access but then reboot when you want to remove all these root capabilities just because they're stored in a boot image that was running from RAM. However you can also use this to bypass the lock screen so by accessing unencrypted user data via this attack one can remove the lock screen restrictions. There's a file that's used for implementing lock screens and by using a custom recovery image or any other functionality you could just modify this and remove it or remove the lock screen entirely from the Android image you're deploying and get access to all the user data directly. So here's a quick outline of how this time of check to time use attacks performed. So again it's just make and run like before from my C++ code. It uploads the two images just via fast boot and then starts booting and then from here it will just swap between the two images not requiring any signing verification and boot into the unsigned image on the locked bootloader. Further to this with developer functionality enabled on a phone you can add further encryption rather than just using the internal keys provided while call home you can also add your own passwords or pin numbers to your phone in order to increase the amount of encryption there. So rather than just having that one layer of encryption you also can require a password to be input on boot which means that you won't decrypt the user data partition without adding extra functionality. So this can be bypassed by taking the Android boot image which you have and sticking the back door in it. So taking a user's phone off them or for a short amount of time or having access to it for a short amount of time you can deploy this exploit of the time of stretch time of user attack with a reverse shell built into the Android boot image put there by yourself and then reboot the phone. What will happen then is the person will put in their pin and it will boot their phone and when that's booted you will have full access to all of the user data via reverse shell. So updated in the same way you can get init script which are usually part of the boot image and then for this particular demonstration putting a metropra to shell in. So this attack was disclosed to Calcom as well but because it was only possible via the initial buffer overflow there's not a huge amount of risk by this being here. If someone could perform this buffer overflow on their phone and then perform this attack it's bad but it's an attack chain that's not always going to be viable. Patching the phone to prevent this attack would be extremely difficult as well as it would require modifying code that isn't directly performed by the Android boot loader. However these could allow an attack with physical access to an STM660 based phone that hadn't been patched now that this has been patched by Qualcomm to bypass all of these boot loader locking mechanisms. So let's move on to project 2. So the NXPPN series is a series of NFC chips used basically NFC communication on both smartphones and some embedded electronics. By breaking the firmware protections on these chips one could add new NFC capabilities to both their phone with root access or to their embedded products which use this particular chipset series. It's extremely popular, it's not understating it to say that these chips are used in millions and millions of smartphones and any exploits in them would be transferable between a large number of devices if you had root access to them. So in this particular instance I was using the NXPPN553 which is an NFC chip used solely in mobile devices. It bears similarity to similar chips in their embedded devices in smartphones such as the PN547, PN548, PIN515 and PN5180. They all were found to use a similar firmware update mechanism and they were all found to use the Cortex-M architecture. There's other chips in the PN series such as the PN544 which don't use ARM Cortex-M architectures but instead use the Cortex-C51 other things like that. I also found that these chips had very little public research available. So on smartphones these chips are communicated with via the ITVC interface for the PN series. This is on the DEV NQNCI device file which uses NSCI for NFC communication and the standard NFC protocol and then a custom protocol for firmware updates which were again a bit attacked for this project. I could trace all communication from the phone to this chip via ADB LogCat. So what I really wanted to do is force firmware updates so I could see how they could function and then analyze any weaknesses in them. So what I did is I took the two firmware images that were on the phone the Libpian553 firmware.SA and Libpian553 REC.SA which are the recovery image and the main firmware image and swapped their two file names and what would happen is when you started up the NFC functionality the phone would then verify which firmware it was verify the version number and see if it was different to what was on the device itself. Swapping these files meant that I could force the update to occur trace it and analyze how it worked and also trace it against some aspects of the source code. So the boatloader update protocol was found to be unique to our next SP chips and I think unique to the NPN series. It consists of a one byte status a one byte size, a one byte command and then a variable size of parameters and then a CRC at the end. This was encapsulated into OXFC byte chunks for large payloads meaning you could send large payloads of data while only having a size of one byte. Reason rights to DEV and QNCI directly translated to I2C commands so because Android devices are essentially embedded Linux devices it would send I2C communication via this interface. I also found that using IOControl functions I could turn the chip on and off and also set it into bootloader mode as needed. So the firmware files were found to be kept in L files like libpn553-firmware.so However these L files were found to only contain one sector which contained binary formatted data but not the code actually being executed in any meaningful capacity. It actually contained commands which needed to be run in sequence for firmware updates to occur and these commands could be updated to rebuild the firmware image as needed to do some cursory analysis in I2C. So the co-write command was to be used the only command that was used for this process and the first command in the sequence contained a lot of unknown high entropy data which was likely to be a kind of checksum or signature or anything. Also which current payloads were found to contain a 24 bit address a 16 bit size and a data payload with an unknown hash. These commands were required to be sent in sequence as they were stored in this update file or they wouldn't function properly. So because the memory address is at the start of commands they did the reconstruction of the firmware. I could generate a firmware binary containing all the firmware data was there. However I did note that the firmware that was created from this was extremely small. Definitely not enough to perform all the NFC functionality that these chips are capable of. I also found multiple code references to memory that wasn't accessible by my firmware updates meaning that the core system functionality was likely to be stored further in the bootloader or as part of some kind of immutable data on the chip. I found that there were two commands in the bootloader allowing for a read back of memory A2 and E0. A2 was just the standard read memory command which allowed you to read memory from some arbitrary addresses but only limited to the memory that could also be written to via firmware updates and couldn't mean I could directly read back the bootloader. However the E0 command was found to calculate checksums of memory and also provide four bytes of configuration data from an EEPROM which I could then reconstruct from these four bytes. So I found in the CEPROM data that there was a large chunk of randomized data sized 0xC0 ending with what looked like the standard RSA exponent 100001. This meant that it was likely to be the modulus and exponent of the public RSA used by the chip for signature verification which meant that when I went back to looking at the firmware updates it would be easier for me to spot where the signature was. I also found an additional write command over the C01 which was found to allow writing of 64 bytes of configuration data to this EEPROM. This memory had no bearing on any functionality and its size was only restricted to those 64 bytes meaning that I couldn't use it for any attacks but it was likely to be used for some kind of logging data. As outlined before the block write commands all ended with an unknown 256 bit hash I assumed it was char256 but because it didn't match the contents of the packet I was looking at I wasn't quite sure if that was correct. Multiple other hashing algorithms have tried for this but there's no validator's results. After a while I discovered that this was actually the hash for the next block in the sequence and I'll explain that now. So the first co command contains a version number the char256 hash and a signature of that specific hash and the version number. This is a hash of the next block which also contains a hash. So what happens is each block has a hash which is then hashed for the next block and its hash creating a sort of hash chain which means that you only need to verify the signature of the start while also only being able to write valid blocks of data if they match that hash sequence. I also noted that this final block in the sequence had no hash because it had no subsequent block and that became very important later. I performed some targeted fuzzing on the chip on both the firmware update functionality and the NCI interfaces. I had taken quite a lot of time in this project trying to look through each aspect of how this chip functions. So I looked at both the firmware update protocol and the NCI protocol and what I found was that the chip was found to contain some vendor specific configs that are only accessible via NCI using the configuration write commands. What I did was then incrementing bitwise values while rebooting the chip to see if it did any things strange to both the NCI and to face all the firmware updates itself but what I found would actually happen was that it bridged the chip itself but only on the firmware side. The bootloader still functioned but I couldn't now rewrite that configuration data to unbreak the chip because it was only accessible from the firmware. But I decided to persevere with the bootloader side rather than the NCI side because I felt that there was probably something usable there. It was noted that the last block of the firmware update could be written multiple times despite this hash chain so I could send the last block several times over and it would still take it and still write it which implied that the hash of the previous block remained in memory and meant that this sequence was using very static global memory as it went along. There was a potential opportunity here for me here to overwrite this hash in memory using some kind of memory corruption such as an invalid command. So I sent an invalid command the same size of the firmware update block including that SHARP 206 to see what would happen. At the end of the sequence after sending this block before the last packet the last block was prevented from being written implying that the hash had been written in the static RAM. So because of this I could create a modified hash and stick it out at the end of my custom command or my corrupted command and use this to override the hash chain. So because I could overwrite this hash I could modify what the last block contained and use this to essentially write arbitrary memory to the chip and I could do this to bypass all the signature verification mechanisms and bypass the entire hash chain just by adding my own hashes and doing this override over and over and over again meaning I can modify any aspect of the code. First thing I wanted to do with this as I had signature verification bypass was repair the firmware. So using a dump of the working configuration which I got at the start of this whole process I took it and tried to write it to the correct portion of memory in the EEPROM. This repaired the chip and I could also prove these arbitrary memory writes took hold and worked appropriately meaning that I could then repair the chip if I needed to but I could also add custom functionality so the next goal was to dump the bootloader from the chip. All standard functions were stored in the bootloader with limited functionality and firmware updates such as mem copies and things like that. However I noted that the NCI version number command which was part of the firmware update so the version number used by the firmware would be changed when the code changed rather than it being some kind of static memory that was written. Because this version number was easy to identify memory and its function mem for instance were easy to identify alongside it I could override this in order to alter the functionality. So I noted that after getting the version number it would call a function as part of the NCI configuration version number read command I decided to see if I could alter this and what I noted was this was likely to be a mem copy function. So the branch instruction to the function could be overwritten using the signature bypass and taking a bit of C code and the GCC-C flag I could write a custom function and modify as I went along as performing firmware updates to alter what could be read. So I could then observe its effect on this version number command and see what the responses were like. Because I had overwritten this function not added any functionality to it I saw that the lack of data in the response after doing this implied that the mem copy was part of that return and meant I had a decent way to return arbitrary memory from the chip. So I assumed the location of RAM was going to be at 1-0-0-0-0 due to the fact that the firmware was referencing the space for reads and writes and parts of all sorts of global memory access. In the overwritten memory copy function I changed it to search for a unique value in RAM. I sent the value FAC in NCI and then searched for it in RAM when it was sent. This provided a global pointer to write at the start of RAM 1-0-0-0-7. I could set this pointer to be this value to now store an arbitrary memory address which I could then use to dump the bootloader directly. So by constantly resetting the chip and sending the version number read command which can only be done at the start of the chip starting up, I could use this to dump the bootloader from the chip. Of course this functionality could then be extended by other core NFC functionality but I decided to leave it here and disclose it to an XP. So I decided to replicate this vulnerability as well to prove that it was on other chips. I decided to go for a chip mainly used in embedded devices rather than smartphones for this particular part of the project. So the PN5180 is a chip often used by hobbyists for NFC connectivity and you buy them very cheaply online and they come with all sorts of accessories and they're very quite easy to work with lots of libraries available. I found that it had a similar architecture to the PN53 but used a custom communication protocol instead of NCI for the core firmware. It can be communicated with via an SPI interface and GPI opens and I use a Raspberry Pi for this project. The firmware update process was the same apart from adding byte at the start because it was using a slightly different communication protocol as SPI rather than ITSD but this allowed me to replicate the signature bypass. So a command of the chip's communication protocol was found to read memory from a specific part of the EEPROM basically the version number again. This point was found in the firmware payload and I found that I could just overwrite the entire firmware and modify this pointer and read memory in order to dump the bootloader from this chip as well. I could read it without any functional code changes and just modifying this memory pointer obviously though this took a lot longer than just doing it via arbitrary code execution. The vulnerability was likely to be available for the similar chip sets, not just the PN5180 and the PN553 it's likely to be on all the other ones apart from the later versions of the series such as the NXPSN series which are completely immune to this and also is encryption on top of their signature verification adding some quite strong protections. These could allow an attack with access to the firmware updates though to completely take over the chips, the vulnerable chips and modify the functionality to add some kind of NFC functionality, add some kind of NFC backdoor or just add functionality to a hobbyist project where you want to use this chip but didn't have the capability to do certain things just because of limitations of the firmware. On smartphones of course this would require full root access which isn't always available but it could not have some functionality such as NFC chip emulation. The vulnerability was disclosed to NXP in June 2020 and they confirmed that it affected multiple chips in their product line. They requested a long remediation period which I agreed to with a public release of August 2021 due to the fact that modifying a single stage bootloader like this is incredibly dangerous when you're altering a primary bootloader from the core firmware running from it. If anything goes wrong during this process you've essentially bricked your chip and because these chips are so pervasive and there's no way to really unbrick them via any other means you want it to be very careful with. So it was done in phase rollouts and was done in quite an impressive manner. The current generation of NXP NFC products including the SN series are definitely not affected. I've checked this myself and found that the encryption in place and their modifications to the code mean that this particular vulnerability does not allow for a significant bypass and remediation across all the affected chips that has now been performed. Special thanks to Qualcomm and NXP for their findings. They did a really good job being communicative throughout this entire process and were very helpful in helping me understand some of the vulnerabilities I found. Firmware signature protection is only good as its implementation and because there were slight weaknesses in these implementations I could use these to bypass them completely and gain quite privileged access to quite pervasive chips. Common chips however are great targets as they have a high impact and they don't always have all of their weaknesses and of course Bulo-de-Vunerability is a common even in popular hardware. Thank you very much.