2008-09-04 7 views
9

Actualmente estamos creando una aplicación que ejecuta varias herramientas externas. A menudo tenemos que pasar la información ingresada en nuestro sistema por los usuarios a estas herramientas.Prevención de ataques de inyección en la línea de comandos

Obviamente, esta es una gran pesadilla de seguridad esperando a suceder.

Desafortunadamente, todavía no hemos encontrado ninguna clase en .NET Framework que ejecute programas de línea de comandos mientras que proporciona el mismo tipo de protección contra los ataques de inyección que los objetos de IDbCommand para las bases de datos.

En este momento, estamos usando una sustitución de cadenas muy primitiva que sospecho que es más bien insuficiente:

protected virtual string Escape(string value) 
{ 
     return value 
     .Replace(@"\", @"\\") 
     .Replace(@"$", @"\$") 
     .Replace(@"""", @"\""") 
     .Replace("`", "'") 
     ; 
} 

Qué hacen ustedes para prevenir ataques de inyección de línea de comandos? Estamos planeando implementar una expresión regular que sea muy estricta y solo permita el paso de un subconjunto muy pequeño de personajes, pero me preguntaba si habría una forma mejor.

Algunas aclaraciones:

  • Algunas de estas herramientas no tienen APIs podemos programar en contra. Si lo hicieran, no estaríamos teniendo este problema.
  • Los usuarios no eligen herramientas para ejecutar, sino que ingresan metadatos que las herramientas que hemos elegido usan (por ejemplo, inyectando metadatos como notificaciones de derechos de autor en archivos de destino).

Respuesta

5

¿Está ejecutando los programas directamente o pasando por el shell? Si siempre ejecuta un programa externo dando el nombre de la ruta completa al ejecutable y dejando el shell fuera de la ecuación, entonces no es realmente susceptible a ningún tipo de inyección de línea de comando.

EDITAR: DrFloyd, el caparazón es responsable de evaluar cosas como el retroceso. Sin shell, sin evaluación de shell. Obviamente, todavía debe estar al tanto de posibles errores de seguridad en los programas a los que llama, pero no creo que esta pregunta sea sobre eso.

0

Bueno, si puede invocar las herramientas mediante programación sin la línea de comandos, esa sería probablemente su mejor opción. De lo contrario, podría ejecutar la herramienta de línea de comando a través de un usuario que no tiene absolutamente ningún acceso para hacer nada (excepto tal vez un único directorio con el que no pueden hacer daño) ... aunque eso puede terminar rompiendo la herramienta, dependiendo en lo que hace la herramienta

Solo tenga en cuenta que nunca he tenido que enfrentar este problema, porque nunca he tenido que invocar una herramienta de línea de comandos desde una aplicación externa donde la herramienta requiere la entrada del usuario.

0

Hmmm ...

Parece que usted tiene una lista de comandos válidos que los usuarios son capaces de ejecutar. Pero no quieres que los ejecuten todos.

Puede tratar de tomar la línea de comando real y verificar que el archivo exista al menos en la ubicación "segura".

También podría resolver el problema con más interfaz, proporcionar un menú desplegable de comandos y parámetros que podrían usar. Es más trabajo por su parte, pero finalmente ayuda a los usuarios.

0

¿Está ejecutando los programas directamente o pasando por el shell? Si siempre ejecuta un programa externo dando el nombre de la ruta completa al ejecutable y dejando el shell fuera de la ecuación, entonces no es realmente susceptible a ningún tipo de inyección de línea de comando.

@Curt Hagenlocher El acento grave puede matar. Si el sistema de Windows está configurado "incorrecto", o el sistema Unix lo permite, un directorio &bt; del * &bt; ejecutará primero el comando del * y luego usará el resultado en lugar del del *, que en este caso, no importará porque no hay nada que dir (o ls)

2

cuando Process.Start un nuevo proceso, proporcione los parámetros en su argumento Parámetros en lugar de construir toda la línea de comando usted mismo.

No tengo tiempo para una prueba adecuada, pero creo que debería ayudar a protegerla en algún nivel.

Probaremos esto mañana.

EDITAR: Ah, alguien me golpeó de nuevo. Pero aquí hay otro punto: intente usar Console.InputStream (no puede recordar el nombre exacto) para suministrar datos en lugar de pasar parámetros, ¿es esa una posible solución? como arreglar el comando para que lea desde el dispositivo CON y luego suministrar los datos a través del flujo de entrada en su lugar.

2

En C++ en de Windows, que acaba de escape \ y "cuando sea necesario, cita el argumento y ShellExecute Entonces, todo dentro de las comillas deben ser tratados como texto

Esto debería ilustrar:..


#include <iostream> 
#include <string> 
#include <windows.h> 
#include <cstdlib> 
using namespace std; 

// Escape and quote string for use as Windows command line argument 
string qEscape(const string& s) { 
    string result("\""); 
    for (string::const_iterator i = s.begin(); i != s.end(); ++i) { 
     const char c = *i; 
     const string::const_iterator next = i + 1; 
     if (c == '"' || (c == '\\' && (next == s.end() || *next == '"'))) { 
      result += '\\'; 
     } 
     result += c; 
    } 
    result += '"'; 
    return result; 
} 

int main() { 
    // Argument value to pass: c:\program files\test\test.exe 
    const string safe_program = qEscape("c:\\program files\\test\\test.exe"); 
    cout << safe_program << " "; 

    // Argument value to pass: You're the "best" around. 
    const string safe_arg0 = qEscape("You're the \"best\" around."); 

    // Argument value to pass: "Nothing's" gonna ever keep you down. 
    const string safe_arg1 = qEscape("\"Nothing's\" gonna ever keep you down."); 

    const string safe_args = safe_arg0 + " " + safe_arg1; 
    cout << safe_args << "\n\n"; 

    // c:\program files\test\ to pass. 
    const string bs_at_end_example = qEscape("c:\\program files\\test\\"); 
    cout << bs_at_end_example << "\n\n"; 

    const int result = reinterpret_cast<int>(ShellExecute(NULL, "open", safe_program.c_str(), safe_args.c_str(), NULL, SW_SHOWNORMAL)); 
    if (result < 33) { 
     cout << "ShellExecute failed with Error code " << result << "\n"; 
     return EXIT_FAILURE; 
    } 
} 

Pero, con cualquier método que utilice, debe probar el infierno fuera de él para ver que realmente evitar la inyección.

1

no utilice una lista negra para la prevención de las inyecciones. Si hay n formas de inyectar código, usted pensará en n - m donde m> 0.

Utilice una lista blanca de parámetros aceptados (o patrones). Es mucho más restrictivo por naturaleza, pero esa es la naturaleza de la seguridad.

Cuestiones relacionadas