Apple EFI firmware passwords and the SCBO myth

My original goal when I started poking around Apple’s EFI implementation was to find a way to reset a Mac‐ Book’s firmware password. My preliminary research found references to a “magical” SCBO file that could be loaded onto a USB flash drive and booted to remove the password. The normal process workflow is to first contact Apple support. Since I don’t have the original sales receipt of this specific Mac, I assume this option isn’t possible, since anyone with a stolen Mac could get the password reset. Things got more interesting when I found a website that allegedly sold the SCBO files – just send them the necessary hash (more on this later), pay USD100, and get a working SCBO file in return. There are videos (in Portuguese but you can watch the whole process) of people claiming this works, and even some claims about an universal SCBO that unlocks multiple Macs.

Since there was (still holds true) virtually no information about the SCBO contents, this aroused my curiosity but I never followed up until now. Upon my return from SyScan360 Singapore, I needed a new research di‐ rection to kickstart my brain back into work, and this fit the bill.

The core question I wanted to answer was if it was really possible for someone to build a SCBO file key generator. If this were true, it would imply that Apple’s EFI contains a significant vulnerability. Understanding how SCBO files work in the first place was also intriguing. So let’s start another EFI reversing engineering adventure…

At the time I could only find a single SCBO file on the Internet, which is bad (impossible to visualise differences between files) but better than no file at all. The sample file can be downloaded here SCBO_origi‐ nal.zip.(SHA256(SCBO_original)= fad3ea1c8ffa710c243957cc834ac1427af0ea19503d9fc7839626f6cac4398b)

01_scbo_hexdump

This picture shows us the full contents of the sample file. The ‘SCBO’ string is clearly visible in the first four bytes, which is a magic number (0x4F424353). A couple of bytes later and we see another string. It appears to be some kind of serial number. This information can be verified because part of this string can be found in the motherboard of each Mac (my sample is only composed of MacBooks but I guess iMacs and others will contain the same information). The rest of the string and binary data that follows are unknown for now. The total file length is 324 bytes.

How are the SCBO files generated?

As previously mentioned, Apple support is able to generate these files after you provide some key informa‐ tion. To obtain the necessary information, you must hold SHIFT + CONTROL + OPTION + COMMAND + S on the firmware password prompt screen and a string will be generated. This is the string Apple support needs, and this is the same string we see inside the SCBO file. The first digits are the machine serial number as previously described, and the last sixteen digits are a nonce value regenerated every time the firmware password is set, removed, or modified. I know this because I had already reversed Apple’s Firmware Password Utility and observed its communications with the kernel extensions that set the EFI NVRAM variables.

02_findhash

Now that we know a bit more about its contents, can the sample SCBO be modified and reused to reset any other Mac’s firmware password?

The answer is no. If we set a firmware password on a test Mac, generate the necessary string, and modify the SCBO accordingly, nothing will happen. The computer will process the file and reset the system, but the password isn’t reset.

This provides us with another bit of information – that there is some kind of integrity check on the SCBO contents. It would be a surprise if this kind of check wasn’t implemented and anyone could modify the SCBO contents.
So if this is true then how is someone selling what appear to be fully working SCBO files? We need to dig deeper and reverse the EFI code responsible for processing this file.

And now let’s start the real reverse engineering fun!

The first thing we need to do is to extract all the EFI binaries either from a flash chip dump that holds EFI contents or from a SCAP file found in EFI updates (for unknown reasons the fd format is also used for some Macs).
I maintain an up-to-date Apple firmware update repository, which you can use to easily download EFI updates or verify the contents of your EFI flash if you fear nation states are attacking you. The great UEFITool can easily extract contents from dumps and SCAP (to mass extract all the files use UEFIExtract utility in‐ stead). You will need UEFITool’s new_engine branch if you want support for NVRAM partition contents (which is super useful feature, thanks Nikolaj!).

The target Mac used on this post is a MacBook Pro 8,2 and the files were all extracted from this firmware update file, MBP81_0047_2CB_LOCKED.scap. You can use other firmware files – the GUIDs will still be the same but addresses and some content might differ.

