Escribir modismos Secure C y Secure C

10 minutos de lectura

Escribir modismos Secure C y Secure C
Ethan Heilman

“El hombre promedio no quiere ser libre. Simplemente quiere estar seguro”. – HL Menken

Estoy tratando de escribir C muy seguro. A continuación, enumero algunas de las técnicas que uso y pregunto si son tan seguras como creo que son. Por favor, no dude en romper mi código/preconcepciones en pedazos. Cualquier respuesta que encuentre incluso la vulnerabilidad más trivial o me enseñe una nueva idea será altamente valorado.

Lectura de un stream:

De acuerdo con la Tutorial de programación GNU C obtener línea:

La función getline automáticamente ampliará el bloque de memoria según sea necesario, a través de la función realloc, por lo que nunca faltará espacio, una de las razones por las que getline es tan seguro. [..] Tenga en cuenta que getline puede manejar con seguridad su línea de entrada, sin importar cuánto tiempo sea.

Supongo que getline debería, bajo todas las entradasprevenir un desbordamiento de búfer ocurra al leer de una secuencia.

  • ¿Es correcta mi suposición? ¿Existen entradas y/o esquemas de asignación bajo los cuales esto podría conducir a un exploit? Por ejemplo, ¿qué sucede si el primer carácter de la transmisión es algún extraño personaje de controltal vez 0x08 RETROCESO (ctl-H).
  • ¿Se ha realizado algún trabajo para demostrar matemáticamente que getline es seguro?

Malloc devuelve nulo en caso de falla:

Si malloc encuentra un error, malloc devuelve un puntero NULL. Esto presenta un riesgo de seguridad ya que todavía se puede aplicar la aritmética de punteros a un puntero NULL (0x0), por lo tanto, wikipedia recomienda

/* Allocate space for an array with ten elements of type int. */
int *ptr = (int*)malloc(10 * sizeof (int));
if (ptr == NULL) {
    /* Memory could not be allocated, the program should handle 
       the error here as appropriate. */
} 

Escaneo seguro:

Cuando usas escanear Me he acostumbrado a asignar el tamaño de las cadenas que se extraerán al tamaño de la cadena de entrada, con la esperanza de evitar la posibilidad de un exceso. Por ejemplo:

const char *inputStr = "a01234b4567c";
const char *formatStr = "a%[0-9]b%[0-9]c":
char *str1[strlen(inputStr)];
char *str2[strlen(inputStr)];

sscanf(inputStr, formatStr, str1, str2);

Debido a que str1 y str2 tienen el tamaño de inputStr y no se pueden leer más caracteres que strlen (inputStr) de inputStr, parece imposible, dado todos los valores posibles para que inputStr provoque un desbordamiento de búfer?

  • ¿Estoy en lo correcto? ¿Hay casos extraños en los que no he pensado?
  • ¿Hay mejores formas de escribir esto? ¿Bibliotecas que ya lo han solucionado?

Preguntas generales:

Si bien he publicado una gran cantidad de preguntas, no espero que nadie las responda todas. Las preguntas son más una guía para el tipo de respuestas que estoy buscando. Tengo muchas ganas de aprender la mentalidad de C seguro.

  • ¿Qué otros lenguajes C seguros existen?
  • ¿Qué casos de esquina necesito siempre revisa?
  • ¿Cómo puedo escribir pruebas unitarias para hacer cumplir estas reglas?
  • ¿Cómo puedo hacer cumplir las restricciones de manera verificable o demostrablemente correcta?
  • ¿Alguna técnica o herramienta de análisis estático/dinámico recomendada para C?
  • ¿Qué prácticas seguras de C sigue y cómo las justifica ante usted mismo y ante los demás?

Recursos:

