Reverse-engineering the Intel Management Engine’s ROMP module

Last month, while I was waiting for hardware to arrive and undergo troubleshooting, I had some spare time to begin some Intel ME reverse engineering work.

First, I need to give some shout out to Igor Skochinsky, a Hex-Rays developer, who had been working on reverse engineering the Intel ME for a while, and who has been very generous in sharing his notes and research on the ME with us, which is going to be a huge help and cut down months of reverse engineering and guesswork. Igor was very helpful in getting me to understand the bits that didn’t make sense to me.

The first thing I wanted to try and reverse was the ROMP module. It is one of the two modules that me_cleaner doesn’t remove, and given how small it is (less than 1KB of code+data), I thought it would be a good starting point. Turns out my hunch was right, as I finished reverse engineering that module after only a couple of days.

I have uploaded the C equivalent of the code to my github account and you can see the file here: romp.c as well as the rapi.h header that I used for defining RAPI (ROM API) calls and data structures (most of that info was taken from Igor’s shared information). Note that this romp.c/rapi.h code is not meant to be compiled (for now), but serves more as a proof of concept—or a way for others who are less at ease with assembly to audit the code and understand what it does exactly. A long term goal would be to make it compile and generate a binary-compatible result (with the same hash as the Intel files).

There are some more good news too: in that small bit of code, I have already found one bug in their implementation. I doubt that particular bug instance is exploitable as-is, but it’s a good indicator that their code is probably going to be full of bugs and it won’t be long before we find an exploitable one.

  • The bug is simple: when the ROMP module reads the partition from the SPI flash, after it validates the RSA signature, it copies it to a memory address and locks that memory address so it can’t be modified by the main CPU. However, they made a mistake and didn’t shift the size by 2 (effectively multiply by 4), which means that they are only locking ¼ of the region that needs to be locked. This could mean that there is a portion of the partition manifest that is accessible by the CPU which could allow us to modify the hash of a module and put our own code in it, since the signature has already been checked, and the module hash is in that ¾ portion that isn’t locked.
  • Unfortunately, we can’t seem to be able to use it because the ROMP module is executed very early, before the DRAM is initialized, and so it’s probably using/locking the internal RAM of the Intel ME ARC core, not the RAM of the main CPU. If, however, I can find the same bug (crossing fingers for a lazy Intel developer doing a copy/paste of that code) in the BUP module or any other module that executes on the main RAM, it would become exploitable and would allow us unsigned code execution on the ME processor… Which would be a nice shortcut for us.

As the Intel Management Engine’s ROMP module is now reversed and auditable (bringing us one little step closer in the freedom roadmap), we now understand much better what it’s doing (Igor thinks it might be a way to recover from an incomplete ME update, since it looks for an FTPR-named partition in the NFTP partition of the FPT header). We’ll continue digging soon. For the time being, as I received hardware prototypes for the new batch of Librems, I need to get back to porting them to coreboot.