Beginning SMM Module Analysis

I’ve recently become interested in “going deeper” in UEFI analysis, and thankfully there have been some excellent resources for learning about SMM Module exploitation. I am currently looking at https://github.com/tandasat/SmmExploit by @standa_t. The write up and corresponding exploit is beautifully written and documented, making learning from this example fairly straightforward. I am hoping to identify code patterns that lead to vulnerabilities in order to extrapolate those patterns for analysis with other UEFI implementations. This blog post is more or less my notes as I begin to learn how to identify this type of vulnerability in SMI Handlers and SMM modules.

These notes will assume you are familiar with UEFITool and either EfiSeek or ghidra-firmware-utils.

Download Firmware Capsules

In the SmmExploit write up, the author provides well analyzed code output for the vulnerable and fixed code. I wanted to see what the code output looks like without the excellent markup provided by the author. I wanted to see the raw Ghidra decompiler output. To do so, I downloaded the vulnerable and fixed UEFI Update capsules from the ASUS website and loaded the SdioSmm module for both versions into Ghidra. Direct links are provided below for the firmware:

SMI Code

System management interrupts (SMI’s) can be generated using the OUT x86_64 instruction. The first operand needs to be 0xb2 and then the second operand is the SMI “code”. The value for the SMI code determines which handler will handle the SMI. The code is specified at the time a SMI handler is registered. In the example below, this is the value of the variable local_20, which is 0x40. It is possible to find the registration invocation by checking for all references of the SMI Handler function, which in this case is swSmiHandler10 for the vulnerable version. Note that in the fixed firmware this handler function name is auto generated as swSmiHandler11.

SMI Handler Registration for SdioSmm module:

Fixed (304) Address: 0x80000c3a Vulnerable (303) Address: 0x80000c56

    local_20 = 0x40;
    EVar1 = (*EFI_SMM_SW_DISPATCH2_PROTOCOL7->Register)
                      (EFI_SMM_SW_DISPATCH2_PROTOCOL7,swSmiHandler10,&local_20,&local_28);

It is worth mentioning that the OUT instruction can only be executed in kernel mode (ring 0), so a kernel driver is the necessary vehicle for exploitation. The SmmExploit demo code is written for Microsoft Windows, but I believe it would be possible to write a Linux version using LKM.

Identifying Patterns

Providing the “raw” or un-analyzed Ghidra decompiler output allowed me to identify a specific pattern that I think can be used for analyzing other firmware images. The pattern can be described as first identifying input in the form of uRam addresses (in this example, uRam000000000000040e). Next we identify the SMRAM validation function that is meant to ensure memory buffers are located outside SMRAM. I named this function CheckIfBufferIsOutsideSmram. Identifying the correct SMRAM validation function is important and I have provided a copy of the “raw” Ghidra decompiler output below. After we have identified these two components, we want to look for locations where user controlled input is used as part of the conditional check for the return value of CheckIfBufferIsOutsideSmram. Conversely, it is worth mentioning that there might not be any such check, in which case any write to user-controlled memory addresses will result in a vulnerability. Lastly, we want to look for writes to memory that should be occuring outside SMRAM but because of either no checking or checking that involves user supplied input, can occur inside SMRAM.

So to recap, I would look for these three things when auditing a SMM Module for these types of vulnerabilities:

1) Look for SMM modules that register a SMI Handler 2) Look for SMI handler’s that read from uRam addresses 3) Look for SMI handlers that do some sort of checking for buffers located outside SMRAM. 4) Look for SMI handler’s that write to user controlled addresses.

Ghidra Decompiler output

In the vulnerable code, pbVar2 is user controlled input to the SMI handler. This is a memory address which is user controlled. The SmmExploit takes advantage of the pbVar2 value being part of the conditional logic (if ((lVar1 < 0) || (3 < *pbVar2))) before a value is written to an offset of the user controlled address.

SMI Handler Code (vulnerable)

Vulnerable (303) Address: 0x80000c84

EFI_STATUS swSmiHandler10(void)
{
  longlong lVar1;
  byte *pbVar2;
  
  DAT_80001f60 = 1;
  pbVar2 = (byte *)(ulonglong)*(uint *)(ulonglong)((uint)uRam000000000000040e * 0x10 + 0x104);
  lVar1 = CheckIfBufferIsOutsideSmram((ulonglong)pbVar2,0x67);
  if ((lVar1 < 0) || (3 < *pbVar2)) {
    pbVar2[2] = 7;
  }
  else {
    (**(code **)(&DAT_80000818 + (ulonglong)*pbVar2 * 8))(pbVar2);
  }
  return 0;
}

SMI Handler (fixed)

Fixed (304) Address: 0x80000c68

EFI_STATUS swSmiHandler11(void)
{
  longlong lVar1;
  byte *pbVar2;
  
  DAT_80001f40 = 1;
  pbVar2 = (byte *)(ulonglong)*(uint *)(ulonglong)((uint)uRam000000000000040e * 0x10 + 0x104);
  lVar1 = CheckIfBufferIsOutsideSmram((ulonglong)pbVar2,0x67);
  if (-1 < lVar1) {
    if (*pbVar2 < 7) {
      (**(code **)(&DAT_80000818 + (ulonglong)*pbVar2 * 8))(pbVar2);
    }
    else {
      pbVar2[2] = 7;
    }
  }
  return 0;
}

In many cases it will be useful to fingerprint the SMRAM validation function for use in analyzing other UEFI images. I included the SMRAM validation function here so I can have a reference it for other implementations.

SMRAM Buffer validation function

Vulnerable (303) Address: 0x80001a74 Fixed (304) Address: 0x80001a50

undefined8 CheckIfBufferIsOutsideSmram(ulonglong param_1,longlong param_2)
{
  ulonglong uVar1;
  undefined8 uVar2;
  ulonglong uVar3;
  ulonglong *puVar4;
  
  uVar3 = 0;
  if (DAT_80001fc8 == 0) {
    uVar2 = 0x800000000000000e;
  }
  else {
    if (param_1 + param_2 < param_1) {
      uVar2 = 0x8000000000000002;
    }
    else {
      if (DAT_80001fc8 != 0) {
        puVar4 = (ulonglong *)(DAT_80001fc0 + 8);
        do {
          uVar1 = *puVar4;
          if (param_1 < uVar1) {
LAB_80001ad6:
            if (uVar1 < param_1 + param_2) {
              return 0x800000000000000f;
            }
          }
          else {
            if (param_1 < uVar1 + puVar4[1]) {
              return 0x800000000000000f;
            }
            if (param_1 < uVar1) goto LAB_80001ad6;
          }
          uVar3 = uVar3 + 1;
          puVar4 = puVar4 + 4;
        } while (uVar3 < DAT_80001fc8);
      }
      uVar2 = 0;
    }
  }
  return uVar2;
}

I extend my sincere gratitude to @standa_t for answering my questions regarding his research, as well as for providing such well-written documentation on SmmExploit.

Home