feat: initial support for L1TF

This commit is contained in:
Stéphane Lesimple 2018-08-15 11:59:23 +02:00
parent 360be7b35f
commit e942616189
2 changed files with 120 additions and 29 deletions

View File

@ -2,11 +2,14 @@ Spectre & Meltdown Checker
========================== ==========================
A shell script to tell if your system is vulnerable against the several "speculative execution" CVEs that were made public in 2018. A shell script to tell if your system is vulnerable against the several "speculative execution" CVEs that were made public in 2018.
- CVE-2017-5753 aka Spectre Variant 1 - CVE-2017-5753 [bounds check bypass] aka 'Spectre Variant 1'
- CVE-2017-5715 aka Spectre Variant 2 - CVE-2017-5715 [branch target injection] aka 'Spectre Variant 2'
- CVE-2017-5754 aka Meltdown or Variant 3 - CVE-2017-5754 [rogue data cache load] aka 'Meltdown' aka 'Variant 3'
- CVE-2018-3640 aka Variant 3a - CVE-2018-3640 [rogue system register read] aka 'Variant 3a'
- CVE-2018-3639 aka Variant 4 - CVE-2018-3639 [speculative store bypass] aka 'Variant 4'
- CVE-2018-3615, CVE-2018-3620, CVE-2018-3646 [L1 terminal fault] aka 'Foreshadow & Foreshadow-NG'
**2018-08-15: Foreshadow support is being worked on, vulnerable/immune CPUs are already detected, and kernel-reported vulnerability status is checked, but manual mitigation detection is currently being implemented, will be available in the next few days**
Supported operating systems: Supported operating systems:
- Linux (all versions, flavors and distros) - Linux (all versions, flavors and distros)
@ -98,6 +101,18 @@ docker run --rm --privileged -v /boot:/boot:ro -v /lib/modules:/lib/modules:ro -
- Mitigation: microcode update + kernel update making possible for affected software to protect itself - Mitigation: microcode update + kernel update making possible for affected software to protect itself
- Performance impact of the mitigation: low to medium - Performance impact of the mitigation: low to medium
**CVE-2018-3615** l1 terminal fault (Foreshadow)
- TBC
**CVE-2018-3620** l1 terminal fault (Foreshadow-NG)
- TBC
**CVE-2018-3646** l1 terminal fault (Foreshadow-NG)
- TBC
## Understanding what this script does and doesn't ## Understanding what this script does and doesn't
This tool does its best to determine whether your system is immune (or has proper mitigations in place) for the collectively named "speculative execution" vulnerabilities. It doesn't attempt to run any kind of exploit, and can't guarantee that your system is secure, but rather helps you verifying whether your system has the known correct mitigations in place. This tool does its best to determine whether your system is immune (or has proper mitigations in place) for the collectively named "speculative execution" vulnerabilities. It doesn't attempt to run any kind of exploit, and can't guarantee that your system is secure, but rather helps you verifying whether your system has the known correct mitigations in place.

View File

