¿Cómo un subdesbordamiento del búfer puede conducir a la ejecución remota de código en 64 bits?

Tengo el siguiente pseudocódigo resumido en C89 de una biblioteca de servidor ssh que solo proporciona acceso a cosas como git-shell (/bin/bash se reemplaza con el programa que se ejecutará, por lo que no es posible hacer otra cosa) :

struct _raw_uncapped_ssh_string { // no limit on the size of the string; uint32_t len; char non_null_terminated_string[]; // by protocol design it have a minimum length of 1 }; typedef struct _raw_uncapped_ssh_string raw_ssh_string; union buffer { void * uncapped_zlib_decompressed_network_data; // yes, the size is uncapped, so it’s possible to put 4Gb of // data in it that would be copied later into memory. zlib // allow easily to turn some Mb in Gb of data, but it’s not // the point of the question. raw_ssh_string st; }; get_command (compressed_network_data) { size_t len; char * command; buffer string=uncompress_to_buffer(compressed_network_data); len=ntohl(string.st.len)+1; command=malloc(len+1); command[len]=0; // here’s the point, both the string length and content as // well it’s supplied size is controlled by the attacker. memcpy(command,string.st.data,len); return command; } 

Aquí está cómo se ejecuta el comando más tarde (la cadena command no cambia después de get_command()) .

const char *args[]={"/bin/bash",command,NULL}; // /bin/bash isn’t the shell, it has been replaced by git‑shell. // redirect the program output to the network. dup2(stdin, 0); dup2(stdout,1); dup2(stdout,2); close(stdin); close(stdout); //if this return execution failed and print an error message return execv(args[0],(char * const *)args); // I don’t know which is the system, so I can’t know about the libc behaviour. 

No puedo hacer memcpy(command,string.st.data,0) ya que el tercer miembro de memcpy tiene un tamaño mínimo de 1, y en mi contexto, size_t usa un número entero de 64 bits, no puedo realizar un desbordamiento de búfer, ya que hay len.

Todo lo que puedo hacer es establecer len en un valor mayor que el asignado a string.st.data. Este es un subdesbordamiento del búfer que me permite leer la memoria no asignada.
Puedo leer la memoria del servidor, sin embargo, no puedo ver qué datos confidenciales puede contener un servidor ssh público (en mi caso, la lista de usuarios que puede realizar ssh es público) .

Lo mismo ocurre con un subdesbordamiento del búfer en memcpy permitir control remoto ejecución de código?

Comentarios

  • Si el código realmente lo hace command[len] = 0 entonces eso es un desbordamiento de búfer, ya que el índice máximo para un búfer de longitud len es len-1. Alternativamente, si el código real tiene un malloc(len+1) en lugar de un malloc(len), entonces puede hacer un desbordamiento masivo de búfer configurando el len valor a 0xFFFFFFFF.
  • @ThomasPornin: No, esto es un error de mi parte, de hecho, llama a varias funciones para hacer esta. Suponga que el resto es cierto.
  • @ThomasPornin: Estoy hablando de 64 bits, ya que len es size_t, usa un entero de 64 bits. Así que no hay forma de desbordamiento de enteros.
  • @ThomasPornin Asignan len+1 para que la configuración en 0 sea válida.
  • @ RoraΖ: Estaba hablando de establecer string.st.len en -1.

Respuesta

En general, no, un desbordamiento del búfer no se puede utilizar para la ejecución remota de código. Dado que los datos controlados por el atacante nunca dejan el espacio asignado para ellos, nunca tienen la capacidad de tomar el control del flujo de ejecución del programa.

Un subdesbordamiento del búfer tiene el potencial de otros tipos de ataques, como divulgación de información (si el programa cuenta con que los nuevos datos eliminen el contenido original del búfer).

Comentarios

  • O como yo visto recientemente en git, se convertirá más tarde en un desbordamiento de búfer.

Respuesta

Cuando escribí originalmente esta respuesta , parece que pasé por alto algunos malentendidos que tiene. Es probable que estos malentendidos le impidan comprender mi respuesta. Para ser claro, voy a hacer este texto grande para resaltar el énfasis, no para ser irrespetuoso:

Muchos de los términos que usa no significan lo que parece pensar que significan.

Por ejemplo:

