Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2022-20474 — Analisi tecnica dettagliata e proof-of-concept per Android CVE-2022-20474, una vulnerabilità di mismatch del Bundle che sfrutta LazyValue con lunghezza negativa per ottenere un comportamento del Bundle auto-modificante. | Kitploit
Strumenti/GitHubGitHub/cxxsheng/cve-2022-20474
Sicurezza AndroidAnalisi delle VulnerabilitàExploitPaper e RicercaApprendimento e FormazioneBinary Exploitation
GitHubcxxsheng/cve-2022-20474

CVE-2022-20474

Analisi tecnica dettagliata e proof-of-concept per Android CVE-2022-20474, una vulnerabilità di mismatch del Bundle che sfrutta LazyValue con lunghezza negativa per ottenere un comportamento del Bundle auto-modificante.

Vedi Repository
20141 anno faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Analisi di CVE-2022-20474 — Self-changed Bundle in LazyValue

Introduzione

Nota: prima di leggere questo articolo, dovreste avere una conoscenza di base delle vulnerabilità Bundle Mismatch. Se non avete ancora letto i seguenti riferimenti, vi consiglio di farlo:

  1. Bundle Feng Shui — Analisi approfondita delle vulnerabilità di disallineamento tra serializzazione e deserializzazione in Android: classico tutorial di livello introduttivo.
  2. Storia di attacchi e difese delle vulnerabilità di deserializzazione Android: buon articolo di sintesi.
  3. TheLastBundleMismatch: il primo articolo sul Bundle Mismatch in modalità LazyValue.

Contesto

Di recente stavo studiando attentamente l'articolo LeakValue di michalbednarski. Durante una discussione con Canyie, mi ha detto che nell'articolo veniva menzionato anche un caso di Self-changing Bundle nello scenario LazyValue. Così sono andato a cercare nel testo originale e in effetti c'era proprio questo passaggio, che avevo completamente saltato leggendo l'articolo di Michal. Il testo originale dice:

(Also LazyValue with negative length specified can be used (without using other bugs described in this writeup) to create self-changing Bundle, the thing LazyValue was created to eliminate. But that is another story (and separately reported to Google), in this exploit I'm aiming for more)

Michal si riferiva probabilmente a CVE-2022-20474 (bulletin, patch). Ho dato un'occhiata alla patch, ma la funzione presente nel link non era completa; l'ho completata e poi l'ho esaminata più attentamente:

@@ -4388,6 +4388,9 @@
    public Object readLazyValue(@Nullable ClassLoader loader) {
         int start = dataPosition();
         int type = readInt();
         if (isLengthPrefixed(type)) {
             int objectLength = readInt();
+            if (objectLength < 0) {
+                return null;
+            }
             int end = MathUtils.addOrThrow(dataPosition(), objectLength);
             int valueLength = end - start;
             setDataPosition(end);
             return new LazyValue(this, start, valueLength, type, loader);
         } else {
            return readValue(type, loader, /* clazz */ null);
         }
    }             

Nel codice, objectLength è il length del LazyValue; in realtà si tratta solo della lunghezza dell'oggetto variabile contenuto nel LazyValue, mentre la lunghezza dell'intero LazyValue è controllata dal campo mLength, cioè valueLength nel codice, che nel costruttore di LazyValue viene passato a mLength.

Confrontiamo ora il formato di layout di LazyValue:

       /**
         *                      |   4B   |   4B   |
         * mSource = Parcel{... |  type  | length | object | ...}
         *                      a        b        c        d
         * length = d - c
         * mPosition = a
         * mLength = d - a
         */

Sulla base di quanto sopra, possiamo ricavare i seguenti fatti:

  1. mLength rappresenta la lunghezza dell'intero LazyValue; mLength = objectLength + 8 byte.
  2. objectLength deve essere maggiore o uguale a 0.
  3. L'oggetto LazyValue conserva solo mLength e non objectLength, perché quando LazyValue esegue una copia in memoria, copia l'intero oggetto.
  4. Dopo la lettura, il puntatore viene riportato al punto iniziale; potrebbe quindi rileggere il LazyValue.

Poi, dopo averci riflettuto a lungo, siamo giunti alla conclusione che questi fatti non servivano a NULLA! Perché sulla base dei fatti sopra, si poteva modificare solo una volta durante la read, mentre sappiamo che l'idea centrale di Self-changed Bundle è modificare dopo che la read è stata completata: solo così si può aggirare il controllo di sicurezza.

Proprio quando stavamo per rinunciare, abbiamo scoperto alcuni dettagli nella descrizione della patch:

Addresses a security vulnerability where a (-8) length object would cause dataPosition to be reset back to the statt of the value, and be re-read again.

objectLength anomalo

Lì si dice che quando objectLength è -8 si verificano alcuni problemi; questo ci ha dato qualche spunto in più. A questo punto, LazyValue può ancora eseguire apply correttamente?

        @Override
        public Object apply(@Nullable Class<?> clazz, @Nullable Class<?>[] itemTypes) {
            Parcel source = mSource;
            if (source != null) {
                synchronized (source) {
                    // Check mSource != null guarantees callers won't ever see different objects.
                    if (mSource != null) {
                        int restore = source.dataPosition();
                        try {
                            source.setDataPosition(mPosition);
                            mObject = source.readValue(mLoader, clazz, itemTypes);
                        } finally {
                            source.setDataPosition(restore);
                        }
                        mSource = null;
                    }
                }
            }
            return mObject;
        }

  	/**
     * @see #readValue(int, ClassLoader, Class, Class[])
     */
    @Nullable
    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz,
            @Nullable Class<?>... itemTypes) {
        int type = readInt();
        final T object;
        if (isLengthPrefixed(type)) {
            int length = readInt();
            int start = dataPosition();
            object = readValue(type, loader, clazz, itemTypes);
            int actual = dataPosition() - start;
            if (actual != length) {
                Slog.wtfStack(TAG,
                        "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type)
                                + "  consumed " + actual + " bytes, but " + length + " expected.");
            }
        } else {
            object = readValue(type, loader, clazz, itemTypes);
        }
        return object;
    }

Si può vedere che la readValue effettiva inizia a leggere da mPosition, poi legge LazyType e objectLength in sequenza, quindi entra nel normale flusso di lettura del Value. Ad esempio, un Parcelable deve leggere ClassName e poi eseguire createFromParcel. Una volta completata la lettura, non c'è alcuna differenza rispetto a una normale Key-Value, e non influisce sulla serializzazione successiva. Rivedendo il tutto, l'idea centrale di Self-changed Bundle è modificare dopo la lettura; qui si tratta solo di una normale lettura fuori dai limiti, quindi questa strada non sembra percorribile.

E se invece il LazyValue non viene sottoposto ad apply in questo processo? In altre parole, continua a partecipare all'IPC come LazyValue; in questo caso viene chiamata la sua funzione writeToParcel:

     public void writeToParcel(Parcel out) {
            Parcel source = mSource;
            if (source != null) {
                synchronized (source) {
                    if (mSource != null) {
                        out.appendFrom(source, mPosition, mLength);
                        return;
                    }
                }
            }
            out.writeValue(mObject);
        }
Scarica lo strumento