
Exploit per CVE-2019-9810 Firefox su Windows 64-bit.
CVE-2019-9810 è una vulnerabilità scoperta e sfruttata al Pwn2Own 2019 da Richard Zhu e Amat Cama. Colpisce il motore JavaScript di Mozilla, Spidermonkey, ed è stata utilizzata per compromettere il renderer.
Il problema è stato risolto in mfsa2019-09 circa due mesi fa.
In poche parole, il bug consente di rendere ridondante un controllo dei limiti e di ottimizzarlo, permettendo al codice di accedere a memoria fuori dai limiti. Il problema risiede nel passaggio di Alias Analysis della pipeline di Ion. L'immagine sottostante evidenzia la conseguenza del bug nel passaggio di ottimizzazione GVN (controllo dei limiti ottimizzato via) e funge da buon riassunto se è ciò che stai cercando:

Se invece vuoi saperne di più, ti consiglio di dare un'occhiata a A journey into IonMonkey: root-causing CVE-2019-9810.
Il repository contiene il codice dell'exploit e una serie di strumenti che avevo sviluppato in precedenza per i miei exploit di blazefox. Li ho semplicemente rifiniti e li ho resi compatibili con BigInt. Di conseguenza, l'exploit presuppone che il supporto per BigInt sia attivo in Firefox, cosa che puoi fare attivando javascript.options.bigint in about:config.

L'exploit è stato testato su Windows RS5 a 64 bit e ha come target una build personalizzata di Firefox, quindi non sorprenderti se è necessario un po' di lavoro per farlo funzionare altrove :). Tuttavia, se vuoi solo eseguire l'exploit senza compilare nulla, ho preparato un browser impacchettato che ho caricato in release/firefox-68.0a1.en-US.win64.7z. Include anche la shell js.exe e le informazioni sui simboli privati per js.exe, firefox.exe e xul.dll.
Il processo di sfruttamento è molto simile al mio precedente exploit kaizen.js come menzionato sopra. Invia l'esecuzione al ReflectiveLoader di una reflective dll che implementa il payload:
js.exe, si limita a scrivere PWN su stdout, apre una calcolatrice e termina.Firefox.exe. Per farlo, l'exploit JavaScript passa un puntatore alla copia della dll riflessiva, e la dll riflessiva la mappa negli altri processi. Una volta fatto ciò, crea un thread remoto sul caricatore riflessivo e si mette in pausa. La ragione è che l'exploit è piuttosto sporco nel suo stato attuale e non implementa la continuazione del processo. Tuttavia, a questo punto non credo richiederebbe molto lavoro. Forse riuscirò a farlo :-).Firefox.exe, effettua un inline-hook della funzione xul!nsJSUtils::ExecutionContext::Compile. Questa funzione viene eseguita quando è necessario valutare script dal motore JavaScript; quindi sembrava un candidato abbastanza buono per ciò che volevo fare. La versione hookata semplicemente prepone un payload JavaScript arbitrario a nostra scelta.In realtà, ci sono molti dettagli più sottili che non sono descritti sopra, quindi se sei interessato sei invitato a cercare la verità e leggere le fonti :).
Per compilare il payload, basta eseguire nmake da un prompt VS 2017 x64.
CVE-2019-9810\payload>nmake
Microsoft (R) Program Maintenance Utility Version 14.16.27027.1
Copyright (C) Microsoft Corporation. All rights reserved.
ml64 /c src\trampoline.asm
Microsoft (R) Macro Assembler (x64) Version 14.16.27027.1
Copyright (C) Microsoft Corporation. All rights reserved.
Assembling: src\trampoline.asm
if not exist .\bin mkdir bin
type injected-script.js > src\injected-script.h
cl /O1 /nologo /W3 /D_AMD64_ /DWIN_X64 /DREFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN /Febin\payload.dll src\ReflectiveLoader.c src\ReflectiveDll.cc trampoline.obj /link /DLL /nologo /debug:full /PDBALTPATH:%_PDB%
ReflectiveLoader.c
Generating Code...
Compiling...
ReflectiveDll.cc
Generating Code...
Creating library bin\payload.lib and object bin\payload.exp
python jsify_payload.py bin\payload.dll
move payload.js ..
1 file(s) moved.
del *.obj
del src\injected-script.h
if exist .\bin del bin\*.exp bin\*.ilk bin\*.lib
Questo crea un file payload.dll / payload.pdb nella directory payload\bin, oltre a un file JavaScript chiamato payload.js che incorpora la dll in un Uint8Array con l'offset al loader.
Ho scritto questo exploit contro una build locale di Windows sincronizzata con il seguente ID di revisione: 2abb636ad481768b7c88619080cf224b2c266b2d (se non vuoi compilarla da solo, ho caricato la mia build qui: release/firefox-68.0a1.en-US.win64.7z):
$ hg --debug id -i
2abb636ad481768b7c88619080cf224b2c266b2d
E ho usato il seguente file mozconfig:
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
ac_add_options --disable-crashreporter
ac_add_options --enable-debug-symbols
. "$topsrcdir/build/mozconfig.clang-cl"
. "$topsrcdir/build/mozconfig.lld-link"
# Use the clang version in .mozbuild
CLANG_LIB_DIR="$(cd ~/.mozbuild/clang/lib/clang/*/lib/windows && pwd)"
export LIB=$LIB:$CLANG_LIB_DIR
ac_add_options --enable-js-shell
ac_add_options --enable-jitspew
mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-ff64
Anche se andava bene per i miei scopi, non sono sicuro che xul!nsJSUtils::ExecutionContext::Compile sia la funzione perfetta per inserire script arbitrari. Sono certo che dedicando più tempo a capire come funziona il front-end di xul si potrebbe trovare un punto di aggancio migliore.
Un'altra coppia di strade che ho scoperto dopo aver scritto l'exploit sono discusse in questa segnalazione bugzilla: 982974 (System principal for the JavaScript interpreter and security.turn_off_all_security_so_that_viruses_can_take_over_this_computer). Sarebbe interessante vedere quanto sia ancora rilevante per Firefox oggi.
Forse qualcuno ha già studiato questo argomento e io me lo sono perso. In ogni caso, sentiti libero di contattarmi per qualsiasi feedback!
Un'altra cosa interessante sarebbe esplorare se esiste un modo per avere un meccanismo di persistenza all'interno del browser. Non ho mai studiato questo aspetto, ma sarebbe molto figo :).