Slide 1

Slide 1 text

Timing Attacks @w3af / @_topo

Slide 2

Slide 2 text

Timing Side Channels ● Enviamos el mensaje M1 a un proceso y devuelve la respuesta R en 3ms ● Enviamos el mensaje M2 al mismo proceso, devuelve nuevamente R pero esta vez tarda 6ms ● Los resultados son consistentes para sucesivas mediciones del tiempo de procesamiento para M1 y M2. ● Encontramos un side channel!

Slide 3

Slide 3 text

Timing Side Channels ● Si prestaron atención mientras estaban haciendo un análisis de seguridad de una web application seguramente notaron un timing side channel en: ○ Login: El hash (bcrypt, pbkdf2) se calcula unicamente cuando el usuario es valido. ○ Múltiples queries SQL (secuenciales) son ejecutadas cuando envio M1, pero no cuando envio M2. ● Side channels de unos pocos milisegundos o menos son muy comunes, no siempre explotables, e imposibles de detectar “a ojo”.

Slide 4

Slide 4 text

Tipos de Timing Attacks ● String compare ○ La diferencia de tiempo se genera al momento de comparar dos strings y depende de la cantidad de bytes en común desde el inicio ● Application logic ○ La diferencia en el tiempo de respuesta está dada por la lógica de la aplicación. def login(username, password): if not self.user_exists(username): return False return self.get_user_hash(username).check(password) if user_input.get('API_KEY') == MASTER_API_KEY: allow()

Slide 5

Slide 5 text

Timing Attack on PIN-Protected Hard Drive https://www.youtube.com/watch?v=p0AuTPmFjTY

Slide 6

Slide 6 text

Simple (unreal?!) test-case. Password input from serial compared with hardcoded value. ● Microcontroller Atmega328P ● Board Arduino UNO ● Compiler AVR-GCC ● AVR LibC * * http://download.savannah.gnu.org/releases/avr-libc/avr-libc-2.0.0.tar.bz2 Timing Attack on Embedded - Test Atmega328P

Slide 7

Slide 7 text

Timing Attack on Embedded - Test Atmega328P

Slide 8

Slide 8 text

Timing Attack Embedded - Test Atmega328P Optimization : None Clock : 16 MHz Invalid Character index ● None ● 5th ● 2nd ● 1st

Slide 9

Slide 9 text

Timing Attack on Embedded - Test Atmega328P Optimization : Speed Clock : 16 MHz Invalid Character index ● None ● 5th ● 2nd ● 1st

Slide 10

Slide 10 text

Timing Attack on Embedded - Other Systems ● Clock Drift (affects Hardware Random Number Generator) ○ Temperature ○ Clock Glitching (Using CPLD & FPGA) ● Side-Channel Attacks (off-topic) ○ Power Analysis (Simple, Differential, etc.) ○ Voltage Glitching ○ Electromagnetic Radiation ○ Others

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

String Compare Timing Attacks Funcion de comparacion de strings con timing side channel: ● timeit(compare_strings('bbb', 'aaa')) > timeit(compare_strings('b', 'aaa')) ● timeit(compare_strings('aaa', 'aaa')) > timeit(compare_strings('abb', 'aaa')) def compare_strings(str_a, str_b): if len(str_a) != len(str_b): return False for i, char_a in enumerate(str_a): if str_b[i] != char_a: return False return True

Slide 13

Slide 13 text

String Compare Timing Attacks Entonces, en un caso ideal, es posible explotar este side channel y realizar un ataque de fuerza bruta byte-per-byte sobre el secreto. Se envían peticiones HTTP a la aplicación con API KEYs con tamaño incremental hasta que se nota un “salto” en el tiempo que toma comparar la key enviada con MASTER_API_KEY. if compare_strings(user_input.get('API_KEY'), MASTER_API_KEY): allow()

Slide 14

Slide 14 text

String Compare Timing Attacks Luego se realiza una fuerza bruta byte-per-byte: ● AXXXXXXXXX ● BXXXXXXXXX ● CXXXXXXXXX Donde el primer byte es el que quiero descubrir y el resto es padding. Continuo probando hasta que encuentro un resultado para el cual la aplicación tarda más tiempo (compara un byte más). if compare_strings(user_input.get('API_KEY'), MASTER_API_KEY): allow()

Slide 15

Slide 15 text

Local Jitter Hay muchas fuentes de ruido que no permiten medir el tiempo de ejecución con la precisión necesaria para explotar todos los timing side channels. Fuentes de ruido en la computadora desde donde se realiza la medición: 1. Context switching 2. CPU load 3. Power save 4. WiFi (no!) 5. Conexión a Internet local (switches, routers, picos de tráfico, ISP QoS) 6. Cable => Network card Ring Buffer => IRQ => Kernel => Timestamp con precisión de milisegundos