Muchos de los recursos se tomaron prestados de las respuestas.

  • Tal vez esto debería ser wiki de la comunidad, dado el amplio alcance de la pregunta…

    – R. Martinho Fernández

    5 de enero de 2010 a las 18:31

  • 1 nota: en lugar de llamar strlen() dos veces, debe almacenarlo: size_t len = strlen(inputStr); char *str1[len]; char *str2[len]; El compilador puede hacer esto por usted, pero no es tan difícil de hacer usted mismo, es tan legible (si no más) y está garantizado para ser lo que desea.

    – Chris Lutz

    5 de enero de 2010 a las 18:39

  • “El hombre promedio no quiere ser libre. Simplemente quiere estar seguro”. Orly, algunas arquitecturas no tienen punteros, etc. (todo en el futuro), pero aún eres libre de hacer cualquier cosa con ellas 🙂

    – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

    5 de enero de 2010 a las 18:40

  • @Chris Lutz, eso tiene mucho sentido dadas cadenas muy grandes, gracias.

    –Ethan Heilman

    5 de enero de 2010 a las 19:22

  • pequeño consejo, utilícelo cuando no quiera cambiar una cadena const char *, y aún más importante cuando no tenga permitido hacerlo, es decir, en su ejemplo debería leerse: const char *inputStr = “a01234b4567c”;

    – quinmars

    5 de enero de 2010 a las 22:28

Escribir modismos Secure C y Secure C
no

Creo que tu ejemplo de sscanf es incorrecto. Todavía puede desbordarse cuando se usa de esa manera.

Pruebe esto, que especifica el número máximo de bytes para leer:

void main(int argc, char **argv)
{
  char buf[256];
  sscanf(argv[0], "%255s", &buf);
}

Eche un vistazo a este artículo de desarrollo de IBM sobre la protección contra desbordamientos de búfer.

En términos de prueba, escribiría un programa que genere cadenas aleatorias de longitud aleatoria y las alimentaría a su programa, y ​​me aseguraría de que se manejen adecuadamente.

  • ¿Puedes explicar cómo es posible que se desborde? Evité usar una cadena de formato que especificaba los caracteres para leer, es decir, %255s, porque afirmo que mi modismo sscanf es seguro incluso si la cadena de formato enumera incorrectamente los caracteres para leer.

    –Ethan Heilman

    5 de enero de 2010 a las 19:19

  • Aún mejor sería sscanf (argv[0], "%*s", sizeof buf - 1, buf); para evitar la codificación de elementos en función del tamaño del búfer.

    – Wallyk

    5 de enero de 2010 a las 19:24

  • @wallyk, ¿funciona esto para más de un carácter de formato? Por ejemplo, sscanf (argv[0]”%*[5-9]abcdefg%*[0-2]”, sizeof str1 – 1, str1, sizeof str2, str2); ¿funciona de manera segura? ¿Qué está haciendo *? ¿Deferenciando s?

    –Ethan Heilman

    5 de enero de 2010 a las 19:42


  • Envuelva su código con el carácter de acento grave (la misma tecla que la tilde)… eso le permitirá publicar código en línea en su comentario.

    – Brian Reavis

    5 de enero de 2010 a las 22:38

  • @wallyk <code>sscanf (argv[0], "%*s", sizeof buf - 1, buf);</code> no funciona, en el contexto de un escaneo * es un token de supresión de asignación. Ver opengroup.org/onlinepubs/009695399/functions/scanf.html . ¿Alguna idea de cómo hacer lo que pretendía sin volver a escribir la cadena como se publicó aquí stackoverflow.com/questions/545018/…

    –Ethan Heilman

    6 de enero de 2010 a las 3:54