With the payload extracted we can finally try to find where to start reversing. The initial best clue is the magic value from the SCBO file, since it should be checked somewhere in the code. My favorite tool for this type of task is bgrep aka binary grep. It allows us to grep files for specific byte sequences, an extremely useful feature to locate binary data. The bytes we want to grep for are 5343424F, the ‘SCBO’ magic value. If you want to grep for strings please remember that most strings in EFI binaries are Unicode (two bytes wide).

There is only a single hit, a DXE phase binary with GUID 9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C. In (U)EFI world there are no filenames, everything is referenced by a 128 bits GUID.

03_scbo_dxe_binary

It is time to load the binary into a disassembler and try to understand what is happening. We can observe the magic bytes being tested in the following piece of code:

04_scbo_bytes

This means that we have a good entrypoint into this problem and now need to reverse backwards to under‐ stand what this function is trying to accomplish and how is it called.

What is the procedure to use the SCBO file?

To assist our reversing engineering effort it is important to collect as much data as possible about our target works. The following describes how to use the SCBO file to reset the firmware password:

  • Format a Flash drive GUID partition scheme and Mac OS Extended format. Name it Firmware (note: doesn’t really need to be Firmware!).
  • Drag the binary file named “SCBO” to your Desktop.
  • Open Terminal.
  • Execute this command in Terminal: cp ~/Desktop/SCBO /Volumes/Firmware/.SCBO
  • You should get a new line, no errors.
  • Execute this command in Terminal: cp ~/Desktop/SCBO /Volumes/Firmware/._SCBO You should get a new line, no errors.
  • Eject the Flash drive.
  • Turn off the customer’s computer.
  • Insert the Flash drive into the customer’s computer.
  • Turn on the customer’s computer while pressing and holding the Option key.
  • You should see the lock symbol for a moment, and then the computer should restart to the Startup Manager.

This gives us important clues to what we should be looking for code that has access to the filesystem and reads one of these two files. If we look at the strings of current disassembled binary we can see we are on the right track.

05_scbo_strings

The .SCBO filename that is copied into the flash drive is referenced in the strings, althought IDA is unable to find any string references to it (IDA bug? most probably!).

Reversing (U)EFI binaries is quite annoying because every external function is a function pointer, so the dis‐ assembly output is not very clear and needs some assistance to improve it. Snare created ida-efiutils, a set of scripts that improve the disassembly output by trying to rename function pointers, offsets, and structures. Because I wanted a couple of more features than it provides and I’m not a Python fan I ended up creating my own IDA C plugin for this task called EFISwissKnife.

It does extra things like commenting the known functions with their prototype and documentation, generate some statistics, and extract information about installed and used protocols into a database. This makes it very easy to find out which binaries are installing and using a certain protocol, avoiding tons of binary grep’ing and wasted time finding which module implements a protocol.

The next picture shows the start() function as IDA disassembles without any plugin help.

06_start_function_nocmts.png

And here we can see the result after running EFISwissKnife.

07_start_function_withcmts

In this case it was able to identify two (U)EFI Boot services being called, SetWatchdogTimer and LocatePro‐ tocol, and also comment the GUID used by LocateProtocol.

The statistics feature gives us some information about the GUIDs we were able to locate in this binary and which (U)EFI RunTime and Boot services are used. This is very useful information to have a quick idea of what the binary is doing.

08_efiswissknife_stats

With improved disassembly output we can proceed to try to understand what happens with the SCBO file. Because reverse engineering is more of an art and less of a science, I’ll start by telling you what happens and then walk you through how it happens.

What happens is that an event notification is installed by this EFI binary. When a USB flash drive is inserted, it triggers the notification and a callback is executed.

One of the callback tasks is to try to read the SCBO file from the flash drive and verify if its format is correct (checking the magic number, etc). If the SCBO contents appear correct then a new EFI NVRAM variable will be set, “.SCBO_0000” using GUID 5D62B28D-6ED2-40B4-A560-6CD79B93D366. This GUID can also be found in the Firmware Password Utility. This GUID is not unique to this variable and it is used for other vari‐ ables, e.g. FWAppCmd. The variable can be observed in NVRAM when a new password is set, changed, or removed by Firmware Password Utility. If the “.SCBO_0000” variable is set succcessfully then the system will be reset via ResetSystem service.