Slide 16

Slide 16 text

Remote Jitter ● “Internet” agrega mucho ruido a las mediciones ● Fuentes de ruido en el servidor vulnerable: 1. Context switching 2. CPU load 3. Virtualización 4. SDN

Slide 17

Slide 17 text

Remote Jitter

Slide 18

Slide 18 text

jitter, die! Para reducir el ruido en nuestra computadora: ● Context switching y CPU load se soluciona utilizando isolcpus ● Se deshabilita power save ● Conectarse con un cable de red a Internet. Reducir lo más posible la cantidad de equipos de red entre el host e Internet. ● pkill -f torrent ● Compramos una placa de red que estampe tiempos en cada paquete con precisión de nanosegundos (Intel i350)

Slide 19

Slide 19 text

CPU load remoto Un problema común es que durante unos segundos el equipo remoto se encuentre con mucha carga. Si medimos un evento durante ese periodo, el resultado se verá afectado. Para reducir el efecto que tiene el CPU load remoto en nuestras mediciones lo que se hace es medir de manera entrelazada y tomar como valor de la medición la diferencia entre dos mediciones consecutivas. for _ in xrange(SAMPLES): data_point = timeit('AXXXX') - timeit('BXXXX') save(data_point)

Slide 20

Slide 20 text

Kill jitter using Si la aplicación objetivo se encuentra en un cloud público (AWS, DigitalOcean) es posible reducir el jitter agregado por Internet utilizando como host para realizar las mediciones un servidor en el mismo data-center que el objetivo. En algunos casos podría hasta ser posible obtener una instancia en el mismo hardware donde se ejecuta el target: “Hey, You, Get Off of My Cloud”.

Slide 21

Slide 21 text

Tiempos muy pequeños Aun con funciones muy naive (más sobre esto luego) de comparación de strings como: Los tiempos a medir son muy pequeños y la precisión con la cual es posible medirlos es reducida. def compare_strings(str_a, str_b): if len(str_a) != len(str_b): return False for i, char_a in enumerate(str_a): if str_b[i] != char_a: return False return True

Slide 22

Slide 22 text

Medir nanosegundos sobre Internet Datos obtenidos durante el research de time trial (en nanosegundos):

Slide 23

Slide 23 text

Incrementar el tiempo a medir Como los tiempos a medir suelen ser muy pequeños, se puede aplicar una técnica conocida como “mathematical amplification” (bleh!) para incrementar el tiempo a medir. No es perfecto porque incrementa la cantidad de peticiones HTTP a realizar , pero incrementa sustancialmente el intervalo de tiempo a medir. En este caso el brute-force se hace de a tres bytes: timeit('AAAXX') vs timeit('AABXXXX') ... vs timeit('ZZZXXXX')

Slide 24

Slide 24 text

Histograma Una solución para los problemas de jitter es realizar cientos de miles de mediciones, entonces se obtiene una distribución:

Slide 25

Slide 25 text

Comparación de distribuciones

Slide 26

Slide 26 text

Comparación de distribuciones

Slide 27

Slide 27 text

Comparación de distribuciones

Slide 28

Slide 28 text

Cientos de miles de mediciones La comparación de estas distribuciones no es trivial, en general hay que tener en cuenta los siguientes componentes: ● Filtros para eliminar outliers de una distribución, se suele utilizar únicamente el rango de datos menor al N% con N: {5, 10, 15, 20} ● Métodos de comparación de distribuciones: ○ Box test ○ Midsummary ○ Expectation-Maximization ● Certeza del resultado

Slide 29

Slide 29 text

It can get worse

Slide 30

Slide 30 text

Comparación de Strings en Ruby, CPython y PHP Todos los lenguajes de programación desarrollados en C terminan utilizando memcmp() de glibc para implementar la comparación de strings. memcmp() está optimizado para que las comparaciones de dos strings sean lo más rápidas posibles, si están disponibles se utilizan instrucciones de SSE para comparar secciones de memoria de 8 o 16 bytes en una sola instrucción de procesador. Entonces, no es posible realizar ataques de fuerza bruta byte-per-byte (*) en targets que utilicen glibc y sea posible utilizar alguna instrucción del CPU que compare strings en bloques.

Slide 31

Slide 31 text

● 34 implementaciones específicas de memcmp() ● Incluye implementaciones con soporte de wide-char (terminan incluyendo las no-wc). ● Lo mismo sucede contras funciones de glibc que se utilizan mucho y requieren buena performance. GLibC memcmp()

Slide 32

Slide 32 text