@ -9,7 +9,7 @@
# #
# Stephane Lesimple # Stephane Lesimple
# #
VERSION='0.39' VERSION='0.39+'
trap 'exit_cleanup' EXIT trap 'exit_cleanup' EXIT
trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT trap '_warn "interrupted, cleaning up..."; exit_cleanup; exit 1' INT
@ -64,7 +64,7 @@ show_usage()
--batch nrpe produce machine readable output formatted for NRPE --batch nrpe produce machine readable output formatted for NRPE
--batch prometheus produce output for consumption by prometheus-node-exporter --batch prometheus produce output for consumption by prometheus-node-exporter
--variant [1,2,3,3a,4] specify which variant you'd like to check, by default all variants are checked, --variant [1,2,3,3a,4,l1tf] specify which variant you'd like to check, by default all variants are checked,
can be specified multiple times (e.g. --variant 2 --variant 3) can be specified multiple times (e.g. --variant 2 --variant 3)
--hw-only only check for CPU information, don't check for any variant --hw-only only check for CPU information, don't check for any variant
--no-hw skip CPU information and checks, if you're inspecting a kernel not to be run on this host --no-hw skip CPU information and checks, if you're inspecting a kernel not to be run on this host
@ -126,6 +126,7 @@ opt_variant2=0
opt_variant3=0 opt_variant3=0
opt_variant3a=0 opt_variant3a=0
opt_variant4=0 opt_variant4=0
opt_variantl1tf=0
opt_allvariants=1 opt_allvariants=1
opt_no_sysfs=0 opt_no_sysfs=0
opt_sysfs_only=0 opt_sysfs_only=0
@ -246,11 +247,12 @@ _is_cpu_vulnerable_cached()
{ {
# shellcheck disable=SC2086 # shellcheck disable=SC2086
{ {
[ "$1" = 1 ] && return $variant1 [ "$1" = 1 ] && return $variant1
[ "$1" = 2 ] && return $variant2 [ "$1" = 2 ] && return $variant2
[ "$1" = 3 ] && return $variant3 [ "$1" = 3 ] && return $variant3
[ "$1" = 3a ] && return $variant3a [ "$1" = 3a ] && return $variant3a
[ "$1" = 4 ] && return $variant4 [ "$1" = 4 ] && return $variant4
[ "$1" = l1tf ] && return $variantl1tf
} }
echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2 echo "$0: error: invalid variant '$1' passed to is_cpu_vulnerable()" >&2
exit 255 exit 255
@ -273,6 +275,7 @@ is_cpu_vulnerable()
variant3='' variant3=''
variant3a='' variant3a=''
variant4='' variant4=''
variantl1tf=''
if is_cpu_specex_free; then if is_cpu_specex_free; then
variant1=immune variant1=immune
@ -280,6 +283,7 @@ is_cpu_vulnerable()
variant3=immune variant3=immune
variant3a=immune variant3a=immune
variant4=immune variant4=immune
variantl1tf=immune
elif is_intel; then elif is_intel; then
# Intel # Intel
# https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable # https://github.com/crozone/SpectrePoC/issues/1 ^F E5200 => spectre 2 not vulnerable
@ -294,20 +298,52 @@ is_cpu_vulnerable()
# capability bit for future Intel processor that will explicitly state # capability bit for future Intel processor that will explicitly state
# that they're not vulnerable to Meltdown # that they're not vulnerable to Meltdown
# this var is set in check_cpu() # this var is set in check_cpu()
variant3=immune [ -z "$variant3" ] && variant3=immune
_debug "is_cpu_vulnerable: RDCL_NO is set so not vuln to meltdown" [ -z "$variantl1tf" ] && variantl1tf=immune
_debug "is_cpu_vulnerable: RDCL_NO is set so not vuln to meltdown nor l1tf"
fi fi
if [ "$capabilities_ssb_no" = 1 ]; then if [ "$capabilities_ssb_no" = 1 ]; then
# capability bit for future Intel processor that will explicitly state # capability bit for future Intel processor that will explicitly state
# that they're not vulnerable to Variant 4 # that they're not vulnerable to Variant 4
# this var is set in check_cpu() # this var is set in check_cpu()
variant4=immune [ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: SSB_NO is set so not vuln to variant4" _debug "is_cpu_vulnerable: SSB_NO is set so not vuln to variant4"
fi fi
if is_cpu_ssb_free; then if is_cpu_ssb_free; then
[ -z "$variant4" ] && variant4=immune [ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4" _debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
fi fi
# variant 4a for xeon phi
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] || [ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
_debug "is_cpu_vulnerable: xeon phi immune to variant 3a"
[ -z "$variant3a" ] && variant3a=immune
fi
fi
# L1TF (RDCL_NO already checked above)
if [ "$cpu_family" = 6 ]; then
if [ "$cpu_model" = "$INTEL_FAM6_ATOM_CEDARVIEW" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_CLOVERVIEW" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_LINCROFT" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_PENWELL" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_PINEVIEW" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT1" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_SILVERMONT2" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_AIRMONT" ] || \
[ "$cpu_model" = "$INTEL_FAM6_ATOM_MERRIFIELD" ] || \
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNL" ] || \
[ "$cpu_model" = "$INTEL_FAM6_XEON_PHI_KNM" ]; then
_debug "is_cpu_vulnerable: intel family 6 but model known to be immune"
[ -z "$variantl1tf" ] && variantl1tf=immune
else
_debug "is_cpu_vulnerable: intel family 6 is vuln"
variantl1tf=vuln
fi
elif [ "$cpu_family" -lt 6 ]; then
_debug "is_cpu_vulnerable: intel family < 6 is immune"
[ -z "$variantl1tf" ] && variantl1tf=immune
fi
elif is_amd; then elif is_amd; then
# AMD revised their statement about variant2 => vulnerable # AMD revised their statement about variant2 => vulnerable
# https://www.amd.com/en/corporate/speculative-execution # https://www.amd.com/en/corporate/speculative-execution
@ -321,9 +357,11 @@ is_cpu_vulnerable()
[ -z "$variant4" ] && variant4=immune [ -z "$variant4" ] && variant4=immune
_debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4" _debug "is_cpu_vulnerable: cpu not affected by speculative store bypass so not vuln to variant4"
fi fi
variantl1tf=immune
elif [ "$cpu_vendor" = CAVIUM ]; then elif [ "$cpu_vendor" = CAVIUM ]; then
variant3=immune variant3=immune
variant3a=immune variant3a=immune
variantl1tf=immune
elif [ "$cpu_vendor" = ARM ]; then elif [ "$cpu_vendor" = ARM ]; then
# ARM # ARM
# reference: https://developer.arm.com/support/security-update # reference: https://developer.arm.com/support/security-update
@ -411,14 +449,16 @@ is_cpu_vulnerable()
fi fi
_debug "is_cpu_vulnerable: for cpu$i and so far, we have <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>" _debug "is_cpu_vulnerable: for cpu$i and so far, we have <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>"
done done
variantl1tf=immune
fi fi
_debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>" _debug "is_cpu_vulnerable: temp results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf>"
[ "$variant1" = "immune" ] && variant1=1 || variant1=0 [ "$variant1" = "immune" ] && variant1=1 || variant1=0
[ "$variant2" = "immune" ] && variant2=1 || variant2=0 [ "$variant2" = "immune" ] && variant2=1 || variant2=0
[ "$variant3" = "immune" ] && variant3=1 || variant3=0 [ "$variant3" = "immune" ] && variant3=1 || variant3=0
[ "$variant3a" = "immune" ] && variant3a=1 || variant3a=0 [ "$variant3a" = "immune" ] && variant3a=1 || variant3a=0
[ "$variant4" = "immune" ] && variant4=1 || variant4=0 [ "$variant4" = "immune" ] && variant4=1 || variant4=0
_debug "is_cpu_vulnerable: final results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4>" [ "$variantl1tf" = "immune" ] && variantl1tf=1 || variantl1tf=0
_debug "is_cpu_vulnerable: final results are <$variant1> <$variant2> <$variant3> <$variant3a> <$variant4> <$variantl1tf>"
is_cpu_vulnerable_cached=1 is_cpu_vulnerable_cached=1
_is_cpu_vulnerable_cached "$1" _is_cpu_vulnerable_cached "$1"
return $? return $?
@ -607,17 +647,18 @@ while [ -n "$1" ]; do
shift shift
elif [ "$1" = "--variant" ]; then elif [ "$1" = "--variant" ]; then
if [ -z "$2" ]; then if [ -z "$2" ]; then
echo "$0: error: option --variant expects a parameter (1, 2, 3, 3a or 4)" >&2 echo "$0: error: option --variant expects a parameter (1, 2, 3, 3a, 4 or l1tf)" >&2
exit 255 exit 255
fi fi
case "$2" in case "$2" in
1) opt_variant1=1; opt_allvariants=0;; 1) opt_variant1=1; opt_allvariants=0;;
2) opt_variant2=1; opt_allvariants=0;; 2) opt_variant2=1; opt_allvariants=0;;
3) opt_variant3=1; opt_allvariants=0;; 3) opt_variant3=1; opt_allvariants=0;;
3a) opt_variant3a=1; opt_allvariants=0;; 3a) opt_variant3a=1; opt_allvariants=0;;
4) opt_variant4=1; opt_allvariants=0;; 4) opt_variant4=1; opt_allvariants=0;;
l1tf) opt_variantl1tf=1; opt_allvariants=0;;
*) *)
echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a or 4" >&2; echo "$0: error: invalid parameter '$2' for --variant, expected either 1, 2, 3, 3a, 4 or l1tf" >&2;
exit 255 exit 255
;; ;;
esac esac
@ -686,6 +727,7 @@ pvulnstatus()
CVE-2017-5754) aka="MELTDOWN";; CVE-2017-5754) aka="MELTDOWN";;
CVE-2018-3640) aka="VARIANT 3A";; CVE-2018-3640) aka="VARIANT 3A";;
CVE-2018-3639) aka="VARIANT 4";; CVE-2018-3639) aka="VARIANT 4";;
CVE-2018-3615/3620/3646) aka="L1TF";;
esac esac
case "$opt_batch_format" in case "$opt_batch_format" in
@ -3180,6 +3222,36 @@ check_variant4()
fi fi
} }
check_variantl1tf()
{
_info "\033[1;34mCVE-2018-3615/3620/3646 [L1 terminal fault] aka 'Foreshadow & Foreshadow-NG'\033[0m"
status=UNK
sys_interface_available=0
msg=''
if sys_interface_check "/sys/devices/system/cpu/vulnerabilities/l1tf"; then
# this kernel has the /sys interface, trust it over everything
sys_interface_available=1
fi
if [ "$opt_sysfs_only" != 1 ]; then
:
elif [ "$sys_interface_available" = 0 ]; then
# we have no sysfs but were asked to use it only!
msg="/sys vulnerability interface use forced, but it's not available!"
status=UNK
fi
cve='CVE-2018-3615/3620/3646'
if ! is_cpu_vulnerable l1tf; then
# override status & msg in case CPU is not vulnerable after all
pvulnstatus $cve OK "your CPU vendor reported your CPU model as not vulnerable"
elif [ -z "$msg" ]; then
pvulnstatus $cve VULN "your CPU is known to be vulnerable, and your kernel doesn't report that it mitigates the issue, but more thorough mitigation checking by this script is being worked on (check often for new versions!)"
else
pvulnstatus $cve "$status" "$msg"
fi
}
if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then if [ "$opt_no_hw" = 0 ] && [ -z "$opt_arch_prefix" ]; then
check_cpu check_cpu
check_cpu_vulnerabilities check_cpu_vulnerabilities
@ -3207,6 +3279,10 @@ if [ "$opt_variant4" = 1 ] || [ "$opt_allvariants" = 1 ]; then
check_variant4 check_variant4
_info _info
fi fi
if [ "$opt_variantl1tf" = 1 ] || [ "$opt_allvariants" = 1 ]; then
check_variantl1tf
_info
fi
_vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|') _vars=$(set | grep -Ev '^[A-Z_[:space:]]' | sort | tr "\n" '|')
_debug "variables at end of script: $_vars" _debug "variables at end of script: $_vars"