was successfully added to your cart.
蘋果資安

逆向工程Mac EFI韌體與SCBO文件之謎(未完待續)

By 2017-08-26 No Comments

本篇非全文翻譯
參考 Mac Recversing Apple EFI firmware passwords and the SCBO myth

        

當Mac電腦被開機Bios密碼鎖住時候.
如果不知道密碼要強制解開. 共有下面處理方式
2008 以前的Mac 可使用清PRAM 方式 (command +option+p+r)
如果是2008後機器 ,官方說法是.如果要強制解開請準備發票到維修中心會幫你解開.

非正規體系則提共下面幾種方法

1.Mac EFI Password由於沒有密碼輸入限制,用可模擬usb鍵盤的teensy或是密碼破解硬體暴力破解. 時效很長,成功率低.
2.Mac EFI  bios eeprom改寫. 這方面OSSLab 2015就開始使用這技術.
3.外面黑服務提供要進入mac密碼恢復模式 提取33位位碼 +機器序號給黑服務方.黑服務方會提供一個SCBO檔案
    將此SCBO檔案考入外接硬碟 選擇其開機 就可解開Mac EFI password.

這方法其實同於一般PC解密碼機制 可參考這網站

其實這流程是蘋果維修中心工程部處理方法.
我們要想要知道的是 是Apple efi 被攻破了? 是技術上的漏洞還是流程上的漏洞?

find hash

照上面方法 回傳給黑服務 得到scbo文件時要照下面流程重設 EFI 密碼

1.格式化 外接硬碟 為GUID分區和Mac OS擴展格式。將它命名為Firmware

2.將名為“SCBO”的二進制文件拖拽到桌面。

3.在終端執行下列命令:

cp ~/Desktop/SCBO /Volumes/Firmware/.SCBO
cp ~/Desktop/SCBO /Volumes/Firmware/._SCBO

4.拔出外接硬碟。

5.將此外接硬碟插到需要解開電腦上,同時按住Option鍵。

6.會看到鎖形符號,一會兒後,電腦應重新啟動,顯示啟動管理器 表示已經解開EFI密碼。

SCBO樣本文件SCBO_original.zip
(SHA256(SCBO_original)= fad3ea1c8ffa710c243957cc834ac1427af0ea19503d9fc7839626f6cac4398b)

scbo hexdump

上面為解鎖全部內容。前四個字節為字符串’SCBO’,這是一個Magic Number(0x4F424353)。在幾個字節之後,可以看到另一個字符串,前面是機器序號 但序號後數據暫時都是未知數。這個檔案的總長度為324byte

 SCBO文件是如何生成

正歸來講,授權維修中心發送給蘋果支持部門就能得到SCBO文件,但你必須提供一些關鍵信息。要獲得這些信息,你必須在出現韌體密碼提示屏幕時,同時按下SHIFT + CONTROL + OPTION + COMMAND + S,從而得到一個字符串。這就是蘋果支持部門所要求提供的字符串,同時,它跟我們在SCBO文件裡面看到的字符串一模一樣。如前所述,在前面數字是機器序列號,而後面的十六位數字是每次設置、刪除或修改固件密碼時現場生成的一個數值。

 

能不能通過修改和重復利用同一個樣本SCBO文件來重置任意其他 Mac 的固件密碼呢?

答案是否定的。如果我們在測試Mac上建立一個固件密碼,必須生成所需要的字符串,然後對SCBO進行相應的修改。 計算機將處理該文件並重新設置系統,但密碼不會重置。

這為我們提供了另一個信息:這個SCBO的內容會進行某種完整性檢驗。如果沒有這種檢驗的話,任何人都可以修改SCBO內容.

但是,如果這是真的,那些人叫賣的SCBO文件為何貌似完全有效呢?
OSSLab簡單分析 這SCBO 流出應該為流程上的漏洞 而非技術上的漏洞.

因此,有必要更深入地挖掘和逆向EFI文件系統代碼。

下面參考 中文翻譯 邪惡16進位

 

首先,我們需要提取出所有EFI二進制代碼,這有兩種途徑,一是途徑從存有EFI內容的Flash芯片轉儲中提取,另一種是從EFI更新文件中發現的SCAP文件中提取(不知何故,fd格式也可用於某些Mac上面)。

MAC 韌體庫,如果你需要早期的EFI更新,或想驗證自己的EFI閃存是否遭到修改的話,可以輕松從這裡下載所需的更新。 利用UEFITool,你可以輕松地從轉儲和SCAP中提取內容(如果需要大量提取所有文件的話,最好使用UEFIExtract工具代替)。如果需要從NVRAM分區提取內容(該功能超級有用,感謝Nikolaj!)的話,則必須使用UEFITool的new_engine分支。

就本文來說,目標Mac是一個的MacBook Pro8.2系統,並且所有文件都是從MBP81_0047_2CB_LOCKED.scap這個固件更新文件中提取的。您可以使用其他的固件文件:GUID仍然不變,但地址和某些內容可能會有所不同。

有效載荷提取來之後,我們終於可以著手尋找合適的逆向突破口了。開始的時候,最好的線索自然是SCBO文件的魔法值了,因為它必然會在代碼中的某個地方進行相應的檢查。對付這種類型的任務,我最喜歡的工具是bgrep。有了它,我們可以在文件中查找特定的字節序列,這個功能對於定位二進制數據來說極其有用。我們想要查找的字節是5343424F,即“SCBO”的魔法值。如果你想查找字符串,請不要忘了,在EFI二進制文件中的字符串,大部分都是Unicode(寬度為兩個字節)。

只有一個符合要求,即一個DXE階段的二進制文件,其GUID為9EBA2D25-BBE3-4AC2-A2C6-C87F44A1278C。請注意,在(U)EFI的世界裡是沒有文件名這一說的,所有的東西都是通過一個128位的GUID來引用的。

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:

scbo bytes

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.

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 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.

start function no comments

And here we can see the result after running EFISwissKnife.

start function with comments

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.

efi swiss knife 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 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:

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.

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

call read scbo

Inside this function we can observe certain file operations.

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 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:

efi simple file system protocol

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

open root volume protocol

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!

efi file protocol

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.

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.

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.

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.

set new scbo variable

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.

scbo in firmware

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:

trb structure

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.

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.

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:

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.

call to verify password

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.

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.

core reset var and system

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.

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 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:

scbo 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).

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.

mem alloc pubkey

The buffer has the following data structure:

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.

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

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.

sprintf serial nonce

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

before sprintf call

after 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.

replace scbo buffer

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.

hash scbo contents

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.

hash debugger result

openssl dgst

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

rsa verify call

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.

remove password from scbo

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.

Have fun,
fG!

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

Update 1:
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.

Update 2:
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.

Update 3:
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.

Thx Chang

Author Thx Chang

More posts by Thx Chang

Leave a Reply