● Glibc decide a que implementacion utilizar en tiempo de ejecución. GLibC memcmp()

Slide 33

Slide 33 text

GLibC memcmp() optimizado SSE 2 - X86_64

Slide 34

Slide 34 text

● glibc-2.23/sysdeps/x86_64/multiarch/me mcmp-sse4.S ● Utiliza instrucciones para comparar de a 128bits. ● Son 1776 líneas de assembler de puro amor! ● Algún hijo de puta se fue a su casa contento después de esto. GLibC memcmp() optimizado SSE 4.1 - X86_64

Slide 35

Slide 35 text

GLibC memcmp() optimizado SSE 4.1 - X86_64

Slide 36

Slide 36 text

Comparación de Strings en Java public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; Naive, pero… después las optimizaciones de la JVM deciden que es mejor ejecutar la versión optimizada de String.equals, la cual tiene optimizaciones similares a memcmp()

Slide 37

Slide 37 text

Got worse.

Slide 38

Slide 38 text

simplify

Slide 39

Slide 39 text

String Compare Timing Attacks sobre IoT Cuando el problema es complejo, lo mejor es simplificarlo, y eso es lo que hizo Paul McMillan en el research que presentó hace unos años en Ekoparty donde utilizo timing attacks para obtener byte a byte la API key de una lampara HUE de Phillips. Lo que logró al atacar lámparas HUE fue: ● CPU más lento, mayor tiempo en ejecutar cada instrucción ● Comparacion naive de strings byte per byte ● Conexión por medio de un cable de red y utilizando Intel i350

Slide 40

Slide 40 text

simplify.ar

Slide 41

Slide 41 text

Mediciones Locales / Ruby 2.3.0 for loop

Slide 42

Slide 42 text

Con código similar en Python obtuve spent_time == 0 start_time = Time.now.nsec # Compare the test strings str_a == str_b end_time = Time.now.nsec spent_time = end_time - start_time if spent_time < 0 puts "Go home Ruby, you're drunk. Negative time spent: #{spent_time}" next

Slide 43

Slide 43 text

Comparación de Strings CPython Extrañamente la comparación de strings en CPython no utiliza memcmp() en todos los casos: switch(kind1) { case PyUnicode_1BYTE_KIND: { switch(kind2) { case PyUnicode_1BYTE_KIND: { int cmp = memcmp(data1, data2, len); ... } case PyUnicode_2BYTE_KIND: COMPARE(Py_UCS1, Py_UCS2); break; case PyUnicode_4BYTE_KIND: COMPARE(Py_UCS1, Py_UCS4); break;

Slide 44

Slide 44 text

Comparación de Strings CPython / COMPARE Donde COMPARE es naive: #define COMPARE(TYPE1, TYPE2) \ do { \ TYPE1* p1 = (TYPE1 *)data1; \ TYPE2* p2 = (TYPE2 *)data2; \ TYPE1* end = p1 + len; \ Py_UCS4 c1, c2; \ for (; p1 != end; p1++, p2++) { \ c1 = *p1; \ c2 = *p2; \ if (c1 != c2) \ return (c1 < c2) ? -1 : 1; \ } \ } \ while (0)

Slide 45

Slide 45 text

Comparación de Strings CPython / COMPARE Entonces la comparación de strings con diferentes byte sizes se realiza utilizando una función con un per-byte side channel: Me imagino que es posible identificar frameworks de desarrollo web python que permitan explotar esta debilidad especificando Content-Encoding en los requests. CHAR_OK = b'A'.decode('ascii') CHAR_FAIL = 'á' assert len(CHAR_OK.encode('utf-8')) != len(CHAR_FAIL.encode('utf-8')) CHAR_OK == CHAR_FAIL # naive strcmp

Slide 46

Slide 46 text

java -Djava.compiler=NONE

Slide 47

Slide 47 text

Objetivos ● Completar las mediciones locales de tiempos para todos los lenguajes, investigar y utilizar técnicas necesarias para reducir jitter ● Definir múltiples entornos de prueba y capturar millones de muestras ● Probar todas las combinaciones de filtros, métodos de comparación de distribuciones y precisión para determinar cuál funciona mejor para cada entorno. ● Desarrollar una aplicación que pueda atacar IoT de manera automática ● Mejorar la aplicación anterior para que pueda ser utilizada contra aplicaciones reales

Slide 48

Slide 48 text

Los repositorios en los cuales estoy trabajando son públicos: ● https://github.com/andresriancho/pico ● https://github.com/andresriancho/pico-string-compare-loc al No dejen de leer las wikis de esos dos repositorios, contienen información interesante sobre el research, links a otras herramientas, etc. Help wanted

Slide 49

Slide 49 text

No content