2010-06-01 40 views
5

¿Alguien me dice cómo bloquear algunas llamadas al sistema específicas dentro de un programa, por favor? Estoy construyendo un sistema que toma una parte del código fuente C, lo compila con gcc y lo ejecuta. Por razones de seguridad, necesito evitar que el programa compilado llame algunas llamadas al sistema. ¿Hay alguna forma de hacerlo, desde el nivel del código fuente (por ejemplo, eliminando los archivos de encabezado de gcc, detectando llamadas externas maliciosas, ...) al nivel del ejecutable?GCC cómo bloquear llamadas al sistema dentro de un programa?

Edited # 1: Agregue detalles sobre llamadas maliciosas.

Edited # 2: Mi sistema es uno de GNU/Linux.

Editado # 3:

me han tratado algunos métodos dentro de unos días y aquí están las conclusiones a las que tenemos hasta ahora:

  1. escanear el código fuente no resuelve el principal problema ya que uno siempre puede obstruir su archivo fuente C bastante bien.
  2. "Reemplazar el símbolo C" funciona bien para las bibliotecas, pero para las llamadas al sistema no he logrado lo que quería. Esta idea no está muerta, sin embargo, hacer esto definitivamente me causaría mucho tiempo de pirateo (gcc y/o ld).
  3. Permiso deescalation funciona como un encanto. Podría usar fakeroot o un usuario "invitado" para hacerlo. Este método también es el más fácil de implementar.

que no he probado aún pero definitivamente lo haría en un futuro debido a lo común entre el proyecto y mi trabajo

+1

Parece un gran desafío. Después de todo, casi todos los programas concebibles de C realizarán, legítimamente, llamadas al sistema tales como: asignar memoria, leer y escribir archivos, usar stdin y stdout, y así sucesivamente. –

+0

¿Qué tipo de programa crees que nunca hace una llamada al sistema? Por favor publica un ejemplo. –

+0

@High Performance Mark: Bueno, solo quiero bloquear algunas llamadas al sistema porque son limitadas, llamadas como fork(), open(), ... @Neil Butterworth: Quise decir que no todas las llamadas al sistema son maliciosas llamadas al sistema, por ejemplo execv() podría usarse para ejecutar un script BASH que borra mis datos en el disco. –

Respuesta

8

Como han señalado otros, es imposible que un programa evite realizar llamadas al sistema, sino que comparten la biblioteca C en todo el lugar.

Sin embargo, puede hacer algunos avances con el uso cuidadoso del mecanismo LD_PRELOAD, si su plataforma lo admite (por ejemplo, Linux): usted escribe una biblioteca compartida con los mismos nombres de símbolos en la biblioteca C, que llamado en lugar de las funciones de libc previstas. (Por ejemplo, cerca eléctrica está construido como una biblioteca compartida en sistemas e intercepta basadas en Debian llama a malloc, free et al.)

sospecho que podría utilizar este mecanismo para atrapar o argumento de comprobación de las llamadas a las funciones de libc no te gusta, y tal vez anotar aquellas que consideras incondicionalmente seguras. Entonces podría ser razonable escanear el ejecutable compilado para el código correspondiente a INT 80 para interceptar cualquier intento de hacer syscalls sin procesar (0xcd 0x80 - aunque tenga cuidado con los falsos positivos). Sin embargo sólo he dar a este un momento de pensamiento, que fácilmente podría haber perdido algo o esto puede llegar a ser poco práctico ...

+0

Brillante idea sobre el tipo de anulación del símbolo C! De esta forma pude controlar y detectar totalmente las llamadas con malos propósitos. Definitivamente voy a probar esto así como las otras dos formas (escanear el archivo de origen y la desescalación de permiso) y ver cuál es el más apropiado. –

+1

Acabo de pensar en un ejemplo más detallado de la potencia de este mecanismo: echa un vistazo al paquete 'fakeroot'. – crazyscot

+3

Es posible ejecutar un syscall sin hacer uso de ninguna biblioteca, por lo que si espera construir un sistema seguro contra usuarios maliciosos, usar LD_PRELOAD no es suficiente. –

1

sólo para demostrar que esto no es posible, el siguiente programa:

int main() { 
    return 0; 
} 

hace más de 20 llamadas al sistema como se indica mediante strace. Las llamadas incluyen open (dos veces), que es una de las llamadas que parece querer bloquear.

+0

Gracias, pero quise decir algunos específicos, no todos. –

3

No puede.

Incluso este programa:

#include <stdio.h> 

int main() 
{ 
    printf("Hello, World\n"); 
    return 0; 
} 

hace al menos una llamada al sistema (para enviar la cadena "Hola, mundo \ n" a la salida estándar). Las llamadas al sistema son la única forma en que un programa puede interactuar con el mundo exterior. Use el modelo de seguridad del sistema operativo para seguridad.

Editado por este comentario:

no implica todas las llamadas al sistema, pero las llamadas al sistema, por ejemplo maliciosos execv() podría usarse para ejecutar un script BASH que borra mis datos en el disco.

Su sistema operativo ya incluye mecanismos para evitar este tipo de cosas. Por ejemplo, para que un script bash elimine sus datos, el proceso ya debe tener acceso de escritura a esos datos. Eso significa que debe haber sido iniciado por usted o por la raíz. Su única opción real es no instalar software no confiable.

Por cierto, dependiendo de su plataforma, la ejecución no es necesariamente una llamada al sistema. En Linux, es un contenedor de biblioteca C para la llamada al sistema real (execve).

+0

Ahora que menciona el permiso del sistema de archivos, descubro una solución para eso al ejecutarlo como un usuario personalizado que no tiene el permiso de escritura en ese directorio. Casi me olvido de eso. Gracias :-D. –

1

Bueno, si solo quiere bloquear llamadas específicas, ¿por qué no simplemente hacer un grep a través del código fuente antes de intentar compilarlo? Y rechace los programas que usan las inseguras llamadas al sistema.

+0

¿Podría hacerlo automáticamente, usando un programa que escanea el código fuente? Por ejemplo, tengo un archivo fuente que incluye el "unistd.h "que podría dar lugar a situaciones maliciosas, y otro archivo fuente que tiene una estructura llamada" unistd "que tiene un campo llamado" .h ", ¿cómo puedo diferenciar las circunstancias? –

+0

Ciertamente puede hacerlo automáticamente. Comenzaría con grep o sed, pero si esa no es su taza de té, cualquier lenguaje de programación común, especialmente uno con buenas capacidades de expresión regular, lo haría. –

+0

Sí, eso probablemente funcionaría, aunque tengo que diseñar cuidadosamente el algoritmo para escanear usando grep o Perl en línea, o restricción de la convención de nomenclatura del código fuente. Gracias :-). –

4

Puede ejecutar el programa compilado bifurcándolo desde un contenedor y utilizar el recurso Linux ptrace (2) para interceptar e inspeccionar todas las llamadas al sistema invocadas por el programa.

El siguiente código de ejemplo muestra un contenedor que ejecuta el comando/usr/bin/w, imprime cada llamada al sistema invocada por el comando y finaliza el comando si intenta invocar la llamada al sistema write (2).

 
#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/ptrace.h> 
#include <sys/wait.h> 
#include <sys/syscall.h> 
#include <sys/reg.h> 

#define BAD_SYSCALL __NR_write 

int main(int argc, char *argv) 
{ 
    pid_t child; 
    int status, syscall_nr; 

    child = fork(); 
    if (child == 0) { 
     /* In child. */ 
     ptrace(PTRACE_TRACEME, 0, NULL, NULL); 
     execl("/usr/bin/w", NULL, NULL); 
     // not reached 
    } 

    /* In parent. */ 
    while (1) { 
     wait(&status); 

     /* Abort loop if child has exited. */ 
     if (WIFEXITED(status) || WIFSIGNALED(status)) 
      break; 

     /* Obtain syscall number from the child's process context. */ 
     syscall_nr = ptrace(PTRACE_PEEKUSER, child, 4 * ORIG_EAX, NULL); 
     printf("Child wants to execute system call %d: ", syscall_nr); 

     if (syscall_nr != BAD_SYSCALL) { 
      /* Allow system call. */ 
      printf("allowed.\n"); 
      ptrace(PTRACE_SYSCALL, child, NULL, NULL); 
     } else { 
      /* Terminate child. */ 
      printf("not allowed. Terminating child.\n"); 
      ptrace(PTRACE_KILL, child, NULL, NULL); 
     } 
    } 

    exit(EXIT_SUCCESS); 
} 

Se pueden hacer cosas mucho más potentes utilizando ptrace, como inspeccionar y cambiar un espacio de direcciones del proceso (por ejemplo, para obtener y modificar los parámetros pasados ​​a una llamada al sistema).

Una buena introducción se puede encontrar en este Linux Journal Article y su follow-up.

Cuestiones relacionadas