Attacchi ROP: cercare gadget nelle applicazioni Android

Gli attacchi di tipo Return Oriented Programming (ROP) sfruttano sequenze di istruzioni chiamate gadget per aggirare le misure di sicurezza su Android, specialmente su dispositivi ARM. Questi gadget, presenti nelle librerie di sistema e nei file .odex, vengono collegati tra loro per eseguire codice arbitrario. L’evoluzione del runtime Android con ART ha aumentato la disponibilità di gadget, facilitando l’esecuzione di attacchi. Utilizzando strumenti come Ropper, è possibile semplificare la ricerca e la selezione dei gadget necessari per costruire una catena ROP.

Cosa sono i gadget negli attacchi Return Oriented Programming (ROP)?

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.

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.

Schema di una catena ROP che illustra il flusso tra gadget, con ogni gadget che esegue istruzioni e passa il controllo al gadget successivo. I gadget sono collegati tramite istruzioni di branching, rappresentando una sequenza tipica in un attacco Return Oriented Programming (ROP).
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.

ROP in sintesi: meccanismi e vulnerabilità

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.

Schema di un attacco informatico base

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

Diagramma del processo di attacco Return Oriented Programming (ROP) che mostra i passaggi principali: trovare una vulnerabilità in un'applicazione, disassemblare l'applicazione e le librerie, cercare sequenze di istruzioni (gadget) utili, collegare le sequenze in un payload, sfruttare la vulnerabilità e invocare la catena di gadget.
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.

ART vs DVM: evoluzione del runtime Android e impatti sulla sicurezza

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.

Strumenti per trovare gadget ROP: Ropper e la sua importanza

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).

Schermata del comando "help" del tool Ropper per la ricerca di gadget in file binari, utilizzato in attacchi ROP (Return Oriented Programming). La schermata elenca i comandi documentati, tra cui "gadgets", "ropchain", "search", e altri, che facilitano la costruzione di catene di gadget per sfruttare vulnerabilità nei sistemi.
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)

Ricerca di gadget ROP su ARM: sessione pratica 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: come costruire una ROP chain su Android con Ropper

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:

Ultimi Articoli