Shellcode
En informática, un shellcode es un programa de lenguaje ensamblador que tradicionalmente ejecuta un shell , como el shell de Unix '/bin/sh' o el shell de command.com en los sistemas operativos DOS y Microsoft Windows . Un shellcode se puede utilizar para explotar un error a través de un exploit , lo que permite que un pirata informático o un cracker obtenga acceso a la línea de comandos de una computadora , o más generalmente para ejecutar código arbitrario .
Descripción
Tipos de Shellcode
Hay dos tipos diferentes de shellcode, local y remoto. La distinción depende del tipo de control que proporciona la ejecución del shellcode en la máquina de destino, que puede ser local o remota (si ocurre a través de la red).
locales
Un shellcode local es utilizado por un atacante que tiene acceso limitado a la máquina, pero que aprovechando una vulnerabilidad en un proceso con mayores privilegios, por ejemplo, un desbordamiento de búfer, puede obtener los mismos privilegios si la ejecución del shellcode es exitosa.
Remoto
El shellcode remoto se usa en su lugar cuando un atacante quiere explotar una vulnerabilidad de un proceso de otra máquina en la red local o en una intranet. Si Shellcode se ejecuta correctamente, devuelve el control de la máquina de destino a través de la red. Los shellcodes remotos normalmente usan el socket TCP/IP estándar para permitir el acceso al shell de la máquina de destino. Otras distinciones se pueden clasificar según el método por el cual se establece la conexión. Si es el propio shellcode el que puede establecer la conexión, esto se denomina "shell inverso" o shellcode de conexión posterior , porque el shellcode que se ejecuta en la máquina remota se conecta a la máquina del atacante. Si, por otro lado, el atacante necesita crear la conexión, el shellcode se llama bindshell , ya que el shellcode se vincula a un determinado puerto, que será utilizado por el atacante para conectarse y controlar la máquina de destino. Un tercer tipo de shellcode menos común es el shellcode de reutilización de sockets . Este tipo de shellcode generalmente se usa cuando un exploit establece una conexión con el proceso vulnerable que no se cierra antes de que se ejecute el shellcode. El shellcode puede reutilizar esta conexión para comunicarse con el atacante. El shellcode de reutilización de socket es más complejo de implementar, porque el shellcode debe identificar qué conexión puede usar (entre las posibles abiertas en la máquina). [1] Se puede usar un firewall para identificar las conexiones salientes realizadas por un shellcode de conexión posterior y el intento de conexión entrante de un bindshell. El cortafuegos puede proporcionar protección adicional contra un atacante, incluso si el sistema es vulnerable, impidiendo de forma preventiva el acceso al shell creado al ejecutar el código de shell. Esta es una de las razones por las que a veces se usa un shellcode que reutiliza un socket, porque no crear nuevas conexiones es más difícil de identificar y bloquear.
Descargar y ejecutar
Descargar y ejecutar es un tipo de shellcode remoto que realiza una descarga y ejecuta algún tipo de malware en el sistema de destino. Este tipo de shellcode no crea un shell, sino que le indica a la máquina que descargue un determinado archivo ejecutable de la red, lo guarde en el disco y luego lo ejecute. Hoy en día, se usa comúnmente en ataques de descargas ocultas , cuando una víctima visita un sitio malicioso que intenta iniciar una descarga y ejecutar un shellcode para instalar software en la máquina de la víctima. Una variación de este tipo de shellcode es "descargar y cargar una biblioteca". [2] [3] Las ventajas de esta técnica son que el código de shellcode puede ser más pequeño, no requiere la creación de un nuevo proceso en la máquina de destino y que el shellcode no necesita implementar el código para limpiar el objetivo. process , pero esto se puede hacer desde una biblioteca cargada en el proceso.
Escenificado
Cuando la cantidad de datos que un atacante puede inyectar en un proceso de destino es demasiado pequeña para que el shellcode se ejecute correctamente, se puede ejecutar por etapas. Primero, se ejecuta una pequeña porción de shellcode (etapa 1). Este código vuelca una parte más grande del shellcode (fase 2) en la memoria del proceso y lo ejecuta.
Búsqueda de huevos
Egg-hunt es otro tipo de shellcode en fase. Se utiliza cuando un atacante tiene la capacidad de insertar un shellcode grande en un proceso, pero no puede determinar en qué lugar de la memoria se colocará. Luego, se inserta una pequeña búsqueda de huevos en el proceso en una ubicación predecible y luego se ejecuta. El código busca en el espacio de la memoria un shellcode más grande (el huevo) y lo ejecuta.
Tortilla
Este tipo de shellcode es similar a la búsqueda de huevos, pero busca bloques más pequeños (huevos) y los recombina en uno más grande (tortilla) que luego se ejecuta. Esta técnica se utiliza cuando un atacante se limita, por alguna razón, a insertar pequeños bloques de datos en el proceso. [4]
Operación
Estrategia de ejecución de Shellcode
Un exploit comúnmente inserta un shellcode en el proceso de destino antes o al mismo tiempo que ocurre un exploit de vulnerabilidad, para obtener control sobre el contador del programa . El contador del programa está dirigido a señalar el shellcode que se ejecutará. La inyección del shellcode a menudo se realiza almacenando el código en los datos enviados a través de la red al proceso vulnerable, haciéndolo disponible en un archivo que lee el proceso o a través de la línea de comandos o variables de entorno en el caso de exploits locales.
Codificación Shellcode
Dado que muchos procesos filtran o limitan los datos que se pueden ingresar, a menudo se debe escribir el shellcode para superar estas restricciones, haciendo que el código sea pequeño, libre de nulos o caracteres alfanuméricos. Se han encontrado varias soluciones para eludir estas restricciones:
- Optimizaciones de diseño e implementación para reducir el tamaño del shellcode.
- Cambios de implementación para sortear las limitaciones en el rango de bytes utilizados en el shellcode.
- Código automodificable que modifica el número de bytes de su código antes de ejecutarlos, con el fin de recrear bytes que normalmente no se pueden insertar en el proceso.
Dado que las herramientas de detección de intrusos pueden identificar la firma de shellcodes simples enviados a través de la red, se codifican y se autodescifran o polimórficos para evitar ser reconocidos.
Codificación porcentual
Los exploits dirigidos a los navegadores comúnmente codifican el shellcode en una cadena de JavaScript usando notación de codificación de porcentaje o codificación de URL, escapando "\ uXXXX" o usando la entidad . Algunos exploits ofuscan aún más el shellcode codificado con cadenas para evitar que las herramientas IDS los detecten . Por ejemplo, en una arquitectura IA-32 , dos instrucciones (sin operación) NOPtienen esta forma antes de codificarse.
90 NOP 90 NOP
Están codificados en una cadena con codificación porcentual. (usando el método unescape () para decodificar)
unescape ("% u9090");
Luego se codifica en la notación "\ uXXXX":
"\ u9090";
Y finalmente en la codificación de entidades.
"& # x9090;"
o
"& # 37008;"
Shellcode libre de nulos
Muchos códigos shell se escriben sin usar bytes nulos , porque están diseñados para insertarse en el proceso de destino a través de una cadena terminada en nulo. Cuando se copia una cadena terminada en nulo, la copia incluirá el primer carácter nulo , pero los bytes siguientes no se procesarán. Cuando el código de shell que contiene el nulo se ingresa de esta manera, solo se insertará una parte del código de shell, por lo que no se podrá ejecutar más tarde. Para producir un shellcode libre de nulos que comience con uno que contenga bytes nulos, las instrucciones de máquina que contienen ceros se pueden reemplazar con instrucciones que produzcan el mismo efecto pero que no tengan bytes nulos. Por ejemplo en una arquitectura IA-32 se podría realizar este reemplazo:
B8 01000000 MOV EAX, 1 // Establece el registro EAX en 0x000000001
esta declaración contiene ceros como parte del literal (1 se expande como 0x000000001) con estas declaraciones:
33C0 XOR EAX, EAX // Establecer el registro EAX en 0x000000000 40 INC EAX // Aumenta el valor de EAX a 0x00000001
que tienen el mismo efecto pero requieren menos bytes para la codificación y están libres de bytes nulos.
Shellcodes alfanuméricos e imprimibles
En determinadas circunstancias, un proceso de destino puede filtrar todos los bytes del shellcode ingresado que no son imprimibles o alfanuméricos. En estas condiciones, el rango de instrucciones que se pueden usar para escribir un shellcode se vuelve muy limitado. Una solución a este problema ha sido publicada por Rix en Phrack 57 [5] donde se muestra cómo es posible convertir cualquier tipo de código en uno alfanumérico. Una técnica muy utilizada es la creación de código automodificable, ya que permite que el código modifique sus propios bytes para incluir otros que no están incluidos entre los permitidos y ampliar el rango de instrucciones utilizables. Con este tipo de truco, inicialmente se puede crear un decodificador automodificable usando solo bytes dentro del rango permitido. Cuando el shellcode saliente entra en ejecución, el decodificador puede modificar su código para poder usar cualquier instrucción requerida para permitir que funcione correctamente y al mismo tiempo continuar decodificando el shellcode original. Después de realizar la decodificación, el decodificador transfiere el control al shellcode, para que pueda ejecutarse normalmente. Se ha demostrado cómo es posible crear shellcodes de complejidad arbitraria que se asemejan al texto normal en inglés. [6]
Shellcode a prueba de caracteres Unicode
Muchos programas modernos utilizan la codificación de cadenas en formato Unicode para permitir la internalización del texto. A menudo, estos programas convierten las cadenas ASCII de entrada antes de procesarlas. Las cadenas Unicode codificadas en UTF-16 usan dos bytes para decodificar cada carácter (o cuatro bytes para algunos caracteres especiales). Cuando una cadena ASCII se convierte a UTF-16, se inserta un byte cero después de cada byte de la cadena original. Obscu mostró en Phrack 61 [7] que es posible escribir shellcodes que pueden ejecutarse correctamente incluso después de esta transformación. Hay programas que pueden cambiar automáticamente cada shellcode en uno codificado por UTF-16 y se basan en el mismo principio que un decodificador automodificable que decodifica el shellcode original.
Plataformas
Muchos códigos de shell están escritos en código de máquina debido al bajo nivel en el que la vulnerabilidad se vuelve explotable. El shellcode a menudo se crea para atacar una combinación específica de procesador, sistema operativo y paquete de servicio, que comúnmente se conoce como plataforma. Para algunos exploits, debido a las restricciones impuestas por el proceso de destino, es necesario crear un shellcode específico. Sin embargo, no siempre es posible que un shellocde funcione para múltiples exploits, service packs, sistemas operativos y posiblemente procesadores. [8] Se puede dar versatilidad creando diferentes versiones del shellcode, basadas en las diversas plataformas a atacar y creando un encabezado que identifique la versión correcta para la plataforma en uso. Cuando se ejecuta, el código se comporta de manera diferente según la plataforma y es capaz de ejecutar la versión correcta del shellcode.
Notas
- ^ BHA, Shellcode/Socket-reuse , en blackhatlibrary.net , 6 de junio de 2013. Consultado el 7 de junio de 2013 .
- ^ SkyLined, Download y LoadLibrary shellcode publicados , en skypher.com , 11 de enero de 2010. Consultado el 19 de enero de 2010 (archivado desde el original el 23 de enero de 2010) .
- ^ SkyLined, Download and LoadLibrary shellcode para Windows x86 , en code.google.com , 11 de enero de 2010. Consultado el 19 de enero de 2010 .
- ^ SkyLined, w32 SEH omelet shellcode , en skypher.com , 16 de marzo de 2009. Consultado el 19 de marzo de 2009 (archivado desde el original el 23 de marzo de 2009) .
- ^ Rix, Writing ia32 alphanumeric shellcodes , phrack.org , Phrack, 8 de noviembre de 2001. Consultado el 29 de febrero de 2008 .
- ^ Joshua Mason, Small, Sam, Monrose, Fabian y MacManus, Greg, English Shellcode ( PDF ), cs.jhu.edu , noviembre de 2009. Consultado el 10 de enero de 2010 .
- ^ Obscou, Building IA32 'Unicode-Proof' Shellcodes , phrack.org , Phrack, 13 de agosto de 2003. Consultado el 29 de febrero de 2008 .
- ^ Eugene, Architecture Spanning Shellcode , phrack.org , Phrack, 11 de agosto de 2001. Consultado el 29 de febrero de 2008 .
Artículos relacionados
- Desbordamiento de búfer
- desbordamiento de montón
- seguridad informatica
- Asamblea
- código auto modificable
- sistema de detección de intrusos
Enlaces externos
- Shell-Storm.org Base de datos de shellcodes multiplataforma.
- https://web.archive.org/web/20080302111910/http://www.metasploit.com/shellcode/ Contiene ejemplos de shellcode x86 y no x86 y una interfaz en línea para la generación y codificación automáticas de shellcode.
- http://www.vividmachines.com/shellcode/shellcode.html Tutorial sobre shellcodes en Windows y Linux con ejemplos paso a paso.
- http://www.orkspace.net/software/libShellCode/ biblioteca de código abierto para la creación automática de ShellCode. Se puede usar para crear ShellCodes dinámicos dentro de un exploit o para crear ShellCodes estáticos mediante el uso de un front-end .