Buffer Overflow: De un DoS a un RCE

· · | Ciberseguridad en Buffer Overflow: De un DoS a un RCE

Conocemos el término DoS o denegación de servicio como una acción en la cual un servicio o software en general deja de funcionar por culpa de algún atacante o en su defecto, un usuario común que realiza una acción sin saber que esto puede afectar al servicio.

Un ejemplo simple de ello es ubicarnos en un caso hipotético en el cual, un programa recibe 2 números, para luego calcular la suma de ellos, y así devolver el resultado.

run-nice

Si este programa recibe, por ejemplo, una palabra como infinity y un numero como 13. El programa no va a saber cómo tratar estos datos de entrada, ya que no tiene idea de cómo sumar números con palabras.

run-bad

En esta situación, el programa sufre un error de ejecución por lo cual deja de funcionar y se cierra. Esto es lo que conocemos como Denegación de Servicio (DoS).

Existen muchas otras formas de denegar un servicio, una de ellas es realizando más solicitudes de las que un programa puede recibir al mismo tiempo, o excediendo un límite de solicitudes definido por el mismo programa.

Fujo de un programa

Todo programa tiene una función principal en donde inicia y sigue ciertas instrucciones.

Hay instrucciones las cuales llaman a funciones, que vendrían a ser como un sub-programa. Luego que el flujo del programa realiza todas las instrucciones de la función, debe retornar a la siguiente instrucción de la función principal, luego del llamado de la función. Esto lo podemos observar en el siguiente gráfico.

flujo-program

Para que este flujo se realice, debe de almacenarse, al entrar a la función, la información de a donde debe retornar el programa luego de ejecutar la función, a esta dirección se le llama dirección de retorno.

Esta dirección de retorno es primordial, puesto que si se corrompe de una u otra forma, el programa no va a completar el flujo correcto y puede estropearse, generando así una denegación de servicio (DoS).

bad-flujo

Buffer

Un buffer es un espacio de almacenamiento determinado que se define para guardar información en él. Por ejemplo, un buffer para almacenar el nombre de un usuario, con longitud 8.

buffer-nice

Si ingresamos otro nombre por ejemplo maximiliana. Este nombre tiene más de 8 caracteres, por ende, esto terminará excediendo la longitud del buffer y por lo tanto generará un desbordamiento del buffer.

buffer-bad

Buffer Overflow

Como vemos en la imagen anterior, el desbordamiento de buffer, permite que el texto ingresado, exceda la longitud del buffer y termine escribiendo mas allá del espacio reservado para el nombre.

De esta forma, en algunos casos, cuando el texto ingresado es muy largo, puede llegar a alcanzar el espacio de memoria donde se almacena la dirección de retorno. De esta manera, termina corrompiendo la dirección de retorno y estropeando el flujo del programa generando un ataque DoS.

Hasta este momento hemos visto que un desbordamiento de buffer puede generar una denegación de servicio, pero ahora nos preguntamos… ¿Se puede aprovechar el cambio de la dirección de retorno? La respuesta es .

Un atacante podría cambiar la dirección de retorno por una dirección de alguna otra función que no forma parte del flujo original del programa, como  por ejemplo, una función responsable de la asignación de permisos de usuario o gestión de estos.

malicius-flujo

Otra acción que podría realizar un atacante sería agregar instrucciones maliciosas en la memoria para que luego, al cambiar la dirección de retorno, el programa se dirija a estas instrucciones ejecutándolas. Este grupo de instrucciones maliciosas están escritas en lenguaje ensamblador y se les conoce como shellcode.

Un ejemplo de shellcode puede ser el siguiente:

char code[] = \
"\x89\xe5\x83\xec\x20\x31\xdb\x64\x8b\x5b\x30\x8b\x5b\x0c\x8b\x5b"
"\x1c\x8b\x1b\x8b\x1b\x8b\x43\x08\x89\x45\xfc\x8b\x58\x3c\x01\xc3"
"\x8b\x5b\x78\x01\xc3\x8b\x7b\x20\x01\xc7\x89\x7d\xf8\x8b\x4b\x24"
"\x01\xc1\x89\x4d\xf4\x8b\x53\x1c\x01\xc2\x89\x55\xf0\x8b\x53\x14"
"\x89\x55\xec\xeb\x32\x31\xc0\x8b\x55\xec\x8b\x7d\xf8\x8b\x75\x18"
"\x31\xc9\xfc\x8b\x3c\x87\x03\x7d\xfc\x66\x83\xc1\x08\xf3\xa6\x74"
"\x05\x40\x39\xd0\x72\xe4\x8b\x4d\xf4\x8b\x55\xf0\x66\x8b\x04\x41"
"\x8b\x04\x82\x03\x45\xfc\xc3\xba\x78\x78\x65\x63\xc1\xea\x08\x52"
"\x68\x57\x69\x6e\x45\x89\x65\x18\xe8\xb8\xff\xff\xff\x31\xc9\x51"
"\x68\x2e\x65\x78\x65\x68\x63\x61\x6c\x63\x89\xe3\x41\x51\x53\xff"
"\xd0\x31\xc9\xb9\x01\x65\x73\x73\xc1\xe9\x08\x51\x68\x50\x72\x6f"
"\x63\x68\x45\x78\x69\x74\x89\x65\x18\xe8\x87\xff\xff\xff\x31\xd2"
"\x52\xff\xd0";

Podemos ver el código en ensamblador de esta shellcode en el siguiente enlace:

https://packetstormsecurity.com/files/156478/Windows-x86-Null-Free-WinExec-Calc.exe-Shellcode.html

En este caso, la shellcode ejecuta una calculadora (calc.exe), provocando el siguiente flujo:

En lugar de un shellcode que solo ejecuta una calculadora, un atacante podría generar una shell reversa y conseguir acceso total a un servidor comprometido.

CONCLUSIÓN

  • En los ataques de Buffer Overflow podemos entender cómo es que una vulnerabilidad puede escalar desde un impacto bajo, a comprometer un servidor completo.
  • Es recomendable mantener los programas actualizados, ya que de encontrarse con programas que tengan un desbordamiento de buffer y que no tenga actualizaciones o parches para reparar la vulnerabilidad, un atacante podría aprovecharse de este vector de ataque y comprometer una organización entera.