Hacking Memory
Introducción al Shellcode
Jeimy J. Cano, Ph.D, CFE
Universidad de los Andes
[email protected]
1
JCM-05 All rights reserved.
Agenda
Introducción
Conceptos Básicos
Administración de memoria
System Call
String format
Stack Overflows
Heap Overflows
Shellcode
Conceptos básicos
Esqueleto de un código shell
Ejemplos de Shellcodes
Ejemplo en Sistemas Windows
Ejemplo en Sistemas Linux
JCM-05 All rights reserved.
2
Agenda
Conceptos Avanzados de hacking memory
Kernel Overflows
Ingeniería Inversa
¿Técnicas Anti-reversing?
Reflexiones finales
Referencias
JCM-05 All rights reserved.
3
Introducción
El manejo de la memoria en las aplicaciones actuales, en el
entorno de ejecución, NO se hace de manera adecuada.
La manipulación de las zonas de memoria en la ejecución de
las aplicaciones genera efectos de borde inesperados
La memoria que manejan las aplicaciones es tan vulnerable
como el código mismo que las integra.
La memoria es un narrador invisible de la ejecución de las
aplicaciones
Reconocer la administración de memoria del SO, es un factor
crítico en el descubrimiento de vulnerabilidades de las
aplicaciones.
JCM-05 All rights reserved.
4
Conceptos Básicos
Administración de Memoria
Env pointer
argc
0xBFFFFFFF – High Address
Stack
Local variables,
function calls
LIFO Structure – Last In First Out
Almacenamiento por cortos periodos de tiempo
FIFO Structure – First In First Out
Almacenamiento por largos periodos de tiempo
Heap
Dynamic variables
BSS – Block Started by Symbol
Uninitialized Data
Data
Initialized Data
Text
Compiled program code
Librerías Compartidas
Segmento de donde es posible la Escritura
Segmento de donde es posible la Escritura
Reservado para variables
Globales
Segmento de Sólo Lectura
JCM-05 All rights reserved.
5
0x8000000 – Low Address
Conceptos Básicos
Registros
Env pointer
argc
0xBFFFFFFF – High Address
Stack
Local variables,
function calls
EIP
EBP
ESP
Function ()
Local variables
Heap
Dynamic variables
Function ()
In compiled code
Text
Compiled program code
BSS – Block Started by Symbol
Uninitialized Data
Data
Initialized Data
Librerías Compartidas
0x8000000 – Low Address
JCM-05 All rights reserved.
6
Conceptos Básicos
System Call
Es un conjunto de funciones que permiten a un tercero
acceso a funciones específicas del sistema operacional
como:
Obtener entradas
Producir salidas
Terminar procesos
Ejecutar binarios
Permiten acceso directo al Kernel, en el cual se tiene
acceso a funciones de bajo nivel.
Interfases entre modo kernel y el modo usuario
Están intimamente a funciones básicas como read, write,
fetch, primitivas del sistema operacional
En Linux – Interrupciones de Software – Int 0x80
JCM-05 All rights reserved.
7
Conceptos Básicos
String Format
Son cadenas alfanuméricas que contienen símbolos
especiales reconocidos por la función printf() y sus
derivadas (sprintf(), fprint(), vprintf()).
Con ellas es posible especificar la manera como serán
visualizados los demás argumentos entregados a la función.
Especificaciones de formato:
%d – presentado como número entero – Como valor
%u – presentado como número natural – Como valor
%x – presentado como número natural hexadecimal – Como valor
%s – presentado como cadena alfanumérica – Como referencia
%n – Número de caracteres impresos hasta el momento – Como
referencia.
JCM-05 All rights reserved.
8
Conceptos Básicos
String Format - 2
Ejemplo básico
0xBFFFFFFF – High Address
Env pointer
argc
Stack
Variables locales de la función
Que nos ha invocado
Int main() {
char *a = “Hola Mundo”;
Printf(“Mensaje a presentar: %s \n”, a);
}
Argumentos de la función
Printf()
Puntero a la variable a
Puntero a la cadena de formato
Dirección de retorno de la función
printf
JCM-05 All rights reserved.
0x8000000 – Low Address
9
Heap
Dynamic variables
Librerías Compartidas
Conceptos Básicos
String Format - 3
Utilizando especificaciones de formato
prog1
Int main(int argc, char **argv) {
char f[256];
Strcpy (f, argv[1]);
Printf(f);
}
1
$ ./prog1 ‘ %x-%x-%x'
Bffffc4f-0-0
-------------------------------------------------
2
$ ./prog1 ' %n %n %n %n '
Segmentation fault
Se le pasa como parámetro al programa, una instrucción en comillas inversas, lo que sugiere la ejecución del programa
1.
y lo que se encuentra entre comillas, tomando una palabra de 4 bytes de la pila (pues no había argumento), y así sucesivamente
2. El programa ha tratado de escribir a una dirección de memoria al azar la cantidad de caracteres impresos.
Tomado de: SOBOLEWSKI, P. y NIDECKI, T. (2004) Abusos de las cadenas de formato. Hakin9. No.5. Noviembre/Diciembre.
JCM-05 All rights reserved.
10
Stack Overflows
Características básicas
Generalmente se dan por manipulación o mal uso de
buffers
Un buffer es un conjunto contiguo de espacio de memoria.
Instrucciones de manejo del Stack
PUSH – Instrucción utilizada para colocar datos en el stack
POP – Instrucción para remover datos del Stack
ESP – Registro que define el límite del Stack, es decir
apunta a la parte superior del Stack
JCM-05 All rights reserved.
11
Stack Overflows
Ejemplo
Void return_input (int a, int b) {
char array [30];
gets (array);
printf (“ %s \n”, array)
}
main( ) {
return_input (1,2 );
return 0;
}
0xBFFFFFFF – High Address
Env pointer
argc
¡Dirección
inexistente o
protegida!
0x41414141
0x41414141
Stack
Variables locales de la función
Que nos ha invocado
b
a
AAAA
AAAA
Argumentos de la función
Printf()
Array
AAAAAAAAAAAA
AAAAAAAAAAAA
AAAAAAAAAAAA
JCM-05 All rights reserved.
0x8000000 – Low Address
Heap
Dynamic variables
Librerías Compartidas
RET
EBP
12
Heap Overflows
Características básicas
La zona de heap, es aquel lugar de memoria para manejo de
variables que permanecen por largo tiempo, las cuales
continuan existiendo aún la función haya retornado (y sus
variables hayan sido elimadas del stack)
Funciones que manejan este segmento – malloc () y free (),
los cuales se apoyan en brk() o mmap() (esta dos últimas
system call).
Consideraciones de funcionamiento
Cuando se usa malloc(), brk() asigna un bloque amplio de
memoria en pedazos, el cual entrega al usuario según su
solicitud.
Cuando se usa free(), decide sobre el nuevo pedazo de
memoria liberado, para armar un bloque mas grande
disponible.
JCM-05 All rights reserved.
13
Heap Overflows
Ejemplo
int main( int argc, char** argv) {
char *buf;
char *buf2;
buf = (char*)malloc(1024);
buf2= (char*)malloc(1024);
printf (“buf=%p buf2=%p\n”, buf, buf2);
strcpy (buf, argv[1]);
free(buf2)
}
$ ./prog1 AAAAAAA … (5000 veces)
malloc(1024)=0x080495b0
malloc(1024)=0x080499b8
printf(“buf=%p buf2=%p”,(…)buf= 0x080495b0
buf2=0x080499b8)=29
strcpy (0x080495b0, “AAAAA….” ) = 0x080495b0
free (0x080499b8) = <void>
Segmentation fault
Existen dos buffers contiguos defindos. El primero sobrepasa su tamaño y afecta al segundo. Particularmente sobre
1.
escribe la estructura de metadatos del segundo. Luego cuando se intenta liberar el segundo buffer posiblemente se esta
teniendo acceso a un segmento de memoria inválido.
JCM-05 All rights reserved.
KOZIOL, J., AITEL, D., LITCHFIELD, D., ANLEY, C., EREN, S., MEHTA, N. y HASSELL, R. (2004)
The shellcoder’s handbook: discovering and exploiting security holes. John Wiley & Sons
14
Shellcodes
Características básicas
Una pieza de código de máquina ejecutable o código script
que tiene una sola misión: abrir un intérprete de comandos
en el sistema objetivo.
Generalmente se crean en código ensamblador
Consideraciones de funcionamiento
Los shellcodes son generalmente utilizados para explotar
vulnerabilidades, basado en una técnica de vulneración
como son :
Stack buffer overflows
Heap buffer overflows
Formar string
Integer overflow
Memory corruption, etc.
Utilizan el contenido de la memoria (Payload)
JCM-05 All rights reserved.
15
Shellcodes
Esqueleto base de un código Shell
Obtener el EIP
Dirección base. Cualquier variable o función en el código shell será
relativa a esta dirección. Para obtener esta dirección utilizamos las
funciones CALL y POP.
Decodificar
Generalmente encontramos en memoria caracteres NULL, los cuales nos
impiden ejecutar nuestro código. Lo que implica que debamos codificar
nuestro código generalmente con XOR con un valor predifinido
Obtener las direcciones de las funciones requeridas
Identificar en memoria los API, para identificar la dirección de los
procesos que requerimos.
Configurar el Socket de conexión
Generalmente se requiere ubicar donde estan (en memoria) las funciones
de socket(), bind(), listen() y accept() o sus equivalentes.
Creación del shell
JCM-05 All rights reserved.
16
Shellcodes en Windows
Ejemplo
Definición de la dirección donde se encuentra el shellcode
Código que descifra el resto
Determinar la dirección de dónde se encuentra kernel32.dll
Determinar la dirección de la función GetProcAddress
Determinar las direcciones de otras funciones de la librería
Kernel32.dll
Carga de la librería ws2_32.dll y extracción de las direcciones
de la función Winsock
Se establece la conexión TCP con el sistema (proceso) del
Ejecución del intérprete de comandos con redirección de
entrada/salida de la conexión establecida
intruso
Parte
Cifrada
JCM-05 All rights reserved.
17
Shellcodes en linux
Ejemplo – Desbordami
Comentarios de: Hacking Memory - Introducción al Shellcode (0)
No hay comentarios