The event notification code can be found at start(). The following code snippet is responsible for creating the event:

09_install_event

The most interesting thing in this code is the third parameter to CreateEvent service, NotifyFunction. This is the callback that gets executed when the event triggers. The code that follows merely registers the event, in this case a file system related event.

10_register_event

Now let’s start looking at the callback code. The first interesting detail is that it tries to locate a new protocol with GUID 75FAB4B4-6AC1-429A-A000-6B0B95E71CA1. This protocol is installed by EFI binary with GUID 818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C. The code continues and we finally reach the interesting code snippet that handles reading the SCBO file contents (I renamed the original function to read_scbo_file_contents).

11_call_read_scbo

Inside this function we can observe certain file operations.

12_open_volume

The code above is responsible for opening the USB flash drive volume so it can read its contents. In this particular case we need to check the layout of the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID proto‐ col so we understand what each function pointer of the protocol is doing. Usually I grep EDK2 sources to find the protocols, even if the EDK2 specification is more advanced than Apple’s EFI fork (the AMIBios source leak is also a good place to search, in particular code that is outside of EDK2 such as power man‐ agement). The following snippet shows the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL structure:

13_efi_simple_file_sys_proto

This protocol contains a single function, OpenVolume. It is interesting to read its description to understand what will happen if it executes successfully.

14_open_rootvol_proto

The OpenVolume function will open the root directory of a volume (the USB flash drive in our case) and re‐ turn a EFI_FILE_PROTOCOL handle. We need to look at another protocol to understand its features. While reversing (U)EFI we are constantly grep’ing the sources to find information about many kinds of protocols and functions. There are no man pages to save us!

15_efi_file_proto

We can observe that EFI_FILE_PROTOCOL supports the basic functions we need to read and write files on a filesystem. The staggering amount of protocols are a bit annoying to the reverser, but after a while we start appreciating some elegant parts of (U)EFI’s design.

The previously posted disassembly snippet first tries to open the root volume, and if it succeeds uses the re‐ turned handle to try to open the “.SCBO” file from the USB flash drive volume. If it manages to open the file, it uses the GetInfo function from the EFI_FILE_PROTOCOL to find the file size, then allocate the necessary memory, and finally read its contents into the allocated buffer.

16_find_scbo_size

The file is read in two steps, first 12 bytes, which is the size of SCBO header. If the header contents appear correct then the remaining is read.

17_read_scbo_header

We can observe in the above code snippet the first 12 bytes being read, and then the verification of header structure. The SCBO file supports multiple units of data, meaning that a single USB flash drive could potentially reset the password of more than one Mac. This may be pretty useful for a system administrator that has to reset many Macs. This could explain the rumored “universal” reset SCBO, which could be a file with multiple units – this is just pure speculation since that file isn’t public. The rest of the function resets the file position back to the beginning and reads the whole SCBO contents into a previously allocated memory buffer.

If the SCBO contents were read successfully, the next step is to call a function from another protocol that will verify if the Mac’s serial number and current nonce match the contents of the SCBO file. Remember that the nonce is rotated every time the firmware password is modified.

18_verify_scbo_serial

If the serial and nonce are confirmed to be correct, then a new variable named “.SCBO_0000” will be set in the EFI NVRAM area and the system will be reset if there are no more units to process in the SCBO file. The new variable contains all the SCBO data minus the 12 bytes header: 312 bytes total length.

19_set_new_scbo_var

Now we understand a bit more how the SCBO feature works.

If the SCBO file contents match the current Mac, a new variable is set and the computer is rebooted before any other operations. This means that there will be another EFI binary reading and processing the new vari‐ able. The current binary is only responsible for reading the SCBO file and doing basic integrity verification but it has no capabilities to remove the firmware password. This feature is reserved to the 818544B5-1B9D- 4E7B-8F7D-835AAEAF3B5C binary.

