Attacking Intel BIOS
Attacking Intel BIOS
BIOS
Rafal Wojtczuk and Alexander Tereshkin
Black Hat USA, July 30 2009
Las Vegas, NV
BIOS Reashing Background
Attacking and Reashing the Intel BIOS
Consequences
1
2
3
BIOS Reashing Background
Have others done it before us?
ACPI tables infection by John Heasman
ACPI tables are stored in BIOS image, so reash capability is
required to alter them!
But most of the recent systems do not allow arbitrary
(unsigned) reashing...
"Generic" BIOS infection by Core
copyright (c) 2005 Core Security Technologies.
A SimpIe way to patch BIOS
BOS contains several checksums
Any modification leads to an unbootable system.
We used two techniques:
1) Use a BOS building tool (Pinczakko's method)
2) Patch and compensate the 8-bit checksum
Three easy steps:
1) Dump BOS using flashrom
2) Patch and compensate
3) Re-flash
source: http://www.coresecurity.com/content/Persistent-Bios-Infection
Did somebody say simple?
copyright (c) 2005 Core Security Technologies.
Introduction
Practical approach to generic & reliable BOS code
injection
True Persistency
Rootkit(ish) behavior
OS independant
source: http://www.coresecurity.com/content/Persistent-Bios-Infection
Did somebody say generic?
So, what the heck are we doing here today? ;)
Why malware can not reash a BIOS on most systems?
Source: intel.com
Source: intel.com
So, what about those programs that can reash the BIOS from
Windows?
They only schedule a reash, which itself takes place during an early
stage of BIOS boot, when the ash locks are not applied yet
So far there has been no public presentation about how to reash a
BIOS that makes use of the reashing locks and requires digitally
signed updates...
... up until today...
We can (potentially) exploit some coding error in the BIOS
code (say, buffer overow) to get control of early BIOS
execution...
Problem: early BIOS code usually takes no external [potentially
malicious] input;
PXE boot code happens too late (all interesting chipset locks,
e.g. reashing locks, are already applied)
...
...with an exception of a ash update process! It processes user
provided data - the update!
Attacking the Intel
BIOS
Intel BIOS Updates Background
A BIO update contains "rmware volumes", described in UEFI
specications
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 4 (0x4)
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=Fixed Product Certificate, OU=OPSD BIOS, O=Intel
Corporation,
+L=Hillsboro, ST=OR, C=US
Validity
Not Before: Jan 1 00:00:00 1998 GMT
Not After : Dec 31 23:59:59 2035 GMT
Subject: CN=Fixed Flashing Certificate, OU=OPSD BIOS, O=Intel
+Corporation, L=Hillsboro, ST=OR, C=US
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (1022 bit)
Modulus (1022 bit):
<snip>
Exponent: 12173543 (0xb9c0e7)
X509v3 extensions:
2.16.840.1.113741.3.1.1.2.1.1.1.1: critical
1............
Signature Algorithm: sha1WithRSAEncryption
<snip>
There are a few PE modules inside BIO that are not packed
with anything. One of them happens to contain a code from:
Edk\Sample\Universal\DxeIpl\Pei\DxeLoad.c,
function PeiProcessFile(), which is responsible for
unpacking BIO sections. The GUID of this le is:
86D70125-BAA3-4296-A62F-602BEBBB9081
EFI_STATUS PeiProcessFile ()
{
...
DecompressProtocol = NULL;
switch (CompressionSection->CompressionType) {
case EFI_STANDARD_COMPRESSION:
Status = InstallTianoDecompress (&DecompressProtocol);
break;
case EFI_CUSTOMIZED_COMPRESSION:
//
// Load user customized compression protocol.
//
Status = InstallCustomizedDecompress
((EFI_CUSTOMIZED_DECOMPRESS_PROTOCOL **) &DecompressProtocol);
break;
...
Status = DecompressProtocol->Decompress (
...
);
Many of the BIO modules are compressed with a
customized algorithm which is not opensourced in the
EDK,
Only the standard Tiano compression algorithm is open
sourced there.
Edk\Foundation\Library\Pei\PeiLib
\Decompress.c:
EFI_STATUS InstallTianoDecompress (
EFI_TIANO_DECOMPRESS_PROTOCOL
**This
)
{
*This = &mTianoDecompress;
return EFI_SUCCESS;
}
EFI_TIANO_DECOMPRESS_PROTOCOL
mTianoDecompress = {
TianoGetInfo,
TianoDecompress
};
EFI_STATUS EFIAPI TianoDecompress ()
{
return Decompress (
...
);
}
Edk\Foundation\Library\CustomizedDecompress
\CustomizedDecompress.c
EFI_STATUS
InstallCustomizedDecompress (
EFI_CUSTOMIZED_DECOMPRESS_PROTOCOL
**This
)
{
*This = &mCustomizedDecompress;
return EFI_SUCCESS;
}
EFI_CUSTOMIZED_DECOMPRESS_PROTOCOL
mCustomizedDecompress = {
CustomizedGetInfo,
CustomizedDecompress
};
EFI_STATUS EFIAPI
CustomizedDecompress ()
{
return EFI_UNSUPPORTED;
}
So, we had to look at the PeiProcessFile() implementation to
locate the decompressor code...
FFFF98CE movzx eax, [eax+EFI_COMPRESSION_SECTION.CompressionType]
FFFF98D2 sub eax, 0
FFFF98D5 jz EFI_UNSUPPORTED
FFFF98DB dec eax
FFFF98DC jz short EFI_STANDARD_COMPRESSION
FFFF98DE dec eax
FFFF98DF jnz EFI_UNSUPPORTED
FFFF98E5 mov edi, offset mCustomizedDecompress
FFFF98EA jmp short loc_FFFF98F1
FFFF98EC
FFFF98EC EFI_STANDARD_COMPRESSION:
FFFF98EC mov edi, offset mTianoDecompress
...
FFFF929C mTianoDecompress dd offset EfiTianoGetInfo
FFFF92A0 dd offset TianoDecompress
...
FFFF92F4 mCustomizedDecompress dd offset CustomizedGetInfo
FFFF92F8 dd offset CustomizedDecompress
FFFFBAE7 CustomizedDecompress proc
FFFFBAE7 push ebp
FFFFBAE8 mov ebp, esp
FFFFBAEA mov ecx, [ebp+arg_4]
FFFFBAED cmp byte ptr [ecx+3], 0
FFFFBAF1 push esi
FFFFBAF2 jnz short loc_FFFFBB25
FFFFBAF4 mov eax, [ebp+arg_18]
FFFFBAF7 mov esi, [ebp+arg_14]
FFFFBAFA shr eax, 1
FFFFBAFC push eax
FFFFBAFD lea edx, [eax+esi]
...
Does not look like "return EFI_UNSUPPORTED"! ;)
Possible Attack Vectors
Obviously, we cannot insert arbitrary code into .BIO update, as
the code is signed (and the signature is veried before reash
is allowed by the BIOS)
But still, the update process must parse "envelope" of the
update (rmware volume format), and perform crypto
operations; some potential for a vulnerability here...
(Although we don't exploit this today)
Does the update contain some unsigned fragments?
Yes, it contains the picture with boot splash logo (which can be
changed by e.g. an OEM)
Intel Integrator Toolkit lets you integrate your logo into the BIOS...
The BIOS displays the logo when booting
(this is at the very early stage of the boot)
The BMP image that is embedded into the *.BIO doesn't need to be
signed in any way (of course)
Where is The Bug?
https://edk.tianocore.org/
EFI_STATUS ConvertBmpToGopBlt ()
{
...
if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') {
return EFI_UNSUPPORTED;
}
BltBufferSize = BmpHeader->PixelWidth * BmpHeader->PixelHeight
* sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
IsAllocated = FALSE;
if (*GopBlt == NULL) {
*GopBltSize = BltBufferSize;
*GopBlt = EfiLibAllocatePool (*GopBltSize);
tiano_edk/source/Foundation/Library/Dxe/Graphics/Graphics.c:
In order to exploit the vulnerability we need to nd an actual code
for this function...
There is only one caller of the vulnerable function -
EnableQuietBootEx(), which is located in the same source le
EnableQuietBootEx() begins with a few references to protocol
GUIDs which can help spotting the binary module
Status = gBS->LocateProtocol (
&gEfiConsoleControlProtocolGuid,
NULL,
(VOID**)&ConsoleControl);
...
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiGraphicsOutputProtocolGuid,
(VOID**)&GraphicsOutput);
...
Status = gBS->HandleProtocol (
gST->ConsoleOutHandle,
&gEfiUgaDrawProtocolGuid,
(VOID**)&UgaDraw);
...
Status = gBS->LocateProtocol (
&gEfiOEMBadgingProtocolGuid,
NULL,
(VOID**)&Badging);
These GUIDs are dened in the EDK. By searching for
their values, the following (packed) le has been found:
A6F691AC-31C8-4444-854C-E2C1A6950F92
and it turns out it contains vulnerable
ConvertBmpToGopBlt() implementation.
.text:000000001000D2C9 sub rsp, 28h
.text:000000001000D2CD cmp byte ptr [rcx], 42h ; 'B'
.text:000000001000D2D0 mov rsi, r8
.text:000000001000D2D3 mov rbx, rcx
.text:000000001000D2D6 jnz loc_1000D518
.text:000000001000D2DC cmp byte ptr [rcx+1], 4Dh ; 'M'
.text:000000001000D2E0 jnz loc_1000D518
.text:000000001000D2E6 xor r13d, r13d
.text:000000001000D2E9 cmp [rcx+1Eh], r13d
.text:000000001000D2ED jnz loc_1000D518
.text:000000001000D2F3 mov edi, [rcx+0Ah]
.text:000000001000D2F6 add rdi, rcx
.text:000000001000D2F9 mov ecx, [rcx+12h] ; PixelWidth
.text:000000001000D2FC mov r12, rdi
.text:000000001000D2FF imul ecx, [rbx+16h] ; PixelHeight
.text:000000001000D303 shl rcx, 2 ; sizeof
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
.text:000000001000D307 cmp [r8], r13
.text:000000001000D30A jnz short loc_1000D32B
.text:000000001000D30C mov [r9], rcx
.text:000000001000D30F call sub_1000C6A0 ; alloc wrapper
Although the source for this function is publicly available, the
ability to unpack the .BIO update and view the actual assembly
was crucial for the future exploitation;
Particularly, e.g. GCC would produce code different to the one
actually used
Also, we could retrieve the assembly for the JPEG parser and
look for vulnerabilities there, even though its source code is
not available in Tiano SDK
A 64-bit code in BIOS?
Aren't all BIOSes execute in 16-bit real mode?
What happens if we use BMP with weird Width and Heigh?
e.g. W=64k, H=64k+1?
W*H*4 in 32bit arithmetics is only
256k (and the output buffer will have
this size); yet, the parser will try to
write over 16G of data there!
We want more the just DoS...
But what for? What can we gain from code execution here?
Keep in mind the BMP processing code executes at the very early
stage of the boot, when the reashing locks are not applied.
(So we can reash with any code we want!)
No reashing locks means our shellcode can reash the SPI chip!
parser code
BMP le
0
outbuf
IDT
#PF handler
GDT
PDE/PTEs
4G
source
source
The for loop that
does the buffer
overwrite
Unmapped memory
Diagram not in scale!
typedef struct {
UINT8 Blue;
UINT8 Green;
UINT8 Red;
UINT8 Reserved;
} EFI_GRAPHICS_OUTPUT_BLT_PIXEL;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
for (Height = 0;
Height < BmpHeader->PixelHeight;
Height++) {
Blt = &BltBuffer[(BmpHeader->PixelHeight-Height-1)*
BmpHeader->PixelWidth];
for (Width = 0; Width < BmpHeader->PixelWidth;
Width++, Image++, Blt++) {
/* 24bit bmp case */
Blt->Blue = *Image++;
Blt->Green = *Image++;
Blt->Red = *Image;
}
The idea of exploitation
The write starts at: (char*)BltBuffer + 4*(W-1)*H
We want to use it to overwrite some interesting data/code at
this address,
The allocation of BltBuffer must succeed, so that W*H,
computed in 32-bits arithmetics, must be reasonably small
How about trying to overwrite the body of the parser itself?
Bad news: suitable W and H do not exist :(
So, inevitably, the parser will raise #PF...
parser code
BMP le
0
outbuf
GDT
PDE/PTEs
4G
source
source
The for loop that does
the buffer overwrite
Unmapped memory
We control this
memory via our
overow
IDT
#PF handler
#PF exception raised
(access to unmapped
memory)
Diagram not in scale!
Triggering the overow
W*H is small (computed in 32 bits)
[WRITESTART=BltBuffer+4*(W-1)*H]<=IDT_BASE
(computed in 64bits)
WRITESTART+8*64k >= HIGHEST_ADDR
(computed in 64bits)
the relevant data structure
(PDE) with highest address
Some numbers
#define WRITESTART 0x7b918994
#define IDT 0x7b952018
#define PF_HANDLER 0x7b9540f8
#define PML4 0x7b98a000
#define PDPT0 (PML4+0x1000)
#define PDE01c0 0x7B98C070
#define PDE0140 0x7B98C050
#define PDE7b80 0x7B98DEE0
#define PDE0000 0x7B98C000
#define GDT38 0x7B958F58
#define BMP_WIDTH 0xe192a103
#define BMP_HEIGHT 0x48a20476
Intel DQ45, 2GB DRAM, BIOS version: CB0075
W = 0xe192a103
H = 0x48a20476
(int)(W*H) = 17250
This is the size for which
the buffer will be allocated
Taking care of details
parser code
BMP le
0
outbuf
IDT
#PF handler
GDT
PDE/PTEs
4G
source
source
The for loop that does
the buffer overwrite
Unmapped memory
Diagram not in scale!
We must preserve IDT[0xe] -- the #PF handler address
We will overwrite it with a JMP to our shellcode
We must preserve the CS entry in GDT
We must preserve a few PTEs as well
(e.g. the one for the stack)
#PF handler
JMP RBX
BMP le
The rst two bytes of a BMP image are: "BM"
-- luckily this resolves to two REX prexes on
x86_64, which allows the execution to
smoothly reach our shellcode (just need to
choose the rst bytes of the shellcode to make
a valid instruction together with those two
REX prexes).
shellcode
"BM"
Putting it all together
User experience
Two (2) reboots: one to trigger update processing,
second, after reashing, to resume infected bios.
It is enough to reash only small region of a ash, so
reashing is quick.
No physical access to the machine is needed!
Looks easy, but how we got all the info about how does the BIOS
memory map looks like? How we performed debugging?
But what about nding offsets for different motherboards/different
memory congurations?
The relevant BIOS data structures (say, IDT, page tables) are
not wiped before handing control to OS; so if OS takes care
not to trash them, all the required offsets can be found in
memory.
So, we can create a small "Stub-OS", infect MBR with it,
reboot the system, and gather the offsets.
We have not implemented this.
Preparing a "development" board
Extra socket soldered to the
motherboard (special thanks
to Piotr Witczak, AVT
Polska)
The SPI-ash chip
The SPI-ash chip
EEPROM Programmer
Where the SPI-ash is
originally soldered in
(normally there is no
socket)
Intel Q45 Board
Still, keep in mind that our exploit is software-only!
(This hardware was only necessary to develop the exploit)
Consequences of BIOS Reash
Malware persistence
SMM rootkits
BIOS reash
attack
SMM
compromise
reash &
reboot
SMM attack
Drawbacks
Very rmware-specic
Very offset-dependent
Very complex debugging
Still, we showed it is possible to bypass the rmware protection on
one of the most secure and latest hardware
BIOS code holds the keys to important system capabilities;
therefore, it is important to code it safely!
Yet-Another-On-The-Fly
SMM Attack
BIOS Reashing Attacks vs. SMM Attacks
BIOS reash
attack
SMM
compromise
reash &
reboot
SMM attack
SMM research quick history
2006: Loic Duot
(not an attack against SMM, SMM unprotected < 2006)
2008: Sherri Sparks, Shawn Embleton
(SMM rooktis, but not attacks on SMM!)
2008: Invisible Things Lab (Memory Remapping bug in Q35 BIOS)
2009: Invisible Things Lab (CERT VU#127284, TBA)
2009: ITL and Duot (independently!): (Caching attacks on SMM)
(checked box means new SMM attack presented; unchecked means no attack on SMM presented)
Note: the two previously presented SMM attacks (remapping attack,
and caching attack) did not rely on the vulnerabilities present in the
SMM code itself, but rather in different mechanisms, that just
happened to allow also an access to the SMM
VU#127284 is different...
We discovered it in December 2008 and used in our TXT bypassing
attack presented at Black Hat DC in February 2009
Until yesterday there was no patch...
We analyzed fragments of the SMM code used by Intel BIOS
mov 0x407d(%rip),%rax #TSEG+0x4608
callq *0x18(%rax)
The TSEG+0x4608 locations holds a value OUTSIDE of
SMRAM namely in ACPI NV storage, which is a DRAM
location freely accessible by OS...
Exploitation: overwrite ACPI NV storage memory with a pointer of
your choice, then trigger SMI in a way that results in reaching the
above code.
SMRAM
ACPINV
call [ACPINV+x]
This memory is not protected
by the chipset! OS (and
attacker) can modify it at will!
Shellcode
http://invisiblethingslab.com