
CVE-2017-8570 Exp e analisi del campione di exploit
Causa: aprendo un documento Office, FLTLDR.EXE viene utilizzato per rendere un file EPS incorporato che contiene la vulnerabilità. Il file è scritto in linguaggio PostScript e può essere sfruttato da un aggressore tramite l'operazione "save-restore"; si tratta essenzialmente di una vulnerabilità UAF. La vulnerabilità può essere sfruttata quando un utente apre un file contenente un'immagine grafica malformata o quando inserisce un'immagine grafica malformata in un file Office.
Versioni interessate: Microsoft Office 2010 Service Pack 2, Microsoft Office 2013 Service Pack 1, Microsoft Office 2016
POC: kcufId's Github
L'autore ha cercato a lungo in rete senza trovare un pacchetto Office contenente EPSIMP32.FLT. Fortunatamente il collega kcufId ha fornito un LoadEps.exe per caricare file EPS; grazie kcufId.
LoadEps.exe prima carica EPSIMP32.FLT:

Poi chiama ImportGr per iniziare il caricamento del file EPS:

A questo punto premere F7 per seguire, e si può interrompere con successo sul punto di interruzione impostato all'interno di EPSIMP32.FLT.
Prima di entrare nel vivo, illustriamo la struttura dell'oggetto PostScript.
// PostScript Object
struct PostScript object
{
dword type;
dword attr;
dword value1;
dword value2; // if array, point to userdict where store the array object
}ps_obj;
I valori corrispondenti ai diversi type sono:
0x0 nulltype
0x3 integertype
0x5 realtype
0x8 booleantype
0x10 operatortype
0x20 marktype
0x40 savetype
0x300 nametype
0x500 stringtype
0x900 filetype
0x30000 arraytype
0x0B0000 packedarraytype
0x70000 packedarraytype
0x110000 dicttype
0x210000 gstatetype
Prendiamo le stringhe come esempio per illustrare la struttura di archiviazione. Impostando un punto di interruzione sulla funzione forall, è possibile visualizzare ulteriormente come viene gestita una stringa (per sapere come individuare la funzione forall, consultare https://paper.seebug.org/368/).

L'immagine 1 corrisponde a ps_obj, il cui campo value2 punta all'elemento corrispondente nell'elenco degli indici (immagine 2); l'elemento dell'indice punta a una struttura di dimensione 0x30, la cui posizione 0x24 contiene un puntatore a un puntatore a una struttura di dimensione 0x28 (immagine 5), e la posizione 0x2C contiene la dimensione della stringa (immagine 3); nella struttura dell'immagine 5, la posizione 0x4 memorizza l'indirizzo dell'elemento corrispondente nell'elenco degli indici (cioè 0x01DB5E94 dell'immagine 4), la posizione 0x20 punta alla posizione finale di archiviazione della stringa (immagine 6), e la posizione 0x24 è la dimensione effettiva della memoria occupata: dimensione della stringa + 1.
Struttura di dimensione 0x30:
+0x0 dword
+0x4 dword
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword
+0x24 dword pp_struct // puntatore a puntatore a struttura di dimensione 0x28
+0x28 dword
+0x2c dword size // dimensione effettiva della stringa
Struttura di dimensione 0x28 (per gli array, la struttura ha dimensione 0x2C e la posizione 0x28 punta agli elementi dell'array, ciascun elemento è un ps_obj):
+0x0 dword
+0x4 dword // indirizzo dell'elemento corrispondente nell'elenco degli indici
+0x8 dword
+0xc dword
+0x10 dword
+0x14 dword
+0x18 dword
+0x1c dword
+0x20 dword ptr_object // punta alla posizione finale di archiviazione della stringa
+0x24 dword size // dimensione effettiva della memoria occupata, dimensione effettiva della stringa + 1
Primo trigger della vulnerabilità:

Innanzitutto, lo stato della VM viene salvato nella variabile l62, quindi per ogni carattere della variabile l63 viene chiamato il processo l61 –>> l59 –>> l56; l62 restore ripristina lo stato precedente, in modo che lo spazio di memoria allocato per l63 dopo l'istruzione /l62 save def venga liberato, diventando un puntatore penzolante.

Le variabili l95-l99 determinano il flusso successivo e i loro valori sono tutti 0 (cioè 32 bit):

Secondo trigger della vulnerabilità: prima viene allocato spazio di memoria di dimensione 0x27 (che in realtà occuperà 0x28) per memorizzare l63:

Poi l62 restore ripristina lo stato precedente, causando la liberazione dello spazio di memoria allocato per l63, che diventa un puntatore penzolante; successivamente viene eseguito l100, e lo spazio di memoria precedentemente occupato da l63 viene utilizzato per memorizzare la struttura 0x28 della stringa l102 (cioè l136) (questo spiega perché l63 richiedeva uno spazio di memoria di dimensione 0x27):

Vengono ottenuti rispettivamente i valori nelle posizioni 0x4, 0x20 e 0x24 di questa struttura:

Infine viene modificato il contenuto della stringa l136 (l'immagine mostra solo una parte delle modifiche):

Queste modifiche sono costruite con cura e verranno utilizzate al terzo trigger della vulnerabilità.
Terzo trigger della vulnerabilità: viene allocato un array contenente 0x37 elementi, quindi durante l'iterazione al 0x34° elemento viene eseguito l62 restore:

Dopo l'esecuzione di restore, la struttura 0x30 dell'array viene sovrascritta dal contenuto della stringa l193:

In questo modo, l'oggetto eseguito nell'ultimo processo forall (0x36) diventa la struttura 0x30 dell'immagine sopra, e ottenere il suo 0x36° elemento porta alla stringa accuratamente costruita durante il secondo trigger della vulnerabilità:

L'elemento dell'array ottenuto è un array di dimensione 4, il cui primo elemento è una stringa con indirizzo iniziale 0 e dimensione 0x7FFFFFFF: