Publicado el 4 de Septiembre del 2018
1.534 visualizaciones desde el 4 de Septiembre del 2018
357,0 KB
29 paginas
Creado hace 7a (13/02/2018)
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Administración de memoria: Memoria y seguridad
Gunnar Wolf
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Índice
1
Introducción
2 Organización de memoria: El stack
3 De una falla a un ataque
4 Mitigación
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
¿Seguridad?
En un sistema de cómputo, no podemos asumir la mejor
intención
Un sistema puede tener distintos usuarios, el sistema protege a
los datos de unos frente a otros
Un usuario puede intentar modificar cómo opera determinado
programa y generar salida que aparente ser legítima
Privilegios parciales/temporales: Bit SUID (a cubrirlo
posteriormente, en sistemas de archivos), capacidades
La memoria es el principal vector de ataque
Todos los datos deben pasar por memoria
. . . Y el procesador no puede entenderla, sólo obedecerla
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
¿No son suficiente los permisos?
La memoria segmentada y paginada ya incluyen los conceptos
de permisos
Desde hace muchos años, los segmentos de texto no admiten
escritura
¿Qué más puede ocurrir?
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Lidiando con información externa
Tal vez no tendríamos que preocuparnos de mucho. . . Pero
tenemos que lidiar frecuentemente con información externa
Información que puede romper nuestras expectativas
Información que no siempre viene de fuentes confiables
Puede ser maliciosa, o presentar errores (¡inocentes!) en el
comportamiento
Los mecanismos principales de manejo de estructuras de
memoria son bastante ingenuos → inseguros
Cualquier error en el manejo de memoria es una vulnerabilidad
en potencia
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Referencias bibliográficas
Para comprender mejor este tema, y para profundizar en él, sugiero
fuertemente:
Smashing The Stack For Fun And Profit (Aleph One, revista
Phrack, 1996)
The Tao of Buffer Overflows (Enrique Sánchez, inédito)
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Índice
1
Introducción
2 Organización de memoria: El stack
3 De una falla a un ataque
4 Mitigación
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Vuelta a la organización de la memoria
Pila de llamadas
Espacio de libres
Sección de datos
Sección de texto
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Vuelta a la organización de la memoria
Pila de llamadas
Espacio de libres
Sección de datos
Sección de texto
Vamos a centrarnos en el
stack (pila de llamadas):
¿Para qué sirve?
¿Cómo se estructura?
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Enfocándonos a la pila de llamadas
¿Cómo se ve una llamada a función ya compilada?
Empleamos dos valores principales, en registros:
Stack Pointer (SP) Apunta al final actual (dirección inferior)
de la pila. En arquitecturas x86, emplea el
registro ESP.
Frame Pointer (FP) Apunta al inicio (dirección superior) del
marco del stack. Algunos textos le llaman base
local (LB), y en arquitecturas x86, emplea el
registro EBP.
Guarda la instrucción de retorno (instruction pointer)
Reserva espacio para los datos locales a la función
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Comparando llamada en C y ensamblador x86-32
void func(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}
void main() {
func(1,2,3);
}
; main
func:
pushl $3
pushl $2
pushl $1
call func
pushl %ebp
movl %esp, %ebp
subl $20, %esp
Figura: Marco del stack con llamada a func(1,2,3) en x86-32
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
¿Qué hizo el código compilado?
Los tres primeros pushl: Debajo de la pila, agrega los tres
argumentos de la función
Los valores literales 1, 2, 3
4 bytes para cada uno (sizeof(int()) en 32 bits)
call: Agrega ret debajo de la pila, y brinca a la ubicación
de la etiqueta func
Al compilar, se pierde la noción de bloques de función
Todo son llamadas por dirección que retienen la dirección de
retorno
Ya en func, grabamos ebp (el frame pointer), le llamamos
sfp (Saved Frame Pointer)
movl graba la dirección del apuntador actual al stack en el
registro esp, y acto seguido, se le restan 20 bytes (por los 2
buffers, alineados a 32 bits)
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
El eslabón más débil: Funciones de manejo de cadenas
El diseño del lenguaje C incluye soporte para cadenas a través
de su biblioteca estándar
Arreglos de caracteres (8bit), terminados por un \0 (NUL)
Presenta familias de funciones relacionadas para su manejo,
principalmente strcat, strcpy, printf, gets
Muchas de estas no toman en cuenta verificación de límites
Otras, no necesariamente incluyen al \0
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Entran en juego los datos suministrados por el usuario
¿Qué pasa cuando un usuario puede proporcionar datos para
que los maneje?
Veamos qué pasa con el siguiente código vulnerable
#include <stdio.h>
int main(int argc, char **argv) {
char buffer[256];
if(argc > 1) strcpy(buffer, argv[1]);
printf("Escribiste %s\n", buffer);
return 0;
}
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Entran en juego los datos suministrados por el usuario
¿Qué pasa cuando un usuario puede proporcionar datos para
que los maneje?
Veamos qué pasa con el siguiente código vulnerable
#include <stdio.h>
int main(int argc, char **argv) {
char buffer[256];
if(argc > 1) strcpy(buffer, argv[1]);
printf("Escribiste %s\n", buffer);
return 0;
}
¿Quién puede encontrar el punto débil?
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Copiado sin límites
Invocamos al programa vulnerable con un parámetro largo (pero
válido):
$ ./ejemplo1 ‘perl -e ’print "A" x 120’‘
Escribiste:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Copiado sin límites
Invocamos al programa vulnerable con un parámetro largo (pero
válido):
$ ./ejemplo1 ‘perl -e ’print "A" x 120’‘
Escribiste:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$
Invocamos al programa vulnerable con un parámetro demasiado
largo para el arreglo:
$ ./ejemplo1 ‘perl -e ’print "A" x 500’‘
Escribiste:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
$
Parecería que el sistema atrapó al error exitosamente.
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
Copiado sin límites
Invocamos al programa vulnerable con un parámetro largo (pero
válido):
$ ./ejemplo1 ‘perl -e ’print "A" x 120’‘
Escribiste:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$
Invocamos al programa vulnerable con un parámetro demasiado
largo para el arreglo:
$ ./ejemplo1 ‘perl -e ’print "A" x 500’‘
Escribiste:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
$
Parecería que el sistema atrapó al error exitosamente. ¿O no?
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducción
Organización de memoria: El stack
De una falla a un ataque
Mitigación
¿Qué significa nuestro Segmentation fault?
La ejecución del programa fue interrumpida tras procesar un
argumento de 500 bytes para un arreglo medía sólo 256
. . . Pero esto no significa que el fallo haya sido atrapado
De hecho, no lo fue
Figura: Estado de la memoria después del strcpy()
Gunnar Wolf
Administración de memoria: Memoria y seguridad
Introducci
Comentarios de: Administración de memoria: Memoria y seguridad (0)
No hay comentarios