entendiendo los peligros de sprintf (…)

OWASP dice:

“Las funciones de la biblioteca C, como strcpy (), strcat (), sprintf () y vsprintf () operan en cadenas terminadas nulas y no realizan comprobación de límites.”

sprintf escribe datos formateados en la cadena int sprintf (formato char * str, const char *, …);

Ejemplo:

sprintf(str, "%s", message); // assume declaration and // initialization of variables 

Si entiendo el comentario de OWASP, entonces los peligros de usar sprintf son

1) si la longitud del mensaje > str ‘s length, hay un desbordamiento del búfer

y

2) si el mensaje no termina en nulo con \0 , entonces el mensaje podría copiarse en str más allá de la dirección de memoria del mensaje , causando un desbordamiento del búfer

Por favor confirme / niegue. Gracias

Tiene razón en ambos problemas, aunque en realidad son ambos el mismo problema (que es acceder a los datos más allá de los límites de una matriz).

Una solución para su primer problema es usar snprintf , que acepta un tamaño de búfer como argumento.

Una solución a su segundo problema es dar un argumento de longitud máxima a s[n]printf . Por ejemplo:

 char buffer[128]; snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA"); // strcmp(buffer, "This is a test\n") == 0 

Si desea almacenar la cadena completa (por ejemplo, en el caso de que sizeof(buffer) sea ​​demasiado pequeño), ejecute snprintf dos veces:

 // Behaviour is different in SUSv2; see // "conforming to" section of man 3 sprintf int length = snprintf(NULL, 0, "This is a %.4s\n", "testGARBAGE DATA"); ++length; // +1 for null terminator char *buffer = malloc(length); snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA"); 

(Probablemente pueda ajustar esto en una función usando va ).

Ambas afirmaciones son correctas.

Hay un problema adicional no mencionado. No hay verificación de tipo en los parámetros. Si no coincide con la cadena de formato y los parámetros, podría producirse un comportamiento indefinido y no deseado. Por ejemplo:

 char buf[1024] = {0}; float f = 42.0f; sprintf(buf, "%s", f); // `f` isn't a string. the sun may explode here 

Esto puede ser especialmente desagradable de depurar.

Todo lo anterior llevó a muchos desarrolladores de C ++ a la conclusión de que nunca debes usar sprintf y sus hermanos. De hecho, hay instalaciones que puede utilizar para evitar todos los problemas anteriores. One, streams, está integrado directamente en el lenguaje:

 #include  #include  // ... float f = 42.0f; stringstream ss; ss << f; string s = ss.str(); 

... y otra opción popular para aquellos que, como yo, aún prefieren usar sprintf proviene de las bibliotecas de formato boost :

 #include  #include  // ... float f = 42.0f; string s = (boost::format("%1%") %f).str(); 

¿Debes adoptar el mantra "nunca usar sprintf"? Decide por ti mismo. Usualmente hay una mejor herramienta para el trabajo y dependiendo de lo que estés haciendo, sprintf puede ser.

Sí, es principalmente una cuestión de desbordamientos de búfer. Sin embargo, ahora son asuntos bastante serios, ya que los desbordamientos de los búferes son el principal vector de ataque utilizado por los crackers del sistema para eludir la seguridad del software o del sistema. Si expone algo como esto a la entrada del usuario, hay muchas posibilidades de que entregue las llaves de su progtwig (o incluso su propia computadora) a los crackers.

Desde la perspectiva de OWASP, pretendemos que estamos escribiendo un servidor web, y usamos sprintf para analizar la entrada que un navegador nos pasa.

Supongamos ahora que alguien malicioso pasa a nuestro navegador una cadena mucho más grande que la que cabe en el buffer que elegimos. Sus datos adicionales en su lugar sobrescribirán datos cercanos. Si lo hace lo suficientemente grande, algunos de sus datos se copiarán a través de las instrucciones del servidor web en lugar de sus datos. Ahora puede hacer que nuestro servidor web ejecute su código .

Sus 2 conclusiones numeradas son correctas, pero incompletas.

Hay un riesgo adicional:

 char* format = 0; char buf[128]; sprintf(buf, format, "hello"); 

Aquí, el format no tiene terminación NULL. sprintf() tampoco lo comprueba.

Tu interpretación parece ser correcta. Sin embargo, su caso n. ° 2 no es realmente un desbordamiento de búfer. Es más una violación de acceso a la memoria. Sin embargo, eso solo es terminología, sigue siendo un problema importante.

La función sprintf, cuando se usa con ciertos especificadores de formato, presenta dos tipos de riesgos de seguridad: (1) escribir memoria no debería; (2) leer memoria no debería. Si snprintf se usa con un parámetro de tamaño que coincida con el búfer, no escribirá nada que no debería. Dependiendo de los parámetros, todavía puede leer cosas que no debería. Dependiendo del entorno operativo y de qué más esté haciendo un progtwig, el peligro de lecturas incorrectas puede ser o no ser menos severo que el de las escrituras incorrectas.

Es muy importante recordar que sprintf () agrega el carácter ASCII 0 como terminador de cadena al final de cada cadena. Por lo tanto, el buffer de destino debe tener al menos n + 1 bytes (para imprimir la palabra “HELLO”, se requiere un buffer de 6 bytes, NO 5)

En el ejemplo siguiente, puede que no sea obvio, pero en el búfer de destino de 2 bytes, el segundo byte se sobrescribirá con el carácter ASCII 0. Si solo se asignó 1 byte para el búfer, esto causaría el desbordamiento del búfer.

 char buf[3] = {'1', '2'}; int n = sprintf(buf, "A"); 

También tenga en cuenta que el valor de retorno de sprintf () NO incluye el carácter de terminación nula. En el ejemplo anterior, se escribieron 2 bytes, pero la función devuelve ‘1’.

En el siguiente ejemplo, la variable miembro ‘i’ del primer byte de la clase se sobrescribirá parcialmente con sprintf () (en un sistema de 32 bits).

 struct S { char buf[4]; int i; }; int main() { struct S s = { }; si = 12345; int num = sprintf(s.buf, "ABCD"); // The value of si is NOT 12345 anymore ! return 0; } 

Más o menos he dicho un pequeño ejemplo de cómo puedes deshacerte de la statement del tamaño del búfer para el sprintf (¡si fue tu intención, por supuesto!) Y no snprintf envolved ….

Nota : Este es un ejemplo de APPEND / CONCATENATION, eche un vistazo aquí