È sicuro deserializzare i dati forniti dall'utente?

12

AppHarbor ha un post sul blog contenente un codice C # di esempio che legge i dati di un cookie non firmato e lo passa attraverso la serializzazione binaria .Net

È sicuro?

Ovviamente, i dati sono completamente manomettibili.

Tuttavia, ci sono dei rischi nel passaggio dell'input controllato dagli attaccanti al deserializzatore?
Ciò potrebbe comportare l'esecuzione di codice?

    
posta SLaks 05.04.2012 - 05:21
fonte

5 risposte

9

Non ne so abbastanza della serializzazione di C # per sapere se si tratta di un rischio per la sicurezza, ma posso dirti questo:

In Java, non è sicuro creare una nuova statistica dei dati non attendibili. Ci sono un certo numero di sottili insidie nella sicurezza che possono davvero rovinarti. Se sei a livello di guru, puoi probabilmente evitare le insidie, ma uno sviluppatore medio probabilmente non ha idea dei pericoli.

Ecco alcuni esempi di punti sottili:

Se comprendi pienamente tutto ciò e riesci a tenerlo in testa, potresti essere un buon candidato per scrivere un codice di deserializzazione sicuro in grado di gestire in modo sicuro flussi di byte non attendibili. D'altra parte, se sei un semplice mortale (come me) che alza le mani in disgusto per l'intera faccenda, allora potrebbe essere prudente assicurarsi di non deserializzare mai dati non fidati.

Nell'esempio che hai di deserializzare un valore del cookie, ecco una difesa plausibile che potresti usare per assicurarti di non deserializzare un flusso di byte non attendibile. Durante la generazione del cookie, è possibile calcolare un codice di autenticazione del messaggio (MAC) sul nome e sul valore del cookie e aggiungerlo al valore del cookie. Quando si riceve il cookie, è possibile verificare se il MAC è valido. Il tuo server dovrebbe generare una chiave MAC casuale e archiviarla in modo sicuro, ma non ha bisogno di condividere questo valore segreto con nessun altro (a parte tutti i server che servono le richieste).

    
risposta data 06.04.2012 - 07:44
fonte
9

So che questo è davvero, molto tardi nel gioco, ma , esiste una vulnerabilità specifica che riguarda direttamente la deserializzazione dei dati non attendibili. Funziona allo stesso modo dei cosiddetti attacchi "PHP Object Injection" - abusando di classi che eseguono azioni nelle fasi di finalizzazione e smaltimento.

Considera la seguente classe:

[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 {}
    }
}

Ora, immagina ci sia una classe serializzabile davvero noiosa chiamata Message che contiene solo alcune stringhe. Niente di veramente interessante. Stai utilizzando questa classe per trasmettere messaggi sulla rete, in questo modo:

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);
    }
}

Sembra innocuo, no? Ma cosa succede se un utente malintenzionato passa una versione serializzata binaria di un oggetto TempFile sulla rete, anziché l'oggetto Message previsto?

Quattro cose accadono:

  1. I dati vengono deserializzati in un'istanza dell'oggetto da BinaryFormatter.
  2. L'oggetto restituito viene lanciato senza successo su un oggetto Message, generando un'eccezione cast.
  3. Il metodo termina senza utilizzare l'oggetto restituito, lasciando la rappresentazione interna con zero riferimenti nell'albero del GC.
  4. Il garbage collector dispone dell'oggetto.

Perché è sfruttabile? Bene, quando la logica di finalizzazione viene chiamata dal GC, il distruttore chiama TempFile.Dispose() ed elimina il file specificato dal campo _fileName . Poiché il valore di quel campo è impostato all'interno del blob serializzato binario, l'utente malintenzionato può controllarlo. Metti tutto insieme e hai un bug di eliminazione file arbitrario.

Questo sembra un po 'eccessivo, ma spesso troverai query SQL e tutti i tipi di altri extra in Dispose() o distruttori di classi serializzabili.

Modifica: ho fatto una rapida ricerca dell'origine di riferimento e ho scoperto che System.CodeDo m.Compiler.TempFileCollection contiene esattamente il problema che ho mostrato sopra: puoi riempirlo con un elenco di file e dopo la distruzione li cancella tutti.

    
risposta data 04.11.2013 - 22:14
fonte
2

A parte l'ovvia debolezza di manomissione, non c'è niente di sbagliato nel farlo in questo modo. Basta non fidarsi dei dati dal client.

Per quanto ne so non ci sono exploit di esecuzione del codice in sospeso delle librerie di serializzazione .NET.

Durante un ulteriore esame ho notato che l'oggetto è deserializzato in IDictionary non Dictionary<string,object> è concepibile che un utente malintenzionato possa creare un oggetto che implementa IDictionary ma che contiene codice arbitrario. Non ne so abbastanza sull'implementazione di serializaiton binario di .NET per sapere se questo avrebbe avuto immediatamente successo, ma a seconda dell'implementazione è fattibile.

    
risposta data 05.04.2012 - 20:19
fonte
2

La serializzazione è un potenziale vettore di attacco? Sì. È in questo caso? Nnnnnyesssssno. Penso che richiederebbe una vulnerabilità nel BinaryFormatter che al momento non esiste (almeno, pubblicamente).

La vulnerabilità esisterebbe nel codice che consuma l'IDictionary dopo che è stata deserializzata.

Penso che questo sia un codice scritto male, ma non del tutto dannoso in quanto la vulnerabilità esisterebbe nel codice chiamante. Nel caso in cui qualcosa di diverso da IDictionary<string, object> sia deserializzato, il cast non genererà un'eccezione. Sarà semplice restituire un valore nullo. Un cast come

thing2 foo = thing1 as thing2

restituirà null. Un cast come

thing2 foo = (thing2)thing1

genererà un'eccezione cast.

Anche questo object deve essere serializzabile, quindi sei limitato a cosa è serializzabile in quel momento nel CLR così come ciò che l'assembly / app sa essere serializzabile (e ciò vale anche per l'implementazione concreta di IDictionary<> ) . Quindi l'oggetto all'interno del dizionario deve essere gettato su qualcosa di utile o una riflessione dovrebbe essere utilizzata per ottenere i dati da esso. Fino a quando l'oggetto non viene effettivamente consumato, il codice all'interno di Dictionary<> o object non viene eseguito.

    
risposta data 06.04.2012 - 02:04
fonte
2

Il consiglio di Microsoft consiste nell'utilizzare DataContractSerializer. Questo articolo (PDF) illustra le vulnerabilità di serializzazione e le mitigazioni sul framework .NET. Inoltre, evita di utilizzare Remoting poiché utilizza BinaryFormatter.

    
risposta data 08.11.2015 - 23:03
fonte

Leggi altre domande sui tag