2019-11-08 03:20:20 +01:00
|
|
|
# Ivy Bridge Lenovo ThinkPad Internal Flashing
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
|
|
|
Old versions of stock BIOS for these models have several security issues.
|
|
|
|
In order to flash coreboot internally, two of them are of interest.
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
**First** is the fact the `SMM_BWP` and `BLE` are not enabled in BIOS
|
2019-11-08 03:20:20 +01:00
|
|
|
versions released before 2014. We have tested many versions on T430 and
|
2020-02-05 16:42:48 +01:00
|
|
|
X230 and found out that `SMM_BWP=1` only since the update, the changelog
|
2019-11-08 03:20:20 +01:00
|
|
|
of which contains following line:
|
|
|
|
|
|
|
|
> (New) Improved the UEFI BIOS security feature.
|
|
|
|
|
|
|
|
**Second** is [S3 Boot Script vulnerability](https://support.lenovo.com/eg/ru/product_security/s3_boot_protect),
|
|
|
|
that was discovered and fixed later.
|
|
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
|
|
- USB drive (in case you need to downgrade BIOS)
|
|
|
|
- Linux install that (can be) loaded in UEFI mode
|
|
|
|
- [CHIPSEC](https://github.com/chipsec/chipsec)
|
|
|
|
|
|
|
|
## BIOS versions
|
|
|
|
|
|
|
|
Below is a table of BIOS versions that are vulnerable enough for our
|
|
|
|
goals, per model. The version number means that you need to downgrade to
|
|
|
|
that or earlier version.
|
|
|
|
|
|
|
|
```eval_rst
|
|
|
|
+------------+--------------+
|
|
|
|
| Model | BIOS version |
|
|
|
|
+============+==============+
|
|
|
|
| X230 | 2.60 |
|
|
|
|
+------------+--------------+
|
|
|
|
| X230T | 2.58 |
|
|
|
|
+------------+--------------+
|
|
|
|
| T430 | 2.64 |
|
|
|
|
+------------+--------------+
|
|
|
|
| T430s | 2.59 |
|
|
|
|
+------------+--------------+
|
|
|
|
| T530 | 2.60 |
|
|
|
|
+------------+--------------+
|
|
|
|
| W530 | 2.58 |
|
|
|
|
+------------+--------------+
|
|
|
|
```
|
|
|
|
|
|
|
|
If your BIOS version is equal or lower, skip to the
|
|
|
|
**[Examining protections](#examining-protections-theory)** section. If not,
|
|
|
|
go through the downgrade process, described next.
|
|
|
|
|
|
|
|
## Downgrading BIOS
|
|
|
|
|
|
|
|
Go to the Lenovo web site and download BIOS Update Bootable CD for your
|
|
|
|
machine of needed version (see above).
|
|
|
|
|
|
|
|
Lenovo states that BIOS has "security rollback prevention", meaning once
|
|
|
|
you update it to some version X, you will not be able to downgrade it to
|
|
|
|
pre-X version. That's not true. It seems that this is completely
|
|
|
|
client-side restriction in flashing utilities (both Windows utility and
|
|
|
|
Bootable CD). You just need to call `winflash.exe` or `dosflash.exe`
|
|
|
|
directly. Therefore you need to modify the bootable CD image you just
|
|
|
|
downloaded.
|
|
|
|
|
|
|
|
Extract an El Torito image:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
geteltorito -o ./bios.img g1uj41us.iso
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
Mount the partition in that image:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo mount -t vfat ./bios.img /mnt -o loop,offset=16384
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
List files, find the `AUTOEXEC.BAT` file and the `FLASH` directory:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
ls /mnt
|
|
|
|
ls /mnt/FLASH
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Inside the `FLASH` directory, there should be a directory called
|
|
|
|
`G1ET93WW` or similar (exact name depends on your ThinkPad model and
|
|
|
|
BIOS version). See what's inside:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
ls /mnt/FLASH/G1ET93WW
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
There must be a file with `.FL1` extension called `$01D2000.FL1` or
|
|
|
|
something similar.
|
|
|
|
|
|
|
|
Now open the `AUTOEXEC.BAT` file:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo vim /mnt/AUTOEXEC.BAT
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
You will see a list of commands:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
@ECHO OFF
|
|
|
|
PROMPT $p$g
|
|
|
|
cd c:\flash
|
|
|
|
command.com
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
Replace the last line (`command.com`) with this (change path to the
|
|
|
|
`.FL1` file according to yours):
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
dosflash.exe /sd /file G1ET93WW\$01D2000.FL1
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Save the file, then unmount the partition:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
2020-04-08 14:42:28 +02:00
|
|
|
sudo umount /mnt
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Write this image to a USB drive (replace `/dev/sdX` with your USB drive
|
|
|
|
device name):
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo dd if=./bios.img of=/dev/sdX bs=1M
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Now reboot and press F1 to enter BIOS settings. Open the **Startup** tab
|
|
|
|
and set the startup mode to **Legacy** (or **Both**/**Legacy First**):
|
|
|
|
|
|
|
|
![](ivb_bios_legacy_only.jpg)
|
|
|
|
|
|
|
|
Press F10 to save changes and reboot.
|
|
|
|
|
|
|
|
Now, before you process, make sure that AC adapter is connected! If your
|
|
|
|
battery will die during the process, you'll likely need external
|
|
|
|
programmer to recover.
|
|
|
|
|
|
|
|
Boot from the USB drive (press F12 to select boot device), and BIOS
|
|
|
|
flashing process should begin:
|
|
|
|
|
|
|
|
![](ivb_bios_flashing1.jpg)
|
|
|
|
|
|
|
|
![](ivb_bios_flashing2.jpg)
|
|
|
|
|
|
|
|
It may reboot a couple of times in the process. Do not interrupt it.
|
|
|
|
|
|
|
|
When it's completed, go back to the BIOS settings and set startup mode
|
|
|
|
to **UEFI** (or **Both**/**UEFI First**). This is required for
|
|
|
|
vulnerability exploitation.
|
|
|
|
|
|
|
|
![](ivb_bios_uefi_only.jpg)
|
|
|
|
|
|
|
|
Then boot to your system and make sure that `/sys/firmware/efi` or
|
|
|
|
`/sys/firmware/efivars` exist.
|
|
|
|
|
|
|
|
## Examining protections (theory)
|
|
|
|
|
|
|
|
There are two main ways that Intel platform provides to protect BIOS
|
|
|
|
chip:
|
|
|
|
- **BIOS_CNTL** register of LPC Interface Bridge Registers (accessible
|
|
|
|
via PCI configuration space, offset 0xDC). It has:
|
|
|
|
* **SMM_BWP** (*SMM BIOS Write Protect*) bit. If set to 1, the BIOS is
|
|
|
|
writable only in SMM. Once set to 1, cannot be changed anymore.
|
|
|
|
* **BLE** (*BIOS Lock Enable*) bit. If set to 1, setting BIOSWE to 1
|
|
|
|
will raise SMI. Once set to 1, cannot be changed anymore.
|
|
|
|
* **BIOSWE** (*BIOS Write Enable*) bit. Controls whether BIOS is
|
|
|
|
writable. This bit is always R/W.
|
|
|
|
- SPI Protected Range Registers (**PR0**-**PR4**) of SPI Configuration
|
|
|
|
Registers (SPIBAR+0x74 - SPIBAR+0x84). Each register has bits that
|
|
|
|
define protected range, plus WP bit, that defines whether write
|
|
|
|
protection is enabled.
|
|
|
|
|
|
|
|
There's also **FLOCKDN** bit of HSFS register (SPIBAR+0x04) of SPI
|
|
|
|
Configuration Registers. When set to 1, PR0-PR4 registers cannot be
|
|
|
|
written. Once set to 1, cannot be changed anymore.
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
To be able to flash, we need `SMM_BWP=0`, `BIOSWE=1`, `BLE=0`, `FLOCKDN=0` or
|
2019-11-08 03:20:20 +01:00
|
|
|
SPI protected ranges (PRx) to have a WP bit set to 0.
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
Let's see what we have. Examine `HSFS` register:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_main -m chipsec.modules.common.spi_lock
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
You should see that `FLOCKDN=1`:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
[x][ =======================================================================
|
|
|
|
[x][ Module: SPI Flash Controller Configuration Locks
|
|
|
|
[x][ =======================================================================
|
|
|
|
[*] HSFS = 0xE009 << Hardware Sequencing Flash Status Register (SPIBAR + 0x4)
|
|
|
|
[00] FDONE = 1 << Flash Cycle Done
|
|
|
|
[01] FCERR = 0 << Flash Cycle Error
|
|
|
|
[02] AEL = 0 << Access Error Log
|
|
|
|
[03] BERASE = 1 << Block/Sector Erase Size
|
|
|
|
[05] SCIP = 0 << SPI cycle in progress
|
|
|
|
[13] FDOPSS = 1 << Flash Descriptor Override Pin-Strap Status
|
|
|
|
[14] FDV = 1 << Flash Descriptor Valid
|
|
|
|
[15] FLOCKDN = 1 << Flash Configuration Lock-Down
|
2019-11-08 03:20:20 +01:00
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
Then check `BIOS_CNTL` and PR0-PR4:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_main -m common.bios_wp
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
Good news: on old BIOS versions, `SMM_BWP=0` and `BLE=0`.
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Bad news: there are 4 write protected SPI ranges:
|
|
|
|
|
2020-02-05 16:36:49 +01:00
|
|
|
[x][ =======================================================================
|
|
|
|
[x][ Module: BIOS Region Write Protection
|
|
|
|
[x][ =======================================================================
|
|
|
|
[*] BC = 0x 8 << BIOS Control (b:d.f 00:31.0 + 0xDC)
|
|
|
|
[00] BIOSWE = 0 << BIOS Write Enable
|
|
|
|
[01] BLE = 0 << BIOS Lock Enable
|
|
|
|
[02] SRC = 2 << SPI Read Configuration
|
|
|
|
[04] TSS = 0 << Top Swap Status
|
|
|
|
[05] SMM_BWP = 0 << SMM BIOS Write Protection
|
|
|
|
[-] BIOS region write protection is disabled!
|
|
|
|
|
|
|
|
[*] BIOS Region: Base = 0x00500000, Limit = 0x00BFFFFF
|
|
|
|
SPI Protected Ranges
|
|
|
|
------------------------------------------------------------
|
|
|
|
PRx (offset) | Value | Base | Limit | WP? | RP?
|
|
|
|
------------------------------------------------------------
|
|
|
|
PR0 (74) | 00000000 | 00000000 | 00000000 | 0 | 0
|
|
|
|
PR1 (78) | 8BFF0B40 | 00B40000 | 00BFFFFF | 1 | 0
|
|
|
|
PR2 (7C) | 8B100B10 | 00B10000 | 00B10FFF | 1 | 0
|
|
|
|
PR3 (80) | 8ADE0AD0 | 00AD0000 | 00ADEFFF | 1 | 0
|
|
|
|
PR4 (84) | 8AAF0800 | 00800000 | 00AAFFFF | 1 | 0
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Other way to examine SPI configuration registers is to just dump SPIBAR:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_util mmio dump SPIBAR
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
You will see `SPIBAR` address (0xFED1F800) and registers (for example,
|
|
|
|
`00000004` is `HSFS`):
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
[mmio] MMIO register range [0x00000000FED1F800:0x00000000FED1F800+00000200]:
|
|
|
|
+00000000: 0BFF0500
|
|
|
|
+00000004: 0004E009
|
|
|
|
...
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
As you can see, the only thing we need is to unset WP bit on PR0-PR4.
|
2020-02-05 16:42:48 +01:00
|
|
|
But that cannot be done once `FLOCKDN` is set to 1.
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Now the fun part!
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
`FLOCKDN` may only be cleared by a hardware reset, which includes S3
|
2019-11-08 03:20:20 +01:00
|
|
|
state. On S3 resume boot path, the chipset configuration has to be
|
|
|
|
restored and it's done by executing so-called S3 Boot Scripts. You can
|
|
|
|
dump these scripts by executing:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_util uefi s3bootscript
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
There are many entries. Along them, you can find instructions to write
|
2020-02-05 16:42:48 +01:00
|
|
|
to `HSFS` (remember, we know that `SPIBAR` is 0xFED1F800):
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
Entry at offset 0x2B8F (len = 0x17, header len = 0x0):
|
|
|
|
Data:
|
|
|
|
02 00 17 02 00 00 00 01 00 00 00 04 f8 d1 fe 00 |
|
|
|
|
00 00 00 09 e0 04 00 |
|
|
|
|
Decoded:
|
|
|
|
Opcode : S3_BOOTSCRIPT_MEM_WRITE (0x0002)
|
|
|
|
Width : 0x02 (4 bytes)
|
|
|
|
Address: 0xFED1F804
|
|
|
|
Count : 0x1
|
|
|
|
Values : 0x0004E009
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
These scripts are stored in memory. The vulnerability is that we can
|
|
|
|
overwrite this memory, change these instructions and they will be
|
2020-02-05 16:42:48 +01:00
|
|
|
executed on S3 resume. Once we patch that instruction to not set `FLOCKDN`
|
2019-11-08 03:20:20 +01:00
|
|
|
bit, we will be able to write to PR0-PR4 registers.
|
|
|
|
|
|
|
|
## Creating a backup
|
|
|
|
|
|
|
|
Before you proceed, please create a backup of the `bios` region. Then,
|
|
|
|
in case something goes wrong, you'll be able to flash it back externally.
|
|
|
|
|
|
|
|
The `me` region is locked, so an attempt to create a full dump will fail.
|
|
|
|
But you can back up the `bios`:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo flashrom -p internal -r bios_backup.rom --ifd -i bios
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
If you will ever need to flash it back, use `--ifd -i bios` as well:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo flashrom -p <YOUR_PROGRAMMER> -w bios_backup.rom --ifd -i bios
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
**Caution:** if you will omit `--ifd -i bios` for flashing, you will
|
|
|
|
brick your machine, because your backup has `FF`s in place of `fd` and
|
|
|
|
`me` regions. Flash only `bios` region!
|
|
|
|
|
|
|
|
## Removing protections (practice)
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
The original boot script writes 0xE009 to `HSFS`. `FLOCKDN` is 15th bit, so
|
2019-11-08 03:20:20 +01:00
|
|
|
let's write 0x6009 instead:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_main -m tools.uefi.s3script_modify -a replace_op,mmio_wr,0xFED1F804,0x6009,0x2
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
You will get a lot of output and in the end you should see something
|
|
|
|
like this:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
[*] Modifying S3 boot script entry at address 0x00000000DAF49B8F..
|
|
|
|
[mem] 0x00000000DAF49B8F
|
|
|
|
[*] Original entry:
|
|
|
|
2 0 17 2 0 0 0 1 0 0 0 4 f8 d1 fe 0 |
|
|
|
|
0 0 0 9 e0 4 0 |
|
|
|
|
[mem] buffer len = 0x17 to PA = 0x00000000DAF49B8F
|
|
|
|
2 0 17 2 0 0 0 1 0 0 0 4 f8 d1 fe 0 |
|
|
|
|
0 0 0 9 60 0 0 | `
|
|
|
|
[mem] 0x00000000DAF49B8F
|
|
|
|
[*] Modified entry:
|
|
|
|
2 0 17 2 0 0 0 1 0 0 0 4 f8 d1 fe 0 |
|
|
|
|
0 0 0 9 60 0 0 | `
|
|
|
|
[*] After sleep/resume, check the value of register 0xFED1F804 is 0x6009
|
|
|
|
[+] PASSED: The script has been modified. Go to sleep..
|
|
|
|
|
2020-02-05 16:42:48 +01:00
|
|
|
Now go to S3, then resume and check `FLOCKDN`. It should be 0:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_main -m chipsec.modules.common.spi_lock
|
|
|
|
|
|
|
|
...
|
|
|
|
[x][ =======================================================================
|
|
|
|
[x][ Module: SPI Flash Controller Configuration Locks
|
|
|
|
[x][ =======================================================================
|
|
|
|
[*] HSFS = 0x6008 << Hardware Sequencing Flash Status Register (SPIBAR + 0x4)
|
|
|
|
[00] FDONE = 0 << Flash Cycle Done
|
|
|
|
[01] FCERR = 0 << Flash Cycle Error
|
|
|
|
[02] AEL = 0 << Access Error Log
|
|
|
|
[03] BERASE = 1 << Block/Sector Erase Size
|
|
|
|
[05] SCIP = 0 << SPI cycle in progress
|
|
|
|
[13] FDOPSS = 1 << Flash Descriptor Override Pin-Strap Status
|
|
|
|
[14] FDV = 1 << Flash Descriptor Valid
|
|
|
|
[15] FLOCKDN = 0 << Flash Configuration Lock-Down
|
|
|
|
[-] SPI Flash Controller configuration is not locked
|
|
|
|
[-] FAILED: SPI Flash Controller not locked correctly.
|
|
|
|
...
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
Remove WP from protected ranges:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_util mmio write SPIBAR 0x74 0x4 0xAAF0800
|
|
|
|
sudo chipsec_util mmio write SPIBAR 0x78 0x4 0xADE0AD0
|
|
|
|
sudo chipsec_util mmio write SPIBAR 0x7C 0x4 0xB100B10
|
|
|
|
sudo chipsec_util mmio write SPIBAR 0x80 0x4 0xBFF0B40
|
|
|
|
|
2019-11-08 03:20:20 +01:00
|
|
|
Verify that it worked:
|
2020-02-05 16:36:49 +01:00
|
|
|
|
|
|
|
sudo chipsec_main -m common.bios_wp
|
|
|
|
|
|
|
|
[x][ =======================================================================
|
|
|
|
[x][ Module: BIOS Region Write Protection
|
|
|
|
[x][ =======================================================================
|
|
|
|
[*] BC = 0x 9 << BIOS Control (b:d.f 00:31.0 + 0xDC)
|
|
|
|
[00] BIOSWE = 1 << BIOS Write Enable
|
|
|
|
[01] BLE = 0 << BIOS Lock Enable
|
|
|
|
[02] SRC = 2 << SPI Read Configuration
|
|
|
|
[04] TSS = 0 << Top Swap Status
|
|
|
|
[05] SMM_BWP = 0 << SMM BIOS Write Protection
|
|
|
|
[-] BIOS region write protection is disabled!
|
|
|
|
|
|
|
|
[*] BIOS Region: Base = 0x00500000, Limit = 0x00BFFFFF
|
|
|
|
SPI Protected Ranges
|
|
|
|
------------------------------------------------------------
|
|
|
|
PRx (offset) | Value | Base | Limit | WP? | RP?
|
|
|
|
------------------------------------------------------------
|
|
|
|
PR0 (74) | 0AAF0800 | 00800000 | 00AAF000 | 0 | 0
|
|
|
|
PR1 (78) | 0ADE0AD0 | 00AD0000 | 00ADE000 | 0 | 0
|
|
|
|
PR2 (7C) | 0B100B10 | 00B10000 | 00B10000 | 0 | 0
|
|
|
|
PR3 (80) | 0BFF0B40 | 00B40000 | 00BFF000 | 0 | 0
|
|
|
|
PR4 (84) | 00000000 | 00000000 | 00000000 | 0 | 0
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Bingo!
|
|
|
|
|
2022-03-01 02:15:48 +01:00
|
|
|
Now you can [flash internally]. Remember to flash only the `bios` region
|
|
|
|
(use `--ifd -i bios -N` flashrom arguments). `fd` and `me` are still
|
|
|
|
locked.
|
2019-11-08 03:20:20 +01:00
|
|
|
|
|
|
|
Note that you should have an external SPI programmer as a backup method.
|
|
|
|
It will help you recover if you flash non-working ROM by mistake.
|
2022-03-01 02:15:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
[flash internally]: ../../flash_tutorial/int_flashrom.md
|