The “.SCBO_0000” variable can be seen in the following firmware dump. If you look at the body size of this variable it’s 312 bytes, the expected value.

20_scbo_in_firmware

This ends the reversing process regarding 9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C binary. We now know what it does and we can move to the really interesting binary.

Before starting to reverse the new binary let’s first understand how the firmware password feature is implemented.

Two years ago I reversed the Firmware Password Utility and built a small EFI password bruteforcer based on that work. My work helped me determine that the EFI variable that contains the firmware password information is “CBF2CC32”. The password is stored as a Message Autentication Code (MAC) using SHA256, with a variable number of rounds. Bruteforcing the firmware password is useless for any password longer than four digits, since the high number of rounds makes it impossible within a reasonable time frame. The following structure can describe the variable contents:

21_trb_struct

Given this structure a bruteforce utility just needs to retrieve this information via IOKit and start bruteforcing until the password matches the current hash. This takes a couple of minutes for a four digits password.
Let’s take a quick look at the main() function of the new EFI binary we want to reverse.

22_core_dxe_main

The main() function is pretty simple. First we have the usual storage of BootServices and RunTimeServices table pointers in local variables, then a call to a function, and last the installation of the protocol that is called from the first EFI binary we reversed.

23_core_dxe_protocol

The installed protocol is composed of seven function pointers. The function called from the first binary is at offset 0x18, sub_10000828. It is interesting to verify which EFI binaries are calling this protocol. This is very easy with EFISwissKnife database:

24_sql_query

The first column contains the GUID of EFI binaries that are using this protocol. The binary that is involved in EFI firmware password verification is 2D61B52A-69EF-497D-8317-5574AEC89BE4. This binary installs another protocol that is called from some other binary – probably the binary that deals with the user input and
screen drawing, which I was unable to pinpoint.

25_call_to_verify_pwd

If you try to patch this function to always return zero, pack it again into a firmware dump, and reflash it, thenany firmware password will be accepted. This means we are on the right track.

Now I’m just rewriting the story, because what I initially did before starting to reverse everything was to use Trammell’s infinite loop trick on each function of 75FAB4B4-6AC1-429A-A000-6B0B95E71CA1 protocol, and after finding the interesting ones I patched them to return zero and found out which function verifies the
password, the first one from the protocol.

The sub_10000704 protocol function will retrieve the “CBF2CC32” variable, generate the hash from the user-inserted password and compare with the information in the variable. SHA1, SHA256 and SHA512 constants can be found at the 818544B5-1B9D-4E7B-8F7D-835AAEAF3B5C binary.

If you have a SPI flasher and want to remove an Apple EFI firmware password, what you need to do is to dump the flash contents, remove the “CBF2CC32” variable (you just need to flip a single bit on its name for example), and reflash the modified firmware. Or just locate the variable and erase or modify it directly without
reflashing the whole contents.

There is also another way to do this. The “3E6D568B” variable is special because if you remove it, the NVRAM will be reset to a default state where the firmware password is not set anymore.

So there you go: you don’t need to search any more web forums and buy some overpriced EFI password reset hardware. You only need a SPI flasher and a SOIC clip and you can do it yourself.

If you look at all the remaining functions from this 75FAB4B4-6AC1-429A-A000-6B0B95E71CA1 protocol there is nothing related to SCBO except the one called by the 9EBA2D25-BBE3-4AC2- A2C6-C87F44A1278C binary that just verifies if the SCBO serial and nonce match the current Mac (Picture 15, offset 0x18, sub_10000828).

This means that the “.SCBO_0000” variable is being processed somewhere else. The answer is the sub_10000314 function called in main() (Picture 18). This is the function that will process the variable and reset the NVRAM in case there is a problem with “3E6D568B” variable as I just mentioned.

26_core_function_start

Here at the beginning of sub_10000314 function the variable “3E6D568B” is retrieved from the NVRAM and if doesn’t exist the code flow will be redirected to address 0x100003CD.

27_core_reset_var_and_system

