Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2022-20474 — Analyse technique détaillée et preuve de concept pour CVE-2022-20474 sur Android, une vulnérabilité d'inadéquation de Bundle exploitant LazyValue avec une longueur négative pour obtenir un comportement de Bundle auto-modifiable. | Kitploit
Outils/GitHubGitHub/cxxsheng/cve-2022-20474
Sécurité AndroidAnalyse des VulnérabilitésExploitationArticles et RechercheApprentissage et ÉducationExploitation de Binaires
GitHubcxxsheng/cve-2022-20474

CVE-2022-20474

Analyse technique détaillée et preuve de concept pour CVE-2022-20474 sur Android, une vulnérabilité d'inadéquation de Bundle exploitant LazyValue avec une longueur négative pour obtenir un comportement de Bundle auto-modifiable.

Voir le dépôt
2015il y a 1 anVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Analyse de CVE-2022-20474 – Bundle auto-modifié sous LazyValue

Préface

Note : avant de lire cet article, il est conseillé d'avoir une compréhension de base des vulnérabilités de décalage de Bundle. Si vous n'avez pas encore lu les documents suivants, veuillez d'abord les consulter :

  1. Bundle Fengshui – Explication détaillée des vulnérabilités de non-correspondance entre sérialisation et désérialisation sous Android : tutoriel classique pour débutants.
  2. Histoire de l'attaque et de la défense des vulnérabilités de désérialisation Android : excellent article récapitulatif.
  3. TheLastBundleMismatch : premier article sur le décalage de Bundle en mode LazyValue.

Contexte

Récemment, j'étudie attentivement l'article LeakValue de michalbednarski. En discutant avec Canyie, il a mentionné que cet article évoquait également un cas de Bundle auto-modifié dans le scénario LazyValue. Je suis donc allé chercher le texte original, et effectivement il y avait ce passage, que j'avais directement omis lors de ma lecture de l'article de Michal. L'original dit :

(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 faisait probablement référence à CVE-2022-20474 (bulletin, patch). J'ai jeté un coup d'œil au patch, mais la fonction dans le lien du patch n'était pas très complète. Je l'ai complétée et examinée attentivement :

@@ -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);
         }
    }             

objectLength dans le code correspond à length du LazyValue. En fait, ce n'est que la longueur de l'objet variable contenu dans LazyValue, tandis que la longueur totale du LazyValue doit être contrôlée par le champ mLength, c'est-à-dire valueLength dans le code, qui est passé à mLength dans le constructeur de LazyValue.

Comparons avec la disposition de LazyValue :

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

Sur la base de ce qui précède, nous pouvons obtenir les faits suivants :

  1. mLength représente la longueur totale du LazyValue ; mLength = objectLength + 8 octets.
  2. objectLength doit être supérieur ou égal à 0.
  3. L'objet LazyValue ne stocke que mLength, pas objectLength, car la copie mémoire de LazyValue est basée sur l'objet entier.
  4. Le pointeur après lecture est avancé, il est possible que LazyValue soit à nouveau lu.

Ensuite, après mûre réflexion, nous sommes arrivés à la conclusion que ces faits ne sont d'aucune utilité ! Car, sur la base des faits ci-dessus, une seule modification peut être effectuée lors de la lecture. Or, nous savons que l'idée centrale d'un Bundle auto-modifié est de modifier après la lecture, afin de contourner les vérifications de sécurité.

Au moment où nous étions sur le point d'abandonner, nous avons soudainement remarqué quelques détails dans la description du patch :

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

objectLength anormal

Il y est mentionné que lorsque objectLength vaut -8, des problèmes se posent, ce qui nous donne des indices supplémentaires. Dans ce cas, LazyValue peut-il encore être appliqué normalement ?

        @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;
    }

On voit que le readValue réel commence à mPosition, puis lit LazyType et objectLength, avant d'entrer dans le flux normal de lecture de Value. Par exemple, un Parcelable doit lire ClassName, puis exécuter createFromParcel. Une fois la lecture terminée, il n'y a aucune différence avec un Key-Value normal, et cela n'affecte pas la sérialisation ultérieure. Reconsidérons : l'idée centrale d'un Bundle auto-modifié est de modifier après la lecture. Ici, ce n'est qu'une simple lecture hors limites, donc cette direction semble inefficace.

Alors, que se passe-t-il si LazyValue n'est pas appliqué pendant ce processus, c'est-à-dire s'il continue à participer à l'IPC en tant que LazyValue ? Dans ce cas, sa fonction writeToParcel est appelée :

     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);
        }
Télécharger l’outil