參考 Mac Recversing Apple EFI firmware passwords and the SCBO myth
2008 以前的Mac 可使用清PRAM 方式 (command +option+p+r)
1.Mac EFI Password由於沒有密碼輸入限制,用可模擬usb鍵盤的teensy或是密碼破解硬體暴力破解. 時效很長，成功率低.
2.Mac EFI bios eeprom改寫. 這方面OSSLab 2015就開始使用這技術.
3.外面黑服務提供要進入mac密碼恢復模式 提取33位位碼 +機器序號給黑服務方.黑服務方會提供一個SCBO檔案
將此SCBO檔案考入外接硬碟 選擇其開機 就可解開Mac EFI password.
我們要想要知道的是 是Apple efi 被攻破了? 是技術上的漏洞還是流程上的漏洞?
照上面方法 回傳給黑服務 得到scbo文件時要照下面流程重設 EFI 密碼
1.格式化 外接硬碟 為GUID分區和Mac OS擴展格式。將它命名為Firmware
cp ~/Desktop/SCBO /Volumes/Firmware/.SCBO
cp ~/Desktop/SCBO /Volumes/Firmware/._SCBO
上面為解鎖檔案全部內容。前四個字節為字符串’SCBO’，這是一個Magic Number（0x4F424353）。在幾個字節之後，可以看到另一個字符串，前面是機器序號 但序號後數據暫時都是未知數。這個檔案的總長度為324byte。
正歸來講,授權維修中心發送給蘋果支持部門就能得到SCBO文件，但你必須提供一些關鍵信息。要獲得這些信息，你必須在出現韌體密碼提示屏幕時，同時按下SHIFT + CONTROL + OPTION + COMMAND + S，從而得到一個字符串。這就是蘋果支持部門所要求提供的字符串，同時，它跟我們在SCBO文件裡面看到的字符串一模一樣。如前所述，在前面數字是機器序列號，而後面的十六位數字是每次設置、刪除或修改固件密碼時現場生成的一個數值。
能不能通過修改和重復利用同一個樣本SCBO文件來重置任意其他 Mac 的固件密碼呢？
OSSLab簡單分析 這SCBO 流出應該為流程上的漏洞 而非技術上的漏洞.
下面參考 中文翻譯 邪惡16進位
MAC 韌體庫，如果你需要早期的EFI更新，或想驗證自己的EFI閃存是否遭到修改的話，可以輕松從這裡下載所需的更新。 利用UEFITool，你可以輕松地從轉儲和SCAP中提取內容（如果需要大量提取所有文件的話，最好使用UEFIExtract工具代替）。如果需要從NVRAM分區提取內容（該功能超級有用，感謝Nikolaj！）的話，則必須使用UEFITool的new_engine分支。
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:
This means that we have a good entrypoint into this problem and now need to reverse backwards to understand 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:
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.
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 disassembly 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.
And here we can see the result after running EFISwissKnife.
In this case it was able to identify two (U)EFI Boot services being called, SetWatchdogTimer and LocateProtocol, 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.
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 variables, 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_0000variable 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:
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.
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).
Inside this function we can observe certain file operations.
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 protocol 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 management). The following snippet shows the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL structure:
This protocol contains a single function, OpenVolume. It is interesting to read its description to understand what will happen if it executes successfully.
The OpenVolume function will open the root directory of a volume (the USB flash drive in our case) and return 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!
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 returned 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.
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.
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.
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.
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 variable. 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.
This ends the reversing process regarding 9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278Cbinary. 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:
Given this structure a bruteforce utility just needs to retrieve this information via IOKitand 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.
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.
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:
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.
If you try to patch this function to always return zero, pack it again into a firmware dump, and reflash it, then any 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.
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.
The code that starts at address 0x100003CD in the above screenshot deletes a couple of variables, including 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_variablebut what it does is delete the variable by setting 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 everything 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 environment 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 providing insight to previously tricky code.
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 debugger. 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 development 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:
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? 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 centers or even Apple itself. Only Apple has a real chance to investigate and track the source of these files. Another 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).
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.
The buffer has the following data 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.
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 Thunderstrike 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 correct public key. The public key used to verify the SCBO signature is the third one on Picture 33, 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 serial 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).
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.
We can observe this in the debugger, before the call to the function that does the printf and after the 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.
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.
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.
The final step is to verify the RSA signature to guarantee that the serial+nonce wasn’t tampered with.
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.
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 purpose!). 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.
Thanks to Jeffrey Czerniak (@geekable) for pre-publication editing.
Biases are part of being Human and knowing them doesn’t make you immune. Since I was looking for an excuse to use Unicorn I didn’t even bother to search for EFI emulators, which was good since it was heaps of fun to write my own and dominate Unicorn engine, something that will be very useful for other projects. EDK2 has an emulator package (blog entry on how to install it), and there is also efiperun project here. I would expect them to run with Apple EFI binaries since basic services are the same. Need to give it a try. If you do that before me please update me how it went.
Where I refer to machine serial number I am talking about the motherboard’s serial number and not the one outside on the back. You have to open the Mac to access it.
If you bothered to read this from start to end you would understand that there is no way for an outsider to generate the codes to reset your Mac firmware. So please stop sending me emails and comments asking for it.