The code that starts at address 0x100003CD in the above screenshot deletes a couple of variables, includ‐ ing “CBF2CC32” (the first one being cleared), and creates “3E6D568B” again so the Mac doesn’t get stuck in a loop of doom. I labelled the function “zero_EFI_variable” but what it does is delete the variable by set‐ ting the DataSize parameter to zero. From (U)EFI documentation “a size of zero causes the variable to be deleted”.

The next problem is how to track the code that processes the SCBO variable?
Static analysis is not always easy on (U)EFI binaries because we have very limited ways to test hypothesis – each reflash takes around 5 to 8 mins if we want to patch code and see what happens. We also don’t have easy access to debuggers – JTAG debuggers for (U)EFI are expensive – some cost 6k USD or more.

I already had reversed many parts of this large function and other functions it calls, but I was still having trouble finding the code that processes the SCBO variable contents, and I didn’t want to really reverse ev‐ erything and/or keep using the slow patch and reflash method.

This is when I had an idea! How about creating an EFI emulator and debugger using the Unicorn Engine framework? I had a feeling this wouldn’t be extremely hard and time consuming because the EFI environ‐ ment is self contained – for example no linkers and syscalls to emulate. I also knew that this binary was more or less isolated, only using a few Boot and RunTime services and very few external protocols. Since the total number of Boot and RunTime services are very small this meant that there wasn’t a lot of code to be emulated.

And with a couple of days work the EFI DXE Emulator was born. To my surprise I was finally able to run and debug an EFI binary in userland, speeding the reverse engineering process up immensely and quickly pro‐ viding insight to previously tricky code.

28_efi_emulator

I gave it a gdbinit-style UX and emulated some basic commands such as add breakpoints, step in and step out of calls, dump memory, set memory and registers, making it a very basic but extremely useful EFI de‐ bugger. It has some limitations (can’t change directly RIP or EFLAGS registers) due to Unicorn/QEMU JIT design but it is definitely usable for basic tasks. I emulated the core Boot and RunTime services such as get and set variables, NVRAM area, allocate/copy/set memory, load additional images and install/locate new protocols. While far from feature and emulation complete this is a pretty useful tool that was a critical devel‐ opment on this and future (U)EFI projects.

Once again let’s do this backwards and start with the conclusions. After I finished reversing this function I finally understood the SCBO feature. First the SCBO file structure can be described by the following data structures:

29_scbo_structs

The unknown binary data we initially saw is nothing but a 2048 bit RSA signature. Unless someone got hold of Apple’s private keys, there is no possibility of building a SCBO key generator. So what is happening with all those videos and people claiming they were able to buy SCBO files from websites? websites? My bet is that these guys somehow are able to submit illegitimate requests to Apple’s support system and then sell the SCBO files they receive for some nice fat profit. These could be insiders working at Apple support cen‐ ters or even Apple itself. Only Apple has a real chance to investigate and track the source of these files. An‐ other alternative is that there is a vulnerability I wasn’t yet able to find. The code and design appear solid and I saw no obvious vulnerabilities.

To verify this hypothesis outside EFI code I adapted some code I previously used to verify the signatures of SCAP firmware updates and voila, I was finally able to verify that the SCBO file I had was indeed a valid SCBO file signed by Apple and it wasn’t possible to modify it to run on other machines unless I patched some firmware code (which is useless since if you can patch firmware code, it would be easier to just reset the variables).

30_verify_scbo_output

The core function that deals with SCBO contents is sub_100021F0. One of the first things it does is to allocate a 0x110 (272) bytes buffer to hold Apple’s public keys.

31_memalloc_pubkey

The buffer has the following data structure:

32_pubkey_structure

The function at address 0x1000128C will be responsible for retrieving Apple’s public keys from the EFI “file system”. The firmware contains five different 2048 bits public keys. They can be found on EFI file B2CB10B1-714A-4E0C-9ED3-35688B2C99F0.

33_apple_pubkeys

Each raw file is 276 bytes meaning that the first 20 bytes are just some meaningless header. We just need to remove those 20 bytes and we get the 256-byte public key. One important detail described by Trammell Hudson in his Thunderstike presentation is that the key bytes are inverted. If we want to use this public key in our own utilities, we need to reverse its bytes. Once again the keys aren’t directly extracted by a function – there is as usual a protocol that implements this feature. This protocol has GUID AC5E4829-A8FD-440B- AF33-9FFE013B12D8, and is installed by binary 8B24E4D4-C84C-4FFC-81E5-D3EACC3F08DD. There is no point in reversing the whole protocol; I saw that it was retrieving the Apple public keys and that’s it.

To avoid emulating filesystem-related operations I simply enabled a Unicorn code hook and injected the cor‐ rect public key. The public key used to verify the SCBO signature is the third one on Picture 25, with a SHA256 of 94218318fe5aaada2889bbd5f9789cf4afa15bd6eb7654ad66f1f199cf70f8ad (for the whole raw file as extracted by UEFITool).

Another 32-byte buffer will be allocated to hold a SHA256 hash. We will see later on that this buffer will hold the checksum of the first 56 bytes of the SCBO variable.

Next is the extraction of the serial number from physical memory at address 0x0FFFFFF08. If you boot a Linux installation and use Chipsec to read the physical memory you are able to read the Mac’s individual se‐ rial number (on older macOS versions you could also use AppleHWAccess.kext or DirectHW.kext to read the memory but they are now blacklisted on El Capitan and Sierra).

34_get_serial_and_nonce

The current nonce on variable “BC9772C5” is extracted next. The goal is to build the same serial+nonce string that is found in the SCBO.

35_sprintf_serialnonce

We can observe this in the debugger, before the call to the function that does the printf and after the call.

36_before_sprintf_call

This generated string will be used to replace the serial+nonce that exists in the SCBO buffer. The signature verification code will use the current values from the Mac instead of the values in the SCBO file.

37_after_sprintf_call

Right after this we have the hashing of 56 bytes from the SCBO contents, field1, field2, and serial+nonce from SCBO_CONTENTS structure described before.

38_replace_scbo_buffer

We can observe the result in the debugger. If we extract the contents from the SCBO file and hash them they should match, meaning that we are on the right track.

39_hash_scbo_contents

The final step is to verify the RSA signature to guarantee that the serial+nonce wasn’t tampered with.

40_hash_debugger_result

Because there are more than one Apple public key we can see a loop, meaning that the signature will be verified against all Apple keys found in the firmware “filesystem”. If one returns a valid result then the password will be removed by clearing the “CBF2CC32” from NVRAM.

41_openssl_dgst

The DataSize parameter (R9 register at address 0x100024F8) is zero so the variable will be deleted from NVRAM. This code once again shows that the EFI password feature is definitely implemented via the “CBF2CC32” NVRAM variable.

And that’s it. The SCBO mystery is finally solved and its format understood. With the precious assistance of my EFI DXE emulator and debugger I sped up the reverse engineering effort. The SCBO feature design is robust, and the only mystery I haven’t solved right now is how someone is apparently selling working SCBO files on the Internet. My bet is on insider access to Apple systems via Apple Support centers or something like that, but once again only Apple can really investigate the root cause.

If you lost your firmware password you can now reset it yourself as long the SPI flash chip is not the new BGA type (newer Macs are using them but there is a sneaky debug port that can be used for this same pur‐ pose!). You just need a device to dump the flash chip, remove the variable and reflash the modified version, or directly remove the variable (I always prefer to full dump and reflash).

Of course this information can be used by thieves selling stolen Macs, but given
that there are already defeat devices being sold all over the web, this post does not reveal any previously- unknown secrets.

I hope you enjoyed this post, and I also hope you are now interested in (U)EFI reversing. It’s not as easy as userland or kernel reversing due to the lack of debuggers, but with some extra work those difficulties can be solved. For the moment I am not going to release the code for my EFI emulator. I am fed up with people stealing my code without giving proper attribution; recently I discovered a couple of cases of stolen code and even modified credits. It is really annoying when I demand nothing but credits and code licensing is pretty much liberal.

Have fun,
fG!

P.S. Thanks to Jeffrey Czerniak (@geekable) for pre-publication editing.