Vulnerabilidad de terminación de cadena
Al pensar más en esto, usar strncpy()
es probablemente la forma más común (en la que puedo pensar) que podría crear errores de terminación nulos. Ya que generalmente la gente piensa que la longitud del búfer no incluye strncpy(a, "0123456789abcdef", sizeof(a));
. Así que verás algo como lo siguiente:
a
Suponiendo que char a[16]
se inicializa con a
, la cadena strlen(a)
no terminará en nulo. Entonces, ¿por qué es esto un problema? Bueno, en memoria ahora tienes algo como:
30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 66
e0 f3 3f 5a 9f 1c ff 94 49 8a 9e f5 3a 5b 64 8e
Sin un terminador nulo, las funciones de cadena estándar no sabrán la longitud del búfer. Por ejemplo, 0x00
continuará contando hasta que alcance un string.h
byte. ¿Cuándo es eso, quién sabe? Pero siempre que lo encuentre, devolverá una longitud mucho mayor que su búfer; digamos 78. Veamos un ejemplo:
int main(int argc, char **argv) {
char a[16];
strncpy(a, "0123456789abcdef", sizeof(a));
... lots of code passes, functions are called...
... we finally come back to array a ...
do_something_with_a(a);
}
void do_something_with_a(char *a) {
int a_len = 0;
char new_array[16];
// Don't know what the length of the 'a' string is, but it's a string so lets use strlen()!
a_len = strlen(a);
// Gonna munge the 'a' string, so lets copy it first into new_array
strncpy(new_array, a, a_len);
}
Acaba de escribir 78 bytes en una variable que solo tiene 16 bytes asignados.
Desbordamientos de búfer
Se produce un desbordamiento de búfer cuando se escriben más datos en un búfer que los asignados para ese búfer. Esto no es diferente para una cadena, excepto que muchas de las funciones a_len
se basan en este byte nulo para señalar el final de una cadena. Como vimos arriba.
En el ejemplo, escribimos 78 bytes en un búfer que solo está asignado para 16. No solo eso, sino que es una variable local. Lo que significa que el búfer ha sido asignado en la pila. Ahora, los últimos 66 bytes que se escribieron, simplemente sobrescribieron 66 bytes de la pila.
Si escribe suficientes datos más allá del final de ese búfer, sobrescribirá la otra variable local do_something_with_a()
(también no es bueno si la usa más adelante), cualquier puntero de marco de pila que se guardó en la pila, y luego Dirección de retorno de la función. Ahora realmente has ido y arruinado las cosas. Porque ahora la dirección de retorno es algo completamente equivocado. Cuando se llega al final de %code% , suceden cosas malas.
Ahora podemos agregar un poco más al ejemplo anterior.
void do_something_with_a(char *a, char *new_a) {
int a_len = 0;
char new_array[16];
// Don't know what the length of the 'a' string is, but it's a string so
// lets use strlen()!
a_len = strlen(a);
//
// By the way, copying anything based on a length that's not what you
// initialized the array with is horrible horrible coding. But it's
// just an example.
//
// Gonna munge the 'a' string, so lets copy it first into new_array
strncpy(new_array, a, a_len);
// 'a_len' was on the stack, that we just blew away by writing 66 extra
// bytes to the 'new_array' buffer. So now the first 4 bytes after 16
// has now been written into a_len. This can still be interpreted as
// a signed int. So if you use the example memory, a_len is now 0xe0f33f5a
//
// ... did some more munging ...
//
// Now I want to return the new munged string in the *new_a variable
strncpy(new_a, new_array, a_len);
// Everything burns
}
Creo que mis comentarios explican todo bastante. Pero al final, ahora has escrito una gran cantidad de datos en una matriz, probablemente pensando que solo estás escribiendo 16 bytes. Dependiendo de cómo se manifieste esta vulnerabilidad, esto podría conducir a la explotación a través de la ejecución remota de código.
Este es un ejemplo muy artificial de codificación deficiente, pero puede ver cómo las cosas pueden escalar rápidamente si no tiene cuidado al trabajar con la memoria y al copiar datos. La mayoría de las veces la vulnerabilidad no será tan obvia. Con los programas grandes, tiene tanto que hacer que la vulnerabilidad no sea fácil de detectar y se desencadene con el código de múltiples llamadas a funciones.
Para obtener más información sobre cómo funcionan los desbordamientos de búfer .
Y antes de que alguien lo mencione, ignoré la endianess al hacer referencia a la memoria por simplicidad
Lecturas adicionales
Descripción completa de la vulnerabilidad
Entrada de Enumeración de debilidad común (CWE)
Presentación de cadenas de codificación seguras (el PDF se descarga automáticamente)
University of Pittsburgh - Codificación segura C / C ++: Vulnerabilidades de cadenas (PDF)