Everything Lit: Ways to achieve UEFI persistence

Everything Lit: Ways to achieve UEFI persistence

There are more than enough techniques in the world to gain a foothold in the system, and these same techniques can be improved depending on the attackers' objectives. Unfortunately (or not so), the more sophisticated techniques for gaining a foothold and controlling said system are not as common anymore, due to the complexity of implementation or lack of expertise on the part of the attackers. Think back to the last time you saw a BYOVD attack? Or attacks that utilize the capabilities of rootkits? Or even bootkits? I am a creative person and I am involved in UEFI-related development as well as Windows-related development; today we are going to talk about a very architecturally and operationally challenging methodology for retaining a presence in a system that is very rarely used in the wild. This article assumes that you already have some knowledge of Windows-related development, UEFI-related development, and skills in exploiting vulnerabilities.

Also, this article is purely for theory, and I also take no responsibility for your actions. You knew that, right?

What is UEFI?

UEFI (Unified Extensible Firmware Interface) – essentially a microapp, successor to BIOS, providing correct device initialization and transferring control to respective OS loaders. All-in-all UEFI contains 7 phases in itself, starting from platform initialization, and finishing with OS kernel initialization with subsequent execution. More in-depth explanation in the figure below:

Boot-Manager & Boot-Loader are in charge of OS initialization. Since as we speak about Windows, bootmgfw.efi & winload.efi are gonna be respective actors. For ease of understanding, let’s set some perspective onto what are exactly Boot-Manager & Boot-Loader.

Boot-Manager & Boot-Loader exist to properly set up the environment for loading the kernel, they also load the kernel itself into RAM, and if all goes well, pass the necessary parameters to the initial kernel thread and only then, completely pass control to the kernel itself, which loads the system drivers, initializes the zero session, the graphical environment.

Achieving persistence. Method 1: EFI System Partition (ESP) attack

Well, since we understood that UEFI loads kernel, we can finally get to the main theme of the article. Let’s set the definition of ESP!
EFI System Partition – system partition which contains OS Boot-Manager and other utilities, that on completion loads DXE phases of UEFI. Fair notice that for Windows, Boot-Manager is located exactly in ESP, hope that much is understood.

From my standpoint, an ESP attack is one of the easiest techniques, since it does not require too much tinkering in the kernel environment (or any at all). All that is needed for said attack is to gain permission to monitor and edit system partitions. Let’s discuss the exploitation aspect more in-depth.

ESP attack. Preparations

Let’s set the scene, we are attacking some system. Obviously, the user’s system permissions are going to be severely limited. In order to gain access to partition monitoring, we have to elevate our permissions. The system is more than likely going to have applications that have a higher level of privilege, which we can exploit. We can use this for purposes of creating an exploit ourselves or utilizing existing exploits in an application that we target. After eventual exploitation, we have to create a command line process, in order to make sure that we have all the necessary permissions. In an oversimplified manner, it should look like this:

After rigging the ESP, we have 2 potential options: changing of Windows Boot Manager or injection into the Boot-Manager itself. Let’s go over both options in more detail!

Persistence through ESP. Boot-manager change option

That’s an easier method, since it doesn’t require any modifications. Following the ESP rigging, we can change it’s contents, in this case, we need to copy bootkit into ESP, while saving the original Boot-Manager. We, as an attacker should already have a bootkit and should only download it from our server, and copy it into ESP and reboot the system. Needless to say, that our bootkit should be able to load original Boot-Manager. Let’s visualize it in a schematic:

Let’s compare normal OS boot and boot with our bootkit:

As we can see, load process changed and UEFI now loads our bootkit instead of regular Boot Manager. In turn, it’s the bootkit that loads the original Boot Manager. With the ability to get the base addresses of Boot-Loader and kernel, we can either modify the kernel itself or load our modules with kernel-level access.

As was said in the beginning – this is one of the easiest ways of gaining persistence. Let’s get to the more interesting method though.

Persistence through ESP. Boot-Manager modification

This option is way harder, since there are more criteria that our bootkit should meet:

  1. Must be compiled according to alignment of Boot-Manager, as well as, file alignment (FILE_ALIGN), that’s the size of a single page.
  2. Bootkit must be able to relocate itself, load original Boot-Manager, and call the original entry point of original Boot-Manager.
  3. Must be able to fix it’s own relocation table.
  4. Bootkit must patch signature check of Boot-Manager.
  5. We must keep original DOS headers of NON-modified Boot-manager

With ground rules set, we can go to ESP modification. For this method saving the original Boot-Manager is unnecessary (but I would recommend saving it), we just have to copy our bootkit into ESP and then reboot the system.

That said, it isn’t as simple, attacker need to have modified Boot-Manager, which we will obtain from server in time of attack.
Even though, Boot-Manager doesn’t have a tendency to change that much, I would recommend having one for every system build, at least starting from Windows 20H1.
Alternatively, you can take an easier route and modify Boot-Manager ESP itself or the one that is stored in one of systems folders (subsequently copying it). Let’s visualize this option of attack:

Doesn’t seem to hard, right? And now let’s compare normal OS boot with modified one:

This one has more complexity, doesn’t it? Alright, let’s clarify some details:
First of, specifications of UEFI support PE/COFF specifications, which means we will have easier time with binaries, and we will need it. For example, in order to match base addresses, we can get base address of Boot-Manager in memory and compare it with saved one (we will have to save it as IMAGE_DOS_HEADER). If base addresses match, then we will only have to allocate new memory pool, fix the relocation table and again call & load Boot-Manager. If base addresses don’t match – then we again fix relocation table, patch integrity check and call entry point of original Boot-Manager.

Talking about integrity check, Boot-Manager has a function called BmFwVerifySelfIntegrity, which is going to be the one we will going to patch:

BmFwVerifySelfIntegrity

his function can be found via signature scan of an entirety of Boot-Manager (base address + size of Boot-Manager). Our patch is going to return us a 0, as simple as that.

The ESP method is quite famous, since it was in use before, but since bootkits are quite unpopular, chances of coming across one are slim, yet still possible. For example, some variation of this method was used by ESPecter. Even though, it is one of the easier methods of retaining persistence in system with UEFI, it has some significant drawbacks:

  1. Interaction with file system, which can trigger detection from antiviruses and EDR.
  2. This method does not survive OS re-installation.
  3. Requires signature or Secure Boot exploit.

Well, since we talked about ESP attack methods, I think it is time to get to something more interesting, but requiring more expertise and even more exploits.

Achieving persistence. Method 2: Attack on SMM & SPI from OS

More sophisticated, yet harder way of attack – SMM exploit. Despite the complexity of implementing and understanding of what can happen in system while infected, this way is more preferable, since it allows bootkit to remain after OS re-installation. As for the only way to get rid of this bootkit – re-flashing of SPI chip.

Alright, with exposition set, let’s get into details of exploitation process in schematic, and we will talk about them:

Schematically doesn’t look that hard, but let’s get more in-depth.

SMM and SPI. User mode

As always, exploit starts from user level. We need any vulnerable application, which we are going to exploit  (preferably, so that it controls the some sort of driver). Main goal is in elevation of privileges, so that we can install some payload into kernel, which in turn is going to perform some actions, in our case payload is going to try to reach the SMM via triggering SMI. About it in the next paragraph.

SMM and SPI. Kernel mode

Payload is going to do one thing – trigger the change into SMM mode. This is performed via triggering one of the ports (0xB2 and 0xB3), which in turn are going to make system switch into SMM mode. These kinds of switches are called SMI (System Management Interrupt) and have highest priority in system, I.e., no other interrupt can “override” SMI call. 

In our scheme of operation, there are two options for calling: independent call of SW SMI (SW - Software) and search for the table of runtime services and call any service from there with the specified parameters.Now I will explain in more detail how it should work.

A standalone SMI trigger implies that you have already found a vulnerable handler and know its number (SW SMIs have their own unique number), which means that you only need to call it with a suitable parameter (passed through port 0xB3). This parameter will be a memory pool of a certain size, and depending on what the vulnerable SMI handler is "sick" of, it will contain certain information needed to exploit the handler.