  • «el tercer miembro de memcpy» no existe porque memcpy es una función, no una struct o union.
  • «No puedo realizar un desbordamiento del búfer, ya que hay len» y no puedo quedarme sin gasolina, ya que hay gasolina. ¿Le estoy rogando al ¿Pregunta? Me parece una analogía adecuada, especialmente porque teóricamente hablando, tu código podría invocar un desbordamiento del búfer.
  • «Todo lo que puedo hacer es configurar len a un valor mayor que el asignado a string.st.data. Esto es un subdesbordamiento del búfer … « No, esa no es la definición de un subdesbordamiento del búfer. Se produce un subdesbordamiento del búfer cuando accede fuera de los límites a una matriz utilizando un índice negativo o un índice que causaría que se produzca el ajuste aritmético del puntero. Esto último puede ser posible para algunas configuraciones (incluso en «sistemas de 64 bits», lo que sea que eso signifique), pero dudo que esto sea lo que quisiste decir cuando estabas escribiendo estas palabras, porque luego las seguiste con:
  • «… permitiéndome leer la memoria no asignada». Creo que quizás te refieres a «memoria no inicializada». Para ser claros, creo que querías decir que «ve asignó un exceso y dejó el exceso sin inicializar , y tal vez desee hacer algo al respecto (consulte calloc o memset).

Consideremos char *fubar = NULL; por un momento … este tipo de puntero generalmente tiene un cero valor.Desreferenciarlo se considera una desreferencia de puntero nulo , pero si escribimos algo como 0["hello"] obtenemos lo mismo que "hello"[0] (es decir, "h"). Por lo tanto, se puede usar una desreferencia de puntero nulo en ataques cuando el atacante controla la expresión entre llaves (como lo hace en su situación).

Volviendo al fubar cosa; digamos que memset(&fubar, UCHAR_MAX, sizeof fubar);, ahora fubar son 1 bits, lo que significa que podría ser una dirección esa es la dirección más grande que nuestro sistema podría (teóricamente) acomodar. ¿Qué pasa si accedemos a fubar[1]? ¿Cómo puede acceder al elemento después de la dirección ¿Dirección y luego volver a ajustar? Técnicamente, todo esto es un comportamiento indefinido, pero si tuviera que nombrar eso en la arquitectura común, sería:

  1. Desbordamiento aritmético de un puntero, que lleva a …
  2. Desreferencia de puntero nulo y / o potencialmente un subdesbordamiento del búfer .

¿un subdesbordamiento del búfer en memcpy permite la ejecución remota de código?

Un búfer underflow puede permitir a un atacante sobrescribir punteros de función ubicados en las regiones de la memoria antes de la matriz en cuestión. Si esos punteros de función están hechos para apuntar a shellcode, ese shellcode puede ser ejecutados cuando se invocan más adelante.

En este fragmento de código algo oscuro, parece que existe un riesgo de subdesbordamiento del búfer cuando int tiene un dominio que uint32_t, ya que ntohl(string.st.len)+1 haría que el valor uint32_t se convierta en un int tipo. Considere, por ejemplo, si INT_MIN es -4294967296 (que es uno menos que 0 - UINT32_MAX) y INT_MAX es 4294967295 … esto esencialmente sería un int de 33 bits con relleno para rellenar hasta el ancho de un byte; poco común, pero posible. En esta circunstancia, la expresión ntohl(string.st.len)+1 no es uint32_t de tipo; tiene un tipo «int, y en lugar de volver a 0 cuando se produce un desbordamiento de enteros sin firmar , probablemente se ajusta a -4294967296 cuando se produce un desbordamiento de enteros con signo .

Si «está buscando una garantía contra el desbordamiento del búfer , utilice el U sufijo literal entero (es decir, ntohl(string.st.len)+1U). Luego, en esa situación, la expresión será uint32_t o unsigned int (según el tipo que tenga el mayor dominio).

Si considera que ntohl(string.st.len) puede devolver uno menos que el valor máximo para ese tipo sin firmar (lo que sea), entonces len=ntohl(string.st.len)+1 dará como resultado el valor máximo, malloc(len+1) causará envoltura sin firmar por lo que» terminará invocando malloc(0) y luego command[len]=0 escribirá bien y verdaderamente más allá del final de la matriz. Entonces, por supuesto, «también tendrá un problema con memcpy(command,string.st.data,len); (esto es un desbordamiento del búfer).

El búfer se desborda y los desbordamientos no son el único riesgo. Si no «t verifica el valor de retorno de malloc y malloc devuelve NULL, luego se puede usar una desreferencia de puntero nulo para provocar la ejecución de código arbitrario. Esto implica que debe tener un método para comunicarse malloc fallas a la persona que llama. También implica que puede usar ese mismo método para verificar y comunicar ese problema de envoltura a la persona que llama.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *