Domanda:
Lettura del frame di memoria fisica precedentemente posseduto da un altro processo per leggere il contenuto della sua pagina di memoria
KOLANICH
2016-01-13 17:50:45 UTC
view on stackexchange narkive permalink

Ho avuto una conversazione con @ anger32 che afferma che l'azzeramento di un frame di pagina di memoria fisica quando si passa la pagina supportata da quel frame a un altro processo non è responsabilità di sistemi operativi come Windows e Linux (sebbene lo fanno, non garantiscono che ciò accadrà), ma una responsabilità dei sistemi operativi con un certificato che gli consente di lavorare con informazioni classificate.

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

  1. mappare un numero sufficiente di pagine di memoria e iniziare a consumare abbastanza tempo CPU per evitare che il thread azzerato, che ha la priorità più bassa (almeno su Windows), ottenga tempo CPU.

  2. un altro processo inserisce dati sensibili in memoria

  3. si verifica un cambio di contesto

  4. chiediamo al sistema operativo una pagina di memoria, il sistema operativo elimina la pagina del processo e ci fornisce la nuova pagina supportata dallo stesso frame di pagina senza azzerarlo.

  5. scansioniamo la pagina per i segreti.

Afferma anche che ci sono wa ys leggere la memoria di un altro processo che altera mmap , i suoi flag e indirizzi fisici su Linux. Conosci qualche? È davvero possibile ottenere la memoria di un altro processo su Linux, ad esempio la memoria del processo di un altro utente o dominio SELinux? Se lo sono, sembra una vulnerabilità molto pericolosa.

Le tue piccole modifiche continuano a portarlo in cima alla home page. Quando fai modifiche, ti chiederei di farne un gruppo insieme, piuttosto che uno ogni 30 minuti.
Dubito seriamente che Linux non azzeri la memoria prima di passarla a un nuovo processo. Perché ... se non lo facesse ... allora un processo senza privilegi potrebbe potenzialmente leggere informazioni sensibili appartenenti a un altro processo. Non sono solo i sistemi operativi benedetti da un sigillo di approvazione da alcune società che hanno la responsabilità di proteggere i processi gli uni dagli altri; TUTTI i sistemi operativi dovrebbero farlo e io considererei quelli che non lo fanno come _insani e inadatti all'uso generale_.
Non era questo il punto debole di una versione precedente di Windows?
`il sistema operativo non ha la responsabilità di proteggere la 'memoria' di un processo - Se lo affermasse davvero senza" dopo che il processo è terminato ", dovrebbe iniziare ad apprendere le basi della sicurezza. Se un sistema operativo non lo fa, qualsiasi utente può eseguire qualsiasi codice (o leggere le informazioni per farlo nel caso in cui abbia solo accesso ro).
No, non l'ha fatto, mi dispiace. Risolto il problema. Voleva dire azzerare le pagine di memoria dal sistema operativo.
È abbastanza plausibile che la linea Windows 9x non lo abbia fatto, perché non erano progettate per essere sicure.
@BlacklightShining: Chiamarlo * folle * è un po 'forte, molti sistemi operativi incorporati sono poco più che librerie di compiti e non fanno nulla del genere. Per certo, la sanificazione della memoria liberata prima del riutilizzo è un requisito per qualsiasi sistema operativo * multiutente *. Se in genere lavori su sistemi multiutente, allora sono d'accordo, i sistemi operativi senza questa funzione sono * non adatti all'uso generale *, ma può essere abbastanza sensato usarne uno in casi specializzati in cui non è possibile caricare codice arbitrario che potrebbe fare qualcosa di dannoso con i dati residui.
La memoria della GPU non viene azzerata in modo affidabile (almeno su alcuni sistemi operativi) ei fornitori di GPU sembrano non essere disposti a risolverlo nei loro driver, nonostante siano a conoscenza di quella vulnerabilità di sicurezza da anni.
(la prossima persona che apporta una modifica significativa, rimuovi il tag "sandbox" e considera anche la rimozione del tag "access-control" e l'aggiunta di tag OS / memoria pertinenti; non farlo per ora per non ribattere la domanda seguente Commento di Mike)
Https://charliehorse55.wordpress.com/2016/01/09/how-nvidia-breaks-chrome-incognito/
* "non garantiscono che ciò accadrà *" - citazione / prove necessarie. Questa premessa sembra errata. Sembra che la tua domanda * dovrebbe * essere: garantiscono che la memoria verrà azzerata prima di essere riutilizzata da un altro processo? Non dare per scontato che non sia garantito (a meno che tu non abbia prove del contrario).
@Sebb Per essere onesti, non è necessariamente responsabilità proteggere la memoria del processo, specialmente sui sistemi che non dispongono di una MMU.Spesso è sufficiente proteggere semplicemente il kernel dallo spazio utente anche se qualsiasi processo dello spazio utente può interferire con qualsiasi altro processo dello spazio utente.
Sette risposte:
#1
+24
Dog eat cat world
2016-01-13 18:45:45 UTC
view on stackexchange narkive permalink

Una volta ero curioso di questo e ho scritto un piccolo programma sotto Linux che mallocò tutta la memoria disponibile e la scaricò su disco.

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

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

-

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

Modifica:

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

"In quale altro luogo implementereste una tale misura di sicurezza?" Potrebbe essere implementata da un processo prima di uscire, in cui azzera la memoria per tutti i dati sensibili. Ma ha sicuramente più senso per il sistema operativo gestirlo.
Quando si verifica un cambio di contesto, il processo non è in grado di azzerare la memoria prima del cambio. L'azzeramento manuale viene eseguito per impedire la fuga di dati se qualcuno sfruttato fuori dai limiti legge vuln. Ma può essere [non abbastanza] (http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html).
Qualche indizio se questo è vero anche su Windows o qualsiasi altro sistema operativo? Il fatto che Linux lo faccia non significa che sia una responsabilità del sistema operativo. Potrebbe essere solo una bella caratteristica extra di Linux
Come so, Windows [lo fa] (http://blogs.msdn.com/b/tims/archive/2010/10/29/pdc10-mysteries-of-windows-memory-management-revealed-part-two.aspx ).
P.S. Secondo il post collegato, se ho capito bene, Windows non ha davvero la responsabilità di fornire pagine azzerate.
@KOLANICH Se sto leggendo correttamente (diagramma di flusso e paragrafo successivo) qualsiasi pagina inviata a un'applicazione utente avrà i vecchi dati sovrascritti; il kernel potrebbe essere in grado di vederli. Le pagine libere ma sporche possono essere azzerate e rese disponibili per le normali allocazioni, utilizzate per i file mappati in memoria (in questo caso i vecchi dati vengono sovrascritti dal file prima di essere dati all'applicazione), oppure utilizzati per le allocazioni dal kernel. Quello che accade in quest'ultimo caso non viene discusso: è possibile che il kernel li disinfetti da solo. È anche possibile che il codice a livello di kernel sia implicitamente attendibile (spero che non lo sia).
Le pagine fisiche non vengono azzerate, ma la memoria che vedi è mappata su una pagina azzerata. È probabile che le pagine contengano tutti i dati fino a quando non vengono salvati, il che avviene alla prima scrittura a meno che non venga eseguito il commit esplicito.
@DanNeely: Sperate che il codice a livello di kernel non sia implicitamente attendibile? Beh ... immagino che tu possa dirlo, perché in realtà è ** esplicitamente attendibile **. Il codice del kernel ha accesso illimitato all'intero sistema. Non ha bisogno di prelevare dati da allocazioni non sanificate, perché può curiosare direttamente o persino modificare qualsiasi pagina desidera (come conseguenza diretta della capacità dell'anello 0 di riconfigurare le tabelle delle pagine).
@BenVoigt Le implementazioni di TEE come SGX possono rendere questo meno vero.
#2
+23
Lie Ryan
2016-01-13 18:37:09 UTC
view on stackexchange narkive permalink

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

  1. Il processo aveva i permessi 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 genitore può fork () / clone () in modo tale che gli consenta di leggere parte o tutta la memoria dei suoi processi figli
  3. Un processo genitore può biforcare un figlio in modo tale da consentire al processo figlio di leggere parte o tutta la memoria del genitore
  4. Un processo può impostare un'area di memoria condivisa
  5. Un processo non può leggere o scrivere nella memoria di un processo arbitrario e non correlato a meno che il processo non venga eseguito con privilegi elevati. In tutti gli altri casi, avresti bisogno di essere il processo genitore o il processo di destinazione doveva impostare deliberatamente un'area di memoria condivisa.

    Il fatto che il processo genitore possa accedere alla memoria di un processo figlio è la caratteristica distintiva della 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 memoria virtuale del nuovo processo come copia in 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 saltare al simbolo di inizio 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 cerca di leggere / scrivere nell'area rimappata, causerà un errore di pagina e il sistema operativo pagherà in memoria la parte corrispondente del file eseguibile che era exec * () . Se il nuovo processo tenta di leggere / scrivere in 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 tutta 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 una mappa anonima. La mappa anonima viene fornita dalla RAM o dallo scambio. La mmap anonima viene riempita con zero dal kernel, a meno che il processo non richieda MAP_UNINITIALIZED , che è rispettato solo su sistemi embedded molto limitati per motivi di prestazioni, il kernel doveva essere compilato per consentire questo.

    Inoltre, per scenari ad alta sicurezza, Linux consente a un processo di richiedere che tutta o parte della sua memoria non sia intercambiabile utilizzando mlock e / o MAP_LOCKED. La memoria bloccata viene sempre fornita dalla RAM e viene solitamente utilizzata per impedire che le chiavi di crittografia e la memoria utilizzata per la decrittografia vengano scambiate con l'archiviazione persistente.

Tutte belle informazioni, ma non rilevanti per la domanda, che riguarda se un'attività di race condition l'attività di azzeramento della pagina renda possibile il trasferimento ** implicito ** di dati tra processi. Stai discutendo i trasferimenti ** espliciti **, per i quali sono progettati, sono anche esplicitamente protetti. Garantire canali imprevisti è ovviamente molto più difficile.
@BenVoigt: la domanda si basa sul presupposto implicito che la memoria debba essere azzerata quando vengono impaginate in entrata e in uscita. Quello che succede è un po 'più complicato, in quanto l'allocazione della memoria è in realtà solo una tabella di pagina e un'operazione vmm, il caricamento effettivo della memoria mappata con dati mappati o l'azzeramento avviene durante l'errore di pagina. Durante un errore di pagina, il processo / thread / attività è in uno stato non eseguibile, non verrà pianificato, quindi non possono verificarsi errori di concorrenza. La prima metà della mia risposta cerca di affrontare questo malinteso spiegando come funziona la memoria virtuale nel sistema operativo moderno.
@BenVoigt: Il penultimo paragrafo affronta la vera questione. Secondo la documentazione del kernel, è garantito che tutta la creazione di memoria anonima venga azzerata quando il processo utente la legge subito dopo la mappatura. L'azzeramento effettivo non avviene quando viene eseguito il nuovo processo, ma piuttosto durante le letture della memoria / gli errori di pagina.
#3
+9
Ben Voigt
2016-01-14 04:26:03 UTC
view on stackexchange narkive permalink

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

Fondamentalmente, un gestore di memoria ingenuo con una garanzia di privacy funziona in questo modo:

  • prenotare una pagina dal elenco liberato
  • azzerarlo
  • renderlo disponibile al codice dell'applicazione (impostare la voce della tabella delle pagine per consentire l'accesso all'anello 3)

La versione ottimizzata sembra più simile:

  • ottieni una pagina dall'elenco azzerato, se ce n'è una
  • se il primo passaggio è riuscito, vai all'ultimo passaggio
  • prenota un pagina dall'elenco liberato
  • azzerarlo
  • renderlo disponibile al codice dell'applicazione (impostare la voce della tabella delle pagine per consentire l'accesso all'anello 3)

Affamato il thread di azzeramento risulterà in un'allocazione lenta, perché l'azzeramento non è stato ancora fatto. Non si verificherà una perdita di dati, perché la struttura dei dati mantiene le pagine azzerate e rimanenti segregate e quando l'allocatore esaurisce le pagine pre-azzerate, deve eseguire l'azzeramento just-in-time.

#4
+5
davidb
2016-01-13 17:59:22 UTC
view on stackexchange narkive permalink

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

Per gli utenti amministrativi questo è ovviamente possibile. In Windows, ad esempio, 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 eseguire il dump dell'intera memoria inclusi tutti i processi e tutto ciò che è archiviato in memoria in quel momento. Non è più così facile perché i sistemi Windows non forniscono questa funzionalità per impostazione predefinita. Per fare ciò l'applicazione carica i propri driver di memoria che gli consentono di accedere alla memoria direttamente e non attraverso il sistema operativo.

Sui vecchi sistemi Linux siamo in grado di scaricare la memoria direttamente attraverso il dispositivo di memoria nel / dev partizione. 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.

// MODIFICA : ho appena chiesto a uno sviluppatore senior con 40 anni di esperienza Questo. 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 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.

Ciò richiede privilegi di root. Intendevo codice senza privilegi che hackerava lo stesso o più privilegiato. Ad esempio l'attacco nel modo seguente: eseguiamo il proprio processo, quello privato mette i dati in memoria, si verifica un cambio di contesto, chiediamo una pagina, il sistema operativo elimina la pagina di memoria del processo privilegiato e ci fornisce una pagina supportata da quella frame della pagina senza azzerarlo (l'uomo insiste che l'azzeramento non è responsabilità del sistema operativo), e siamo in grado di scansionarlo alla ricerca di segreti.
Vedi modifica ... Ho appena chiesto a un collega esperto ...
Il collega esperto ha torto riguardo al C ++: * puoi * accedere alla memoria non inizializzata da C ++, sebbene raramente ci sia una buona ragione per farlo.
#5
+2
Simon Richter
2016-01-14 04:37:06 UTC
view on stackexchange narkive permalink

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

Questo è tuttavia completamente poco pratico, perché 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 disponga di meno autorizzazioni di accesso rispetto al set di privilegi originale.

Dato che i privilegi possono anche essere abbastanza dettagliati, lo sforzo per tenere traccia dei processi a cui può essere assegnata una pagina senza prima cancellarla è significativamente più alto che cancellare semplicemente ogni pagina restituita al sistema operativo, soprattutto perché l'architettura della memoria del computer favorisce fortemente le grandi scritture sequenziali.

Alcune architetture incorporate utilizzano anche il controller DMA per questo compito, riducendo il tempo della CPU a pochi cicli per impostare il controller e riconoscere il completamento.

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

Se un'attività mappa, modifica e annulla la mappatura rapidamente le pagine, questo aumentare il carico di sistema ei processi potrebbero essere sospesi durante l'attesa di un processo a bassa priorità per cancellare una pagina. I sistemi operativi devono fare attenzione a non introdurre inavvertitamente 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.

Non è mai necessario confondere con la priorità del thread di azzeramento in background, perché è perfettamente fattibile eseguire l'azzeramento nel contesto attivo come fallback.
@BenVoigt, a seconda di come è configurato il tuo sistema operativo, potrebbe essere uno sforzo maggiore da implementare, soprattutto se hai già il codice per gestire l'inversione di priorità nei blocchi.
#6
+1
billc.cn
2016-01-15 23:57:36 UTC
view on stackexchange narkive permalink

La maggior parte dei sistemi operativi deve essere certificata per essere utilizzata per determinati scopi / in determinate organizzazioni. La maggior parte di loro utilizza il framework Common Criteria per 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:

Uno dei motivi per cui le pagine con inizializzazione zero sono richieste è per soddisfare vari requisiti di sicurezza, come i Common Criteria. La maggior parte dei profili Common Criteria specifica che ai processi in modalità utente devono essere assegnati frame di pagina inizializzati per impedire loro di leggere i contenuti della memoria di un processo precedente. Pertanto, il gestore della memoria fornisce ai processi in modalità utente frame di pagina azzerati a meno che la pagina non venga letta da un archivio di backup. In questo caso, il gestore della memoria preferisce utilizzare frame di pagina diversi da zero, inizializzandoli con i dati fuori dal disco o dalla memoria remota.

(da Windows Internals, Part 2 6th ed. By Mark E. Russinovich, David A. Solomon, Alex Ionescu. Copyright © 2012 di David Solomon e Mark Russinovich)

L'abilitazione di una tale funzionalità molto spesso richiede che l'architettura di gestione della memoria sia progettata di conseguenza e non ha senso per escludere questa funzionalità in una versione "civile" / insicura per miglioramenti non evidenti delle prestazioni.

Nello specifico, è importante che le pagine del kernel e le pagine di altri utenti non vengano trasmesse a processi non privilegiati in modo che abbiano essere cancellato a un certo punto. È anche inefficiente che una pagina venga azzerata a meno che non attraversi effettivamente il confine (nel caso in cui una pagina venga assegnata di nuovo allo stesso processo / kernel). Quindi l'unico momento ragionevole per farlo è al momento dell'assegnazione e poiché non ci si può fidare del destinatario per farlo, il sistema operativo dovrà assumersi la responsabilità. (Naturalmente, il sistema operativo del mondo reale adotterà tutti i tipi di ottimizzazioni per ridurre il ritardo nell'allocazione delle pagine.)

#7
-3
Alexey Vesnin
2016-01-13 22:28:05 UTC
view on stackexchange narkive permalink

L'unico modo per eseguire ciò di cui stai parlando è un attacco con avvio a freddo , quando stai distruggendo il kernel spegnendolo. OPPURE puoi provare a giocare con le chiamate kexec () , ma nella maggior parte dei casi non funzionerà.

Questa domanda non riguarda il coldboot, l'attacco coldboot è l'ultima risorsa e richiede il controllo sull'hardware, non solo un singolo processo senza privilegi. Potresti chiarire su `` kexec```, se puoi giocarci per avere accesso alla memoria a cui non devi avere accesso, è una chiara vulnerabilità?
@KOLANICH dà un'occhiata ad alcuni esempi pratici [qui] (https://www.linux.com/community/blogs/129-servers/413862)
Non vedo alcun vuln qui, perché kexec richiede i privilegi di root.
@KOLANICH non è un vuln da parte di utenti non privilegiati: la memoria è riempita a zero dal kernel


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...