Escribir modismos Secure C y Secure C
gavinb

  1. Lectura de un flujo

    El hecho de que getline() “aumentará automáticamente el bloque de memoria según sea necesario” significa que esto podría usarse como un ataque de denegación de servicio, ya que sería trivial generar una entrada tan larga que agotaría la memoria disponible para el proceso (o peor, el sistema!). Una vez que ocurre una condición de falta de memoria, también pueden entrar en juego otras vulnerabilidades. El comportamiento del código con poca memoria o sin memoria rara vez es agradable y muy difícil de predecir. En mi humilde opinión, es más seguro establecer límites superiores razonables en todo, especialmente en aplicaciones sensibles a la seguridad.

    Además (como anticipas al mencionar caracteres especiales), getline() solo te da un búfer; no ofrece ninguna garantía sobre el contenido del búfer (ya que la seguridad depende completamente de la aplicación). Por lo tanto, desinfectar la entrada sigue siendo una parte esencial del procesamiento y la validación de los datos del usuario.

  2. escanear

    Tiendo a preferir usar una biblioteca de expresiones regulares y tener expresiones regulares muy definidas para los datos del usuario, en lugar de usar sscanf. De esta manera, puede realizar una buena cantidad de validación en el momento de la entrada.

  3. Comentarios generales

    • Hay herramientas de fuzzing disponibles que generan entradas aleatorias (tanto válidas como no válidas) que se pueden usar para probar su manejo de entradas
    • La gestión del búfer es crítica: desbordamientos de búfer, subdesbordamientos, falta de memoria
    • Las condiciones de carrera se pueden explotar en un código seguro
    • Los archivos binarios pueden manipularse para inyectar valores no válidos o valores de gran tamaño en los encabezados, por lo que el código de formato de archivo debe ser sólido como una roca y no asumir que los datos binarios son válidos.
    • Los archivos temporales a menudo pueden ser una fuente de problemas de seguridad y deben administrarse con cuidado.
    • La inyección de código se puede usar para reemplazar las llamadas a la biblioteca del sistema o en tiempo de ejecución con versiones maliciosas
    • Los complementos proporcionan un gran vector de ataque
    • Como principio general, sugeriría tener interfaces claramente definidas en las que los datos del usuario (o cualquier dato externo a la aplicación) se supongan inválidos y hostiles hasta que se procesen, desinfecten y validen, y sea la única forma de que los datos del usuario ingresen a la aplicación.

1646748739 949 Escribir modismos Secure C y Secure C
robar pozos

Un buen lugar para empezar a ver esto es Excelente sitio de codificación segura de David Wheeler.

Su libro online gratuito “Programación segura para Linux y Unix CÓMO” es un excelente recurso que se actualiza periódicamente.

También te puede interesar ver su excelente analizador estático Buscador de fallas para obtener más pistas. Pero recuerde, ninguna herramienta automatizada es un reemplazo para un buen par de ojos experimentados, o como David lo expresa tan coloridamente…

Cualquier herramienta de análisis estático, como Flawfinder, es simplemente una herramienta. ¡Ninguna herramienta puede sustituir al pensamiento humano! En breve, “un tonto con una herramienta sigue siendo un tonto”. Es un error pensar que las herramientas de análisis (como el detector de fallas) son un sustituto de la capacitación y el conocimiento de seguridad.

He utilizado personalmente los recursos de David durante varios años y los encuentro excelentes.

Programación insegura por ejemplo
blog con algunas de las respuestas

1646748741 758 Escribir modismos Secure C y Secure C
Pascal Cuoq

Yannick Moy desarrolló un sistema de condición previa más débil de Hoare/Floyd para C durante su doctorado y lo aplicó a la biblioteca de cadenas administradas por CERT. Encontró una serie de errores (ver página 197 de sus memorias). La buena noticia es que ahora la biblioteca es más segura para su trabajo.

1646748742 124 Escribir modismos Secure C y Secure C
jackson

También puede consultar el sitio web de Les Hatton aquí y en su libro C más seguro que puede obtener de Amazon.

1646748743 937 Escribir modismos Secure C y Secure C
Alok Singhal

no usar gets() para entrada, use fgets(). Usar fgets()si su búfer se asigna automáticamente (es decir, “en la pila”), utilice esta expresión:

char buf[N];
...
if (fgets(buf, sizeof buf, fp) != NULL)

Esto seguirá funcionando si decide cambiar el tamaño de buf. Prefiero esta forma a:

#define N whatever
char buf[N];
if (fgets(buf, N, fp) != NULL)

porque la primera forma usa buf para determinar el segundo argumento, y es más claro.


Compruebe el valor de retorno de fclose().


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad