×

Langue

Fermer
Atelier 801
  • Forums
  • Dev Tracker
  • Connexion
    • English Français
      Português do Brasil Español
      Türkçe Polski
      Magyar Română
      العربية Skandinavisk
      Nederlands Deutsch
      Bahasa Indonesia Русский
      中文 Filipino
      Lietuvių kalba 日本語
      Suomi עברית
      Italiano Česky
      Hrvatski Slovensky
      Български Latviešu
      Estonian
  • Langue
  • Forums
  • /
  • Atelier 801
  • /
  • Hors-sujet
  • /
  • Hilos temporales!
  • /
  • Fallos y vulnerabilidades en C/C++
Fallos y vulnerabilidades en C/C++
Conservador
« Censeur »
1587783540000
    • Conservador#1895
    • Profil
    • Derniers messages
#1
  6
  • C/C++

C(++)



C y C++ son considerados unos de los lenguajes más potentes y eficaces que existen. Son realmente buenos y sirven para una gran cantidad de tareas, desde programas de uso general y videojuegos hasta sistemas operativos y embebidos. Ambos son lenguajes compilados, y estáticos.

Aún así, tienen fallos de seguridad y funcionamiento, unos ligeros, otros mucho más graves, los cuales son:

» Buffer Overflow Desbordamiento del búfer

Esto ocurre cuando tienes una cierta cantidad de memoria reservada e intentas llenar esa memoria con más datos de lo que su capacidad admite. Los datos que sobran, van a espacios de memoria adyacentes.

Normalmente un compilador de cualquier otro lenguaje avisaría del error y no permitirá correr el programa, pero esto no ocurre en C, y lo que hace -en algunos casos- es avisar de SEGFAULT (fallo de segmentación) mientras el proceso está ejecutandose.

Esto es especialmente peligroso porque, además de manipular datos de otras variables (y en algunos casos, el mismo funcionamiento) en el programa ejecutado, puede interferir en el sistema y corromperlo. Es algo que suele ser usado para esto y para saltarse barreras de seguridad en el sistema.

Varias funciones de la biblioteca estándar tiene este defecto, por lo que no se recomienda para nada su uso, las cuales son:
  • gets(char *str): Esta función NO comprueba la longitud del string; al asignar los datos a un array que es de una longitud menor, produce un desbordamiento del búfer. Al hacerlo con uno de mayor longitud, llena todos los espacios restantes con 0 desperdiciando memoria.
    Código de demostración
    Code C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <stdio.h>
    #include <stdlib.h>

    int main(void){
    char usuario[8];
    int permitir = 0;

    printf("Introduce tu nombre de usuario: ");
    gets(usuario); // el usuario introduce 'malicioso'

    if(concederAcceso(usuario)){
    permitir = 1;
    }
    if(permitir != 0){ // se han sobreescrito datos por el desbordamiento de usuario
    accionPrivilegiada();
    }

    return EXIT_SUCCESS;
    }

    En este caso es preferible usar fgets(char *str, int n, FILE *stream) y memoria dinámica; en el cual n correspondería al límite de lectura (si antes no encuentra \n, \0 o EOF) y *stream correspondería al puntero con la dirección de la que deben de extraerse los datos [stdin para este caso].
  • strcpy(char *dest, const char *src): Al igual que gets, no realiza una comprobación de la longitud del string y el error se puede cometer de la misma forma, de hecho, toda su familia de funciones (strcmp, strcat) también lo es por la misma vía. El primer argumento corresponde a un puntero con dirección al destino y el segundo a un puntero constante con dirección a la fuente de la que provienen los datos.

    En este caso es preferible usar la familia strl-, o la familia strn, que incluyen al final una variable size_t que corresponde al tamaño de los datos. La familia strn no copia el carácter nulo '\0'.
  • sprintf(char *str, const char *format, ...): Igual que las otras dos, no realiza comprobación de la longitud del string, dando lugar a desbordamientos al formatear el string.
    Código de demostración
    Code C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <stdio.h>
    #include <stdlib.h>

    enum{
    BUFFER_SIZE = 10
    };

    int main(void){
    char buffer[BUFFER_SIZE];
    int check = 0;

    sprintf(buffer, "%s", "Este string es realmente largo...");

    printf("check: %d", check); /* No va a imprimir 0 */

    return EXIT_SUCCESS;
    }


    La solución es usar snprintf(char *str, size_t size, const char *format, ...); que evita desbordamiento de búfer al descartar el exceso y además retorna en valor de entero el tamaño del string formateado, con lo que se pueden realizar excepciones que comprueben si el tamaño del string introducido era mayor al límite.

» Formateo de entrada y salida de datos

La entrada y salida de datos es muy importante, pero la biblioteca estándar trae un par de funciones algo peligrosas, que puede causar fallos de seguridad, indeterminismo y hasta crashear al programa.
  • Toda la familia de printf: Aquí entran los ataques al formateo de datos. Datos que normalmente serían privados, propios del programa e inaccesible por otros, pueden ser accedidos al llamar a la función de forma externa. Este es un gran fallo de seguridad.

    La solución es ser muy riguroso al momento de realizar el formateo de datos y evitar en lo posible la entrada de datos por parte del usuario a estas funciones.
  • Toda la familia de scanf:
  • Esta función es muy peculiar, fue hecha para recolectar del búfer de entrada una gran variedad de datos, desde los tipos básicos hasta valores científicos. Esto es bueno... ¿no? Es lo que en el curso te dijeron que debías de usar... Es funcional, pero peligrosa.

    Cómo crashear a un programa en un par de líneas
    Code C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #include <stdio.h>
    #include <stdlib.h>

    int main(void)
    {
    int a;
    printf("Introduce un numero: ");
    scanf("%d", &a);
    printf("Haz introducido %d.\n", a);

    return EXIT_SUCCESS;
    }

    Ejecutas el programa:
    a dit :
    Introduce un numero: 42
    Haz introducido 42.

    Todo bien. Se realiza la conversión de un doble flotante a un entero. ¿Qué tal si introducimos un tipo distinto?

    a dit :
    Introduce un numero: sfadsfadsddads
    Haz introducido 2583.

    ¿¡Qué está ocurriendo aquí!? Scanf intenta realizar la conversión, pero al no haber ningún número, convierte a nada. El valor de a nunca se define, por lo que la variable tendrá un comportamiento indefinido.

    El comportamiento indefinido de una función o una variable es peligroso, porque al momento de utilizarla podría no funcionar como uno espera. Podría no ocurrir sólo esto, sino también que el programa se crashee. Esto en otros lenguajes se evita ya que no se ejecuta el código a menos que cada tipo se defina de alguna forma.

    Scanf verifica en el string con los argumentos formateados, "%d", si en búfer no se encuentra ningún número, pasará de cualquier otro tipo y convertirá a nada, pues el valor a convertir no fue definido.

    Scanf retorna la cantidad de elementos convertidos satisfactoriamente, con eso sería suficiente, ¿no? Podríamos hacer que no pare de escanear hasta que los argumentos correctos se introduzcan,
    Code C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <stdio.h>
    #include <stdlib.h>

    int main(void){
    int a;
    printf("Introduce un numero: ");
    while(scanf("%d", &a) != 1){
    // la entrada no fue un número? pregunta de nuevo:
    printf("Introduce un numero: ");
    }
    printf("Introduciste %d.\n", a);

    return EXIT_SUCCESS;
    }
    hasta que...
    a dit :
    Introduce un numero: asd Introduce un numero: Introduce un numero: Introduce un numero: Introduce un numero: Introduce un numero: Introduce un numero: Introduce un numero: Introduce un numero: Introduce un numero: ^Z

    ¡Oh por Glob! ¿Qué pasó? Scanf no fue hecha para leer entrada sino para ESCANEARLA. Scanf nunca leerá algo que no se le ha mandado a escanear. asd no es un número, por lo que ni siquiera será escaneado, por lo que, Scanf irá nuevamente al búfer de entrada y se encontrará con lo mismo, entrando en un bucle infinito de escaneos fallidos.

    Podríamos limpiar el búfer de entrada con fflush(stdin);... No. Es una terrible práctica, primero porque no todos los sistemas lo harán, y segundo, porque aunque se pueda hacer en algunos, el comportamiento del búfer de entrada quedará indeterminado, algo que ya hace saltar alarmas.

    Pero... ¿qué pasa si intentamos escanear un string? (reemplaza la "%d" por una "%s")

    Tenemos una variable de string de 10 de tamaño, introducimos datos y el scanf se encsrga de escanearlo:
    a dit :
    Introduce un string: hola! 123
    Salida: hola! 123

    a dit :
    Introduce un string: Hipopotomonstrosesquipedaliofobia

    ...

    El string ha producido un desbordamiento del búfer, bien el programa podría crashear, avisar de un fallo de segmentación o funcionar "correctamente". El comportamiento es indeterminado en el momento en el que el desbordamiento se realiza. Una forma de arreglar esto es determinar en el scanf la cantidad que debe de leer, siendo en vez de "%s", utilizar %Ns", en el que N es la cantidad de caracteres.

    Si sólo se quiere leer entrada, bien puede usarse fgets y realizar una función propia. Hay varias funciones incorporadas en la biblioteca estándar que permiten la conversión de unos tipos a otros.

    Scanf es una función que de la forma adecuada, puede llegar a ser bastante poderosa. Hay que saber usarla de la forma correcta.


» Dinamismo de tipos

Todo dato puede considerarse un entero. Puedes formatear un carácter como de un entero se tratase, realizar conversiones y hasta operaciones aritméticas entre tipos de datos totalmente distintos. Esto es bueno y malo a la vez.

Por ejemplo:
Code C

1
2
3
4
5
6
int main(void){
int a = 5, b = 11;
char c = a*b;

putc(c, stdout);
}
¿Cuál será el valor de C? Spoiler: '7'. Ya que 5×11 da 55 y este valor corresponde al carácter 7 en el estándar US-ASCII. Utilizar enteros para representar un carácter puede ser muy útil y bueno, pero esa operación de multiplicación no tiene el mínimo sentido para darle valor al carácter.

Ahora, hago esto:

Code C

1
    c *= 2;

El valor será 110, lo que corresponde al carácter 'n'. No hay un sentido lógico real detrás de esa conversión. ¿Multiplicar un carácter por un número me da otro carácter?

Code C

1
2
    double d = 0.08;
c /= b / d;

La salida será un '}'. No debo de explicar lo absurdo que es.

También se pueden realizar operaciones entre enteros con signo y sin signo entre sí, con float y double, con long long y short.


Fuentes: Experiencias propias y un par de artículos de Internet.

Dernière modification le 1587783720000
Conservador
« Censeur »
1587784080000
    • Conservador#1895
    • Profil
    • Derniers messages
#2
  1
Si este Thread llega a dos corazones editaré el hilo con fallos de otros lenguajes
Kelt
« Sénateur »
1587794460000
    • Kelt#0365
    • Profil
    • Derniers messages
    • Tribu
#3
  3
no leí nada pero le di like porque vi el número 42

y me gusta el número 42
Conservador
« Censeur »
1593817740000
    • Conservador#1895
    • Profil
    • Derniers messages
#4
  0
Conservador a dit :
Si este Thread llega a dos corazones editaré el hilo con fallos de otros lenguajes

Perdón por la demora. Mañana hago la pestaña sobre otros lenguajes, me re olvidé de esto.
Leon
« Sénateur »
1593898080000
    • Leon#8578
    • Profil
    • Derniers messages
    • Tribu
#5
  1
No sabía lo de la mala práctica del fflush , tendré que aprender nuevas alternativas porque mi profesor es fanático de Linux si me ve , me putea
Conservador
« Censeur »
1593913320000
    • Conservador#1895
    • Profil
    • Derniers messages
#6
  0
Conservador a dit :
Conservador a dit :
Si este Thread llega a dos corazones editaré el hilo con fallos de otros lenguajes

Perdón por la demora. Mañana hago la pestaña sobre otros lenguajes, me re olvidé de esto.

Hoy no he podido. Estuve algo ocupado y el poco tiempo libre que tuve para el foro lo dediqué a responderle al tipo de la foto naranja con negro. En fin, creo que me dedicaré a terminar el tema de C/C++ antes que todo. de otros idiomas no conozco mucho asi que tendré que buscar con quién cersiorarme.
  • Forums
  • /
  • Atelier 801
  • /
  • Hors-sujet
  • /
  • Hilos temporales!
  • /
  • Fallos y vulnerabilidades en C/C++
© Atelier801 2018

Equipe Conditions Générales d'Utilisation Politique de Confidentialité Contact

Version 1.27