¿Es seguro deserializar los datos proporcionados por el usuario?

12

AppHarbor tiene una publicación de blog que contiene código C # de muestra que lee datos de una cookie sin firmar y la pasa a través de la serialización binaria .Net.

¿Eso es seguro?

Obviamente, los datos son completamente manipulables.

Sin embargo, ¿existen riesgos al pasar la entrada controlada por el atacante al deserializador?
¿Podría resultar en la ejecución del código?

    
pregunta SLaks 05.04.2012 - 05:21
fuente

5 respuestas

9

No sé lo suficiente sobre la serialización de C # para saber si es un riesgo de seguridad, pero puedo decirle esto:

En Java, no es seguro deserializar datos no confiables. Hay una serie de trampas de seguridad sutiles que realmente pueden arruinarte. Si tienes un nivel de gurú, probablemente puedas evitar las trampas, pero un desarrollador promedio probablemente no tenga ni idea de los peligros.

Aquí hay algunos ejemplos de puntos sutiles:

Si comprende todo eso y puede mantenerlo en su mente, puede ser un buen candidato para escribir un código de deserialización seguro que pueda manejar de forma segura flujos de bytes no confiables. Por otro lado, si eres un simple mortal (como yo) que echa mano de disgusto por todo el asunto, entonces sería prudente asegurarte de no deserializar datos no fiables.

En el ejemplo que da de deserializar un valor de cookie, aquí hay una defensa plausible que podría usar para asegurarse de que nunca deserialice un flujo de bytes no confiable. Al generar la cookie, puede calcular un código de autenticación de mensaje (MAC) sobre el nombre y el valor de la cookie y adjuntarlo al valor de la cookie. Al recibir la cookie, puede comprobar si el MAC es válido. Su servidor necesitaría generar una clave MAC aleatoria y almacenarla de manera segura, pero no necesita compartir este valor secreto con nadie más (excepto todos los servidores que atienden solicitudes).

    
respondido por el D.W. 06.04.2012 - 07:44
fuente
9

Sé que esto es realmente, muy tarde en el juego, pero sí , existe una vulnerabilidad específica que se relaciona directamente con la deserialización de datos no confiables. Funciona de la misma manera que lo hacen los ataques de "Inyección de objetos PHP", al abusar de las clases que realizan acciones en sus etapas de finalización y eliminación.

Considera la siguiente clase:

[Serializable]
class TempFile : IDisposable
{
    private readonly string _fileName;

    public TempFile()
    {
        _fileName = Path.GetTempFileName();
    }

    ~TempFile()
    {
        Dispose();
    }

    public FileStream GetStream()
    {
        return new FileStream(_fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
    }

    public void Dispose()
    {
        try
        {
            if (File.Exists(_fileName))
                File.Delete(_fileName);
        } catch {}
    }
}

Ahora, imagine que hay una clase serializable realmente aburrida llamada Message que solo contiene algunas cadenas. Nada realmente interesante en absoluto. Estás utilizando esta clase para pasar mensajes a través de la red, de esta manera:

public void ProcessNetworkMessage(byte[] message)
{
    var bf = new BinaryFormatter();
    Message msg = null;
    try
    {
        using (var ms = new MemoryStream(message))
        {
            msg = (Message) bf.Deserialise(ms);
        }
    }
    catch (Exception ex)
    {
        AppLog.WriteLine("Exception: " + ex.Message);
    }
    if (msg != null)
    {
        ProcessMessage(msg);
    }
}

Parece inocuo, ¿no? Pero, ¿qué sucede si un atacante pasa una versión serializada binaria de un objeto TempFile a través de la red, en lugar del objeto Message esperado?

Suceden cuatro cosas:

  1. El BinaryFormatter deserializa los datos en una instancia de objeto.
  2. El objeto devuelto se convierte sin éxito en un objeto de Mensaje, lanzando una excepción de conversión.
  3. El método sale sin usar el objeto devuelto, dejando la representación interna con cero referencias en el árbol GC.
  4. El recolector de basura se deshace del objeto.

¿Por qué es explotable? Bueno, cuando la lógica de finalización es llamada por el GC, el destructor llama a TempFile.Dispose() y borra el archivo especificado por el campo _fileName . Dado que el valor de ese campo se establece dentro del blob serializado binario, el atacante puede controlarlo. Póngalo todo junto y tendrá un error de eliminación de archivo arbitrario.

Esto parece un poco exagerado, pero a menudo encontrarás consultas SQL y todo tipo de otras cosas en Dispose() o destructores de clases serializables.

Editar: Hice una búsqueda rápida de la fuente de referencia, y resulta que System.CodeDo m.Compiler.TempFileCollection contiene exactamente el problema que mostré anteriormente. Puedes llenarlo con una lista de archivos y luego de destruirlo. los elimina a todos.

    
respondido por el Polynomial 04.11.2013 - 22:14
fuente
2

Aparte de la evidente debilidad de manipulación, no hay nada de malo en hacerlo de esta manera. Simplemente no confíe en los datos del cliente.

Por lo que sé, no hay explotaciones de ejecución de código pendientes de las bibliotecas de serialización .NET.

En un examen más a fondo, observé que el objeto se deserializa a IDictionary no Dictionary<string,object> . Es posible que un atacante pueda crear un objeto que implemente IDictionary pero contenga código arbitrario. No sé lo suficiente sobre la implementación de .NET de la serialización binaria para saber si esto sería un éxito inmediato, pero dependiendo de la implementación es factible.

    
respondido por el pdubs 05.04.2012 - 20:19
fuente
2

¿Es la serialización un vector de ataque potencial? Sí. ¿Es en este caso? Nnnnnyesssssno. Creo que requeriría una vulnerabilidad en el BinaryFormatter que no existe actualmente (al menos, públicamente).

La vulnerabilidad existiría en el código que consume el IDictionary después de que se haya deserializado.

Creo que este es un código mal escrito, pero no tan perjudicial como la vulnerabilidad podría existir en el código de llamada. En el caso de que se deserialice algo que no sea IDictionary<string, object> , el elenco no lanzará una excepción. Simplemente devolverá un valor nulo. Un reparto como

thing2 foo = thing1 as thing2

devolverá nulo. Un reparto como

thing2 foo = (thing2)thing1

lanzará una excepción de lanzamiento.

Ese object también debe ser serializable, por lo que está limitado a lo que actualmente se puede serializar en el CLR, así como lo que el ensamblado / aplicación sabe que es serializable (y eso también se aplica a la implementación concreta del IDictionary<> ) . Entonces ese objeto dentro del diccionario debe ser convertido en algo útil o la reflexión debería usarse para obtener datos de él. Sin embargo, hasta que ese objeto se consuma realmente, no se ejecuta el código dentro de Dictionary<> o object .

    
respondido por el Steve 06.04.2012 - 02:04
fuente
2

El consejos de Microsoft es usar DataContractSerializer. Este artículo (PDF) describe las vulnerabilidades de serialización y las mitigaciones en el marco .NET. Además, evite usar Remoting ya que usa BinaryFormatter.

    
respondido por el Chui Tey 08.11.2015 - 23:03
fuente

Lea otras preguntas en las etiquetas