Second method is a bit more interesting. It implies search of runtime service and call time sevice tables, for suitable for our needs service. The fact is, that call for runtime service also counts as SMI interrupt, because these servises are processed as SMM. As to find this table, is quite easy, it’s address is declared as global variable and ever since 20H2 is located in CFGRO section, after table pointer RtlpInvertedFunctionTable.

Yet still, if you are not exploiting some sort of specific service or you need to work with platform variables, then just check ExGetFirmwareEnvironmentVariable
All-in-all, we need to find ourselves in SMM mode.

SMM and SPI. SMI handlers and SPI

After we have called a specific SMI, we will end up in its handler. This is a very peculiar place and it's better to run away from it as soon as possible. But, first of all, we need to exploit the handler itself.
Unfortunately, platform developers can't always keep track of all their mistakes, which, for example, can cause SMRAM memory to break. In general, almost all types of vulnerabilities are relevant for SMM.
Our task is to write to SPI. For this purpose, the SMI handler is exploited, then the write protection bits are disabled and only then the write is performed. It is worth noting that it is likely to overwrite some driver rather than add a new one - it is banally easier to overwrite something than to modify something. Also, it's worth mentioning that technically there are quite a lot of write-protect bits and they are different for Intel and AMD. As for example, bits for AMD:

Of course in the end, bootkit will end up in SPI and you will have access to platform at it’s earliest boot stages. From positives, our bootkit will stay all the way up, until SPI chip will be re-flashed, as well as we well have access to early stages of boot.

From obvious negatives – the difficulty in exploitation, it has to be a meticulously crafted chain of exploits. On the plus side, a feature of the platform's architecture, which is going to be exploited, may also come into play here.

Platform defense mechanisms.

Let’s in short, talk about defense mechanisms. 

As any other thing, UEFI has multiple defense lines against malicious code, that can compromise the system. We won’t talk too much about them in-depth – as it’ll waste a lot of time. Instead, we will talk about what mechanisms exist and what they do.

Defense mechanisms. Secure Boot

Secure Boot is a platform protocol that implements cryptographic verification of UEFI driver signatures, comparing them with authorized and disallowed images.

This protocol exists in all modern UEFI implementations and it can be either switched off or on. The method is not quite reliable, but it is very effective, because in order for your driver to be passed by Secure Boot, it must have a signature and not be in the directory of drivers not allowed to be loaded (dbx), but it must be in the directory of drivers allowed to be loaded (db).
Why is this method not very secure? The user can register his own keys instead of the platform keys. At the same time, it may happen that Secure Boot will enter the configuration mode, where it will not require validation of signatures of unknown images, which means that it will be formally disabled. Therefore, you should trust the configuration of this protocol to experienced users.

On top of that, even if Secure Boot is configured correctly, it is still vulnerable because its implementation can change from platform to platform, which means that it can potentially contain bugs and can be mitigated.

Defense mechanisms. Intel Boot Guard and AMD Platform Secure Boot

A little remark should be made here, these two technologies are conceptually the same and serve similar roles, so I’ll bundle them, and tell what they do.

Intel Boot Guard (IBG) and AMD Platform Secure Boot (AMD PSB) – hardware defense mechanisms, created for control of boot integrity of firmware and keeping track of “invalid blocks” of boot. 

Simply, these mechanisms are validating first blocks of boot, and related to SEC and PEI, in these phases platform itself is initialized, which in turn boots the system.  

For verification of integrity of these phases, both IBG, and AMD PSB use cryptographic keys located in SPI chip, as well as, some other keys. In verify some data from ME and PSP is pulled respectively, because these things in fact are Root-Of-Trust.

Conclusion

In this article we unpacked some of the methods that help to retain presence in system via UEFI abuse. Figured out what they might look like, how they work, and even looked at a theoretical but very possible attack vector on SPI. I hope that this article helped you understand how UEFI can be used with malicious intent, as well as, keep track of some vectors of attack.

Stay updated and engage with us on security discussions by joining our Telegram channel: @exploitorg

Subscribe to exploit.org

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe