ROP: cercare gadget nelle applicazioni Android

In questo articolo ci occuperemo della ricerca di sequenze di istruzioni presenti nella piattaforma Android (su sistemi ARM) al fine di sfruttare delle vulnerabilità nelle applicazioni, con attacchi di tipo ROP, aggirando i principali sistemi di sicurezza.

Gadget: cosa sono e a cosa servono

Gli attacchi di tipo Return Oriented Programming (ROP, vedi Riquadro 1) si basano su delle sequenze di istruzioni chiamate gadget. Un gadget deve fornire un’operazione usabile, quindi di fondamentale importanza è la loro disponibilità.

I gadget sono istruzioni o sequenze di istruzioni che soddisfano le seguenti condizioni:

  • devono modificare il flusso dell’applicazione;
  • devono eseguire una operazione utile al fine dell’attacco;
  • devono terminare con una chiamata ad un altro indirizzo e quindi i gadget devono essere combinabili in modo da formare un programma. E sono appunto concatenabili se terminano con un’istruzione che, se gestita dall’attaccante, altera il flusso originario;
  • il flusso deve fare riferimento ad un registro;
  • tale registro deve poter essere modificabile dall’attaccante.

Dopo aver trovato le varie sequenze di istruzioni utili (gadget) disponibili non resta che metterli insieme in una “ROP chain” in modo da ottenere l’esecuzione di codice arbitrario (figura 1). In Android (vedi Riquadro 2) tali gadget si possono trovare nelle librerie di sistema, nei file .oat di sistema, nelle librerie dell’applicazione e nei file .oat dell’applicazione.

Figura 1

C’è da aggiungere che un attaccante, all’atto di eseguire un attacco di tipo ROP, potrebbe trovarsi di fronte a diversi limiti dovuti ad esempio all’applicazione o alla particolare vulnerabilità. In una situazione del genere non tutti i gadget disponibili saranno utili all’attacco.

Riquadro 1: ROP in sintesi

La tecnica di attacco di tipo ROP utilizza codice esistente già nell’applicazione indirizzando il flusso di controllo attraverso l’indirizzo di ritorno. Il concetto è semplice: si tratta di trovare ed eseguire diverse sequenze di istruzioni. Semplificando, i gadget fanno alcune operazioni e alla fine chiamano un indirizzo di ritorno, quindi l’operazione si ripete con il successivo gadget. Il risultato finale consiste nel concatenare tali gadget per ottenere in questo modo il comportamento voluto ed eseguire l’attacco.

Questa tecnica permette di sfruttare delle vulnerabilità aggirando il meccanismo che prevede la separazione di codice e dati. È da sottolineare che gran parte delle contromisure attualmente esistenti (ASLR, DEP, ecc.) sono aggirabili da tecniche avanzate di questo tipo. Gli exploit di tipo ROP possono essere visti come una versione avanzata di quelli Ret2libc.

Old school attack vs new attack

Un attacco generalmente avviene attraverso i seguenti passi (figura 2):

Figura 2
  • Trovare una vulnerabilità in una applicazione;
  • Disassemblare l’applicazione e le librerie di sistema;
  • Cercare sequenze di istruzioni (gadget) utili nel codice appena disassemblato;
  • Collegare insieme le sequenze di istruzioni in modo da creare il payload voluto;
  • Sfruttare la vulnerabilità e invocare la catena di gadget appena creata.

Riquadro 2: ART vs DVM

ART (Android RunTime) è il runtime system software di Android introdotto come alternativa a Dalvik nella versione 4.4 (Kitkat, 31 Ottobre 2013) e introdotto ufficialmente nella versione 5.0 (Lollipop, 12 Novembre 2014). L’introduzione di ART, essenzialmente, riguarda due campi: l’esecuzione delle applicazioni e la garbage collection. Il vantaggio principale di ART su Dalvik è nelle performance delle app in quanto è stata introdotta la compilazione Ahead-Of-Time (AOT) in sostituzione di quella Just-In-Time (JIT), quindi ART precompila il bytecode di una applicazione in codice nativo.

Quello che ci interessa è che con ART abbiamo la presenza di file compilati AOT (file di sistema e file con estensione .odex relativi all’applicazione). In pratica con l’introduzione del compilatore AOT i file delle applicazioni .dex vengono compilati in file .odex, che contengono istruzioni native, al momento dell’installazione e dell’aggiornamento. Inoltre, tutte le app verranno compilate ogni volta che il sistema sul dispositivo viene aggiornato o anche la prima volta che viene avviato. In aggiunta anche l’intero framework viene compilato in un singolo file (boot.oat). Tale file viene chiamato dall’app ogni volta che questa fa riferimento ad un metodo dell’API del framework.

In definitiva, le versioni di Android con ART offrono una maggiore disponibilità di gadget che un attacante può usare per costruire la propria ROP chain.

Cercare gadget per costruire una ROP chain

Introduzione a Ropper

Ropper (https://github.com/sashs/Ropper) è un tool per la ricerca di gadget in file binari. Sono attualmente supportate diverse architetture (x86, x86_64,MIPS, MIPS64, ARM, ARM THUMB, ARM64, PowerPC, PowerPC64) e diversi tipi di eseguibili (ELF, PE, Mach-O, Raw).

Figura 3

Ropper offre numerose opzioni (figura 3) per la ricerca di gadget che permettono di semplificare non poco il processo di ricerca e selezione. È possibile anche usare Ropper all’interno di uno script Python, ad esempio:

#!/usr/bin/env python
 from ropper import RopperService
 
 options = {'color': True,
            'all': True,
            'inst_count': 6,
            'type': 'all',
            'detailed': False}
 rs = RopperService(options)
 
 rs.addFile('Test.odex', arch='ARMTHUMB')
 
 rs.loadGadgetsFor()
 
 for _, gadget in rs.search(search='pop % r15'):
     print(gadget)

Cercare gadget con Ropper

In Android su tecnologia ARM i gadget usualmente includono istruzioni che permettono di indicare un registro per un trasferimento del controllo di flusso, ad esempio una POP che ha r14 (LR, link register) o r15 (PC, program counter) tra i suoi operandi, oppure un’istruzione di branching tipo BL, BX e BLX poiché scrivono un indirizzo nel registro r14 e quindi effettuano una chiamata a tale indirizzo.

Un esempio di sessione in Ropper, dove ad esempio filtriamo i risultati contenenti una pop che coinvolge il registro r15, è la seguente:

(Ropper)> help
 (Ropper)> help file
 (Ropper)> file Test.odex
 [INFO] Load gadgets from cache
 [LOAD] loading... 100%
 [INFO] File loaded.
 (Test.odex/ELF/ARM)> help arch
 (Test.odex/ELF/ARMTHUMB)> arch ARMTHUMB
 [INFO] Load gadgets from cache
 [LOAD] loading... 100%
 [LOAD] removing double gadgets... 100%
 (Test.odex/ELF/ARMTHUMB)> show information
 ELF Header
 ==========
 Name                  Value
 ----                  -----
 Class                 BITS_32
 Machine               ARM
 Version               1
 EntryPoint            0x00000000
 ProgramHeader Offset  52
 SectionHeader Offset  1118336
 Flags                 0x05000000
 ELF Header Size       52
 ProgramHeader Size    32
 ProgramHeader Number  5
 SectionHeader Size    40
 SectionHeader Number  9
 (Test.odex/ELF/ARMTHUMB)> search pop % r15
 [INFO] Searching for gadgets: pop % r15
 [INFO] File: Test.odex
 0x000b0c50 (0x000b0c51): pop {r0, r1, r2, r3, r7, pc};
 0x001038d8 (0x001038d9): pop {r0, r1, r2, r4, r5, r7, pc};
 0x00103802 (0x00103803): pop {r0, r1, r3, r4, r5, r6, r7, pc};
 0x000d574c (0x000d574d): pop {r0, r1, r3, r5, r6, r7, pc};
 0x00106874 (0x00106875): pop {r0, r1, r4, r7, pc};
 0x000d06d8 (0x000d06d9): pop {r0, r2, r3, r5, pc};
 0x000bf690 (0x000bf691): pop {r0, r2, r3, r5, r6, pc};
 0x00104c48 (0x00104c49): pop {r0, r2, r3, r5, r7, pc};
 0x000bf728 (0x000bf729): pop {r0, r2, r4, r5, r6, r7, pc};
 0x000d0740 (0x000d0741): pop {r0, r3, r4, r5, r7, pc};
 0x000bf648 (0x000bf649): pop {r0, r3, r5, pc};
 0x000e590c (0x000e590d): pop {r0, r4, r5, pc};
 0x000f3290 (0x000f3291): pop {r1, r2, r3, r4, r6, r7, pc};
 0x0009d948 (0x0009d949): pop {r1, r2, r3, r5, pc};
 0x000f9518 (0x000f9519): pop {r1, r2, r3, r5, r6, r7, pc};
 0x000f32f0 (0x000f32f1): pop {r1, r2, r3, r5, r7, pc};
 0x000c2b38 (0x000c2b39): pop {r1, r2, r4, pc};
 0x000c553c (0x000c553d): pop {r1, r3, r4, r6, r7, pc};
 0x00104cee (0x00104cef): pop {r1, r4, r5, r6, pc};
 0x000d5534 (0x000d5535): pop {r1, r4, r6, r7, pc};
 0x000c2bf8 (0x000c2bf9): pop {r1, r6, r7, pc};
 0x000c54e4 (0x000c54e5): pop {r1, r7, pc};
 0x000e7420 (0x000e7421): pop {r2, r3, r6, pc};
 0x0010680c (0x0010680d): pop {r2, r4, r5, pc};
 0x000e74b8 (0x000e74b9): pop {r2, r4, r6, r7, pc};
 0x001039ca (0x001039cb): pop {r2, r5, r6, pc};
 0x000d4ba0 (0x000d4ba1): pop {r2, r7, pc};
 0x000e5564 (0x000e5565): pop {r4, r6, r7, pc};

A questo punto dopo aver disassemblato tutte le sorgenti (librerie di sistema, boot.oat e file odex dell’applicazione), non ci resta che cercare le sequenze che terminano con una istruzione di ritorno necessarie all’attacco.

Conclusioni

In questo articolo abbiamo visto come cercare i gadget per costruire una ROP chain in Android su ARM con Ropper; ed in particolare dove cercarli e cosa cercare per poter eseguire un attacco.

A cura di: Gianluigi Spagnuolo

Profilo Autore

Si interessa di reverse engineering, attualmente si occupa della sicurezza dei firmware.
Collabora con diverse riviste del settore scrivendo di programmazione e sicurezza.

Condividi sui Social Network:

Articoli simili