Leggere il frame di memoria fisica precedentemente posseduto da un altro processo per leggere il contenuto della sua pagina di memoria

26

Ho avuto una conversazione con @ anger32 che afferma che l'azzeramento di una cornice di memoria fisica durante il passaggio della pagina sostenuta da il frame ad un altro processo non è responsabilità di sistemi operativi come Windows e Linux (anche se lo fanno, non garantiscono che ciò accada), ma una responsabilità dei sistemi operativi con un certificato che consente di lavorare con informazioni classificate.

È possibile effettuare il seguente attacco su un altro processo (forse più privilegiato)?

  1. mappare abbastanza pagine di memoria e iniziare a consumare abbastanza tempo di CPU per evitare che il thread di azzeramento, che ha la priorità più bassa (almeno su Windows), dal tempo della CPU.

  2. un altro processo mette i dati sensibili nella memoria

  3. si verifica un cambio di contesto

  4. chiediamo al sistema operativo una pagina di memoria, il sistema operativo sfrutta la pagina 'process' e ci dà la nuova pagina supportata dallo stesso frame della pagina senza azzerarla.

  5. eseguiamo la scansione della pagina per i segreti.

Dichiara inoltre che ci sono modi per leggere un altro processo di "memoria" con mmap , i suoi flag e gli indirizzi fisici su Linux. Conosci qualche? È davvero possibile ottenere una memoria di un altro processo su Linux, ad esempio memoria del processo di un altro utente o dominio SELinux? Se lo sono sembra una vulnerabilità molto pericolosa.

    
posta KOLANICH 13.01.2016 - 12:50
fonte

7 risposte

23

In Linux, i processi sono in grado di leggere un'altra memoria di processo quando si verifica una delle seguenti condizioni:

  1. Il processo ha avuto il permesso di root o può leggere /proc/$PID/mem o /dev/mem , per impostazione predefinita /proc/$PID/mem e /dev/mem sono accessibili solo da root
  2. Il processo padre può fork() / clone() in modo tale da consentirgli di leggere parte o tutta la memoria dei suoi processi figli
  3. Un processo genitore può biforcarsi un bambino in modo tale da consentire al processo figlio di leggere alcuni o tutta la memoria del genitore
  4. Un processo può impostare un'area di memoria condivisa

Un processo non può leggere o scrivere nella memoria di un processo arbitrario, non correlato a meno che il processo non funzioni con privilegi elevati. In tutti gli altri casi, avresti bisogno di essere il processo genitore o il processo di destinazione ha dovuto impostare deliberatamente un'area di memoria condivisa.

Il processo genitore può accedere a una memoria di processo secondaria è la caratteristica che definisce la maggior parte dei sistemi Unix. Nei sistemi Unix (incluso Linux), il nuovo processo viene creato utilizzando la chiamata di sistema fork() . fork() crea una copia del processo esistente creando una nuova voce nella tabella dei processi del sistema operativo e imposta la nuova memoria virtuale del processo come copia sulla scrittura della memoria virtuale del genitore. Ciò significa che il nuovo processo può leggere la memoria del genitore, ma a questo punto, il nuovo processo sta ancora eseguendo il codice del genitore, quindi questo non è un problema di sicurezza. Il nuovo processo può quindi chiamare una delle chiamate di sistema exec*() per rimappare un nuovo file eseguibile nella propria memoria e passare al simbolo di avvio di quel file eseguibile. La rimappatura significa che ora l'unica voce nella tabella di paging sulla memoria virtuale del nuovo processo è il nuovo eseguibile. Quando il nuovo processo tenta di leggere / scrivere nell'area rimappata, causerà un errore di pagina e il sistema operativo impaginerà la parte corrispondente del file eseguibile che era exec*() -ed in memoria. Se il nuovo processo tenta di leggere / scrivere su un'area di memoria non mappata, ciò causerà un errore di segmentazione. Negli usi più avanzati di fork ed exec, un processo può eseguire il fork e quindi mappare la memoria del processo figlio in modo tale che tutte o parte della memoria del bambino sarà accessibile dal genitore dopo exec*() .

In Linux, quando un processo alloca memoria (ad esempio usando malloc) dal sistema operativo, il processo chiama mmap() per allocare la mappa anonima. La mappa anonima viene servita dalla RAM o dallo swap. mmap anonimo è riempito a zero dal kernel, a meno che il processo non richieda MAP_UNINITIALIZED , che è onorato solo su sistemi embedded molto limitati per ragioni di prestazioni, il kernel doveva essere compilato per consentirlo.

