Mostrando entradas con la etiqueta Internals. Mostrar todas las entradas
Mostrando entradas con la etiqueta Internals. Mostrar todas las entradas

lunes, 22 de octubre de 2012

Linux x86: Adjacent Memory Overflows

Durante los días 18 y 19 de Octubre de 2012, tuve el placer de viajar y presentarme como disertante a la 8.8 Security Conference en la hermosa ciudad de Santiago de Chile.

Durante mi conferencia sobre "Buffer Overflow for fun and pr0fit", luego de la larga y aburrida introducción teórica hice algunas demostraciones prácticas de explotación de binarios, bypasseo de protecciones y "adjacent memory corruption explotation" en la cual esta última falló, quizá por la presión de hacerlo en vivo, nervios o lo que sea, falló. Entonces me gustaría a través de este post hacer un repaso de este último tópico que me hubiera encantado haberlo compartido en vivo durante mi charla.

sábado, 12 de mayo de 2012

POSIX: System-V Executable and Linkeable Format (ELF) and the Linux ABI

ELF son las siglas en inglés de Executable and Linkeable Format, y es el formato de archivo utilizado por los file objects (.o), binarios, librerías y coredumps en Linux y en Unix en general.
El principio de todo proceso, define al mismo como un archivo en un dispositivo de almacenamiento, que una vez compilado y al ser ejecutado, es cargado en memoria para que el scheduler (mediante el sistema operativo), le asigne tiempo de CPU y recursos a fin de poder ejecutar las rutinas definidas en el.
Este archivo, ya convertido en binario, se encuentra escrito en algunos de los cientos lenguajes de programación que existen actualmente, por ejemplo ANSI C o Fortran. Cuando fue compilado, el compilador en un momento determinado ejecuto el linker, y definió en un header todas aquellas librerías que el programa necesita para poder ejecutarse y que estás les provean aquellas funciones y procedimientos que utilice.
Un binario, puede ser compilado de dos formas, una es estática y la otra dinámica. Compilar un binario de manera estática (parámetro -Bstatic en gcc(2)), significa que se incluirán en el mismo binario todas aquellas librerías que el programa requiera. Por su parte, la principal idea de las librerías compartidas, es tener una sola copia de las mismas instaladas en el sistema operativo y que todo aquel programa que las necesite, haga uso de ellas, y las cargue en memoria para ejecución, en segmentos que pueden ser mapeados de manera privada para el proceso, o compartido entre varios procesos/threads.

Los procesos en Unix, nacen de alguna de las variantes de la syscall fork(2). fork(2), bifurca el proceso padre en una nueva imagen del proceso (una nueva entrada en la estructura proc_t), y mediante exec(2) desplaza esta imagen para crear el mapeo y la estructuras en memoria para el nuevo proceso ejecutado.
Una vez realizado el exec del proceso, y si este está compilado de manera dinámica, en invocado el runtime linker, en este caso ld.so.1,

sábado, 5 de mayo de 2012

POSIX: INT_MAX, INT_MIN and SIGFPE

En sistemas operativos modernos, los límites de direccionamiento de memoria y cantidad de procesos/threads está estipulada de dos formas: la primera acorde al hardware específico con que cuenta el equipo, y la segunda, mediante configuración específica del sistema operativo.

A modo de ejemplo, y para dejar las cosas más en claro, vamos a hablar un poco sobre los límites de los procesos: Cada proceso en el sistema operativo ocupa un slot en una estructura de datos denominada kernel process table. Esta tabla contiene toda la información necesaria que el kernel mantiene a fin de poder manejar los procesos, schedulear las distintas tareas (acorde a sus prioridades) y estados de los mismos. Cuando el OS bootea, el kernel inicializa una estructura de datos denominada process_cache, donde comenzará a direccionar la estructura de procesos que luego serán empleada por el kernel y el userspace. Esta tabla es una lista doblemente enlazada con dos punteros, uno a su elemento anterior y otro a su elemento posterior, la cual tiene una capacidad máxima que se define acorde a la cantidad de memoria física instalada en el equipo. El sistema operativo, para esto asigna la cantidad de memoria instalada a una variable MAX_MAXUSERS (que no tiene nada que ver con el número máximo de usuarios que el sistema soporta), que luego será utilizada por dos variables más del propio kernel max_nprocs y maxuprc. max_nprocs define el número máximo de procesos del sistema operativo, y maxuprc determina el número máximo de procesos que usuarios no privilegiados (que no sean root), puedan crear y ocupar slots en la kernel process table. Y basándose en el valor de MAX_MAXUSERS, poder definir el tamaño en memoria de cada segmento del proceso (región .text, .stack, .heap, .bss, etc.).