Pregunta

Actualmente estamos creando una aplicación que ejecuta una serie de herramientas externas.A menudo tenemos que pasar información ingresada en nuestro sistema por los usuarios a estas herramientas.

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

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

En este momento, estamos usando una sustitución de cadenas muy primitiva que sospecho que es bastante 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 caracteres, pero me preguntaba si habría una manera mejor.

Algunas aclaraciones:

  • Algunas de estas herramientas no tienen API con las que podamos programar.Si lo hicieran, no estaríamos teniendo este problema.
  • Los usuarios no eligen herramientas para ejecutar, ingresan metadatos que utilizan las herramientas que hemos elegido (por ejemplo, inyectando metadatos como avisos de derechos de autor en archivos de destino).
¿Fue útil?

Solución

¿Está ejecutando los programas directamente o pasando por el shell?Si siempre inicia un programa externo dando el nombre de ruta completo 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:Dr. Floyd, el shell es responsable de evaluar cosas como el acento grave.Sin caparazón, sin evaluación de caparazón.Obviamente, aún debes estar al tanto de cualquier posible problema de seguridad en los programas a los que estás llamando, pero no creo que esta pregunta se refiera a eso.

Otros consejos

Cuando usted Proceso.Inicio un nuevo proceso, proporcione los parámetros en su argumento Parámetros en lugar de crear usted mismo toda la línea de comando.

No tengo tiempo para una prueba adecuada, pero creo que eso debería ayudar a protegerlo hasta cierto punto.

Lo probaré mañana.

EDITAR: Ah, alguien se me adelantó otra vez.Pero aquí hay otro punto:Intente usar Console.InputStream (no recuerdo el nombre exacto) para proporcionar datos en lugar de pasar parámetros, ¿es esa una posible solución?como arreglar el comando para que se lea desde el dispositivo CON y luego proporcione los datos a través del flujo de entrada.

En C++ en ventanas, simplemente escapa \ y " cuando sea necesario, cita el argumento y ejecuta Shell.Luego, todo lo que esté dentro de las comillas debe tratarse 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 uses, debes probarlo a fondo para ver si realmente previene la inyección.

No utilice una lista negra para prevenir inyecciones.Si hay norte formas de inyectar código, pensarás en norte - metro dónde metro > 0.

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

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

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

Mmm...

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

Puede intentar 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, proporcionando un menú desplegable de comandos y parámetros que podrían usar.Es más trabajo por su parte, pero en última instancia ayuda a los usuarios.

¿Está ejecutando los programas directamente o pasando por el shell?Si siempre inicia un programa externo dando el nombre de ruta completo 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 La comilla invertida puede matarte.Si el sistema Windows está configurado "incorrectamente" o el sistema Unix lo permite, un directorio &bt;del *&bt;Primero ejecutará el comando del * y luego usará la salida en lugar de del *, lo que en este caso no importará porque no hay nada que dir (o ls).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top