Inoltre, per scenari di elevata sicurezza, Linux consente a un processo di richiedere l'eliminazione totale o parziale di parte della sua memoria utilizzando mlock e / o MAP_LOCKED. La memoria bloccata viene sempre servita dalla RAM e viene solitamente utilizzata per impedire che le chiavi di crittografia e la memoria utilizzate per la decrittografia vengano scambiate in memoria permanente.

    
risposta data 13.01.2016 - 13:37
fonte
23

Ero curioso di ciò anch'io una volta, e scrissi un piccolo programma sotto linux che malloc'ad tutta la memoria disponibile e l'ho scaricato su disco.

Si è scoperto che era tutto azzerato prima che fosse consegnato alla mia applicazione.

Successivamente, ho anche controllato il codice del kernel, e ho potuto confermare che era il kernel a farlo.

-

Penso che abbia perfettamente senso che è responsabilità del sistema operativo assicurarsi che i vecchi dati non siano resi disponibili per un altro processo. In quale altro luogo implementeresti tale misura di sicurezza?

Modifica:

Non ricordo se ho provato contro la memoria SWAP. A causa dell'IO del disco necessario per azzerare lo spazio su disco assegnato (memoria), potrebbe essere implementato in modo diverso.

    
risposta data 13.01.2016 - 13:45
fonte
9

L'attacco che descrivi non funziona su Windows. Starving il thread di azzeramento della pagina non impedisce l'azzeramento, ma lo ritarda. L'esistenza dell'attività di background di azzeramento della pagina è un ottimizzazione delle prestazioni.

In sostanza, un ingenuo gestore di memoria con una garanzia di privacy funziona così:

  • riserva una pagina dalla lista libera
  • azzeralo
  • renderlo disponibile al codice dell'applicazione (imposta la voce della tabella di pagina per consentire l'accesso all'anello 3)

La versione ottimizzata sembra più simile a

  • ottiene una pagina dalla lista azzerata, se ce n'è una
  • se il primo passaggio è riuscito, passa all'ultimo passaggio
  • riserva una pagina dalla lista libera
  • azzeralo
  • renderlo disponibile al codice dell'applicazione (imposta la voce della tabella di pagina per consentire l'accesso all'anello 3)

L'esaurimento del thread di azzeramento provocherà una allocazione lenta, poiché l'azzeramento non è ancora stato eseguito. Non causerà perdite di dati, perché la struttura dei dati mantiene le pagine azzerate e le pagine rimanenti segregate e quando l'allocatore esaurisce le pagine pre-azzerate, deve eseguire l'azzeramento just-in-time.

    
risposta data 13.01.2016 - 23:26
fonte
5

È assolutamente possibile leggere la memoria di un altro processo, ma questo è possibile solo con privilegi amministrativi e ovviamente il sistema operativo non consentirà a nessun processo di accedere a qualsiasi spazio della memoria che non è assegnato a quel processo.

Per gli utenti amministrativi questo è ovviamente possibile. Ad esempio, in Windows questa funzionalità viene implementata per impostazione predefinita per eseguire il debug di un processo. Puoi farlo utilizzando il Task Manager come descritto qui .

Ma è anche possibile scaricare l'intera memoria, inclusi tutti i processi e tutto ciò che è memorizzato in memoria in quel momento. Non è più così semplice perché i sistemi Windows non forniscono questa funzionalità di default. Per fare ciò l'applicazione carica i propri driver di memoria che consentono loro di accedere direttamente alla memoria e non attraverso il sistema operativo.

Sui vecchi sistemi Linux siamo in grado di scaricare la memoria direttamente attraverso il dispositivo di memoria nella partizione /dev . Questo non è più possibile ma ci sono moduli del kernel che consentono di scaricare anche l'intera memoria. Ciò richiede anche i privilegi di root. Puoi anche farlo manualmente per un processo come descritto qui .

// EDIT : ho appena chiesto a uno sviluppatore senior con 40 anni di esperienza al riguardo. La risposta è: si basa su vari fattori. Mi ha detto che in C ++ e Java le variabili sono inizializzate, il che significa che un'applicazione che è scritta in C ++ o Java non sarà in grado di ottenere le vecchie informazioni perché viene sovrascritta inizializzando quella variabile. Ma C non lo fa, quindi potrebbe essere possibile, ma poi si basa ancora sul sistema operativo.

    
risposta data 13.01.2016 - 12:59
fonte
2

Tecnicamente, un sistema operativo potrebbe riciclare pagine da processi che hanno lo stesso contesto di sicurezza, perché qualsiasi informazione che il nuovo processo potrebbe raccogliere da quella sarebbe anche accessibile direttamente al processo.

Ciò è tuttavia del tutto inattuabile, poiché il contesto di sicurezza di un processo può cambiare nel tempo e quando i privilegi vengono eliminati (che è un modello comune), i dati contenuti nel processo devono ancora essere protetti da chiunque abbia meno accesso autorizzazioni rispetto al set di privilegi originale.

Dato che i privilegi possono anche essere abbastanza dettagliati, lo sforzo di tenere traccia di quali processi può essere assegnato a una pagina senza prima averlo rimosso è significativamente più alto che semplicemente cancellare ogni pagina restituita al sistema operativo, specialmente come memoria del computer l'architettura favorisce pesantemente le scritture sequenziali di grandi dimensioni.

Alcune architetture integrate utilizzano anche il controller DMA per questa attività, riducendo il tempo della CPU a pochi cicli per configurare il controller e riconoscere il completamento.

Se un processo può presumere che le pagine appena mappate siano deselezionate è un contratto tra esso e il sistema operativo, ma in genere questo non è scontato, e di nuovo ci sono di solito poche ragioni per farlo, perché i processi devono ancora tenere traccia di quali dati all'interno del loro spazio indirizzo considerano validi, e lo farebbero solo per tutto ciò che contiene realmente informazioni.

Se un'attività esegue il mapping, modifica e unmap delle pagine rapidamente, ciò aumenterà il carico del sistema e i processi potrebbero essere sospesi in attesa di un processo a bassa priorità per cancellare una pagina. I sistemi operativi devono fare attenzione a non inavvertitamente introdurre una inversione di priorità qui, aumentando temporaneamente la priorità dell'attività di azzeramento a quella dell'attività con la priorità più alta che tenta di mappare una pagina.

    
risposta data 13.01.2016 - 23:37
fonte
1

La maggior parte dei sistemi operativi deve essere certificata da utilizzare per determinati scopi / in alcune organizzazioni. La maggior parte utilizza il framework Common Criteria a diversi livelli di garanzia e alcuni livelli richiedono che il sistema operativo cancelli una pagina prima di passarla a un altro processo. Un riferimento indiretto a questo requisito, che afferma:

One reason zero-initialized pages are required is to meet various security requirements, such as the Common Criteria. Most Common Criteria profiles specify that user-mode processes must be given initialized page frames to prevent them from reading a previous process’s memory contents. Therefore, the memory manager gives user-mode processes zeroed page frames unless the page is being read in from a backing store. If that’s the case, the memory manager prefers to use nonzeroed page frames, initializing them with the data off the disk or remote storage.

(da Windows Internals, seconda parte dell'articolo 6 di Mark E. Russinovich, David A. Solomon, Alex Ionescu. Copyright © 2012 di David Solomon e Mark Russinovich)

L'attivazione di tale funzione richiede spesso che l'architettura di gestione della memoria sia progettata di conseguenza e non ha senso escludere questa funzione in una versione "civile" / non sicura per ottenere guadagni di prestazioni non ovvi.

In particolare, è importante che le pagine del kernel e le pagine di altri utenti non vengano divulgate a processi non privilegiati, per cui dovranno essere chiariti ad un certo punto. Inoltre, è inefficiente che una pagina venga azzerata a meno che non superi effettivamente il limite massimo (nel caso in cui una pagina venga allocata allo stesso processo / kernel). Quindi l'unico momento ragionevole per farlo è al momento dell'assegnazione e dal momento che il ricevente non può essere considerato responsabile per fare ciò, il sistema operativo dovrà assumersi la responsabilità. (Naturalmente, il sistema operativo del mondo reale adotterà tutti i tipi di ottimizzazioni per ridurre il ritardo di allocazione delle pagine.)

    
risposta data 15.01.2016 - 18:57
fonte
-3

L'unico modo funzionante per eseguire ciò di cui stai parlando è un Cold Boot Attack , quando stai distruggendo il kernel spegnendolo. O puoi provare a giocare con kexec() di chiamate, ma non funzionerà nella maggior parte dei casi.

    
risposta data 13.01.2016 - 17:28
fonte

Leggi altre domande sui tag