Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-20474 — Análisis técnico detallado y prueba de concepto para Android CVE-2022-20474, una vulnerabilidad de discrepancia de Bundle que explota LazyValue con longitud negativa para lograr un comportamiento de Bundle que se modifica a sí mismo. | Kitploit
Herramientas/GitHubGitHub/cxxsheng/cve-2022-20474
Seguridad AndroidAnálisis de VulnerabilidadesExplotaciónPapers e InvestigaciónAprendizaje y EducaciónExplotación de Binarios
GitHubcxxsheng/cve-2022-20474

CVE-2022-20474

Análisis técnico detallado y prueba de concepto para Android CVE-2022-20474, una vulnerabilidad de discrepancia de Bundle que explota LazyValue con longitud negativa para lograr un comportamiento de Bundle que se modifica a sí mismo.

Ver Repositorio
2015hace 1 añoRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Análisis de CVE-2022-20474 — Bundle auto-modificado en modo LazyValue

Prefacio

Nota: antes de leer este artículo, debería tener una comprensión básica de las vulnerabilidades relacionadas con Bundle Mismatch. Si aún no ha leído los siguientes materiales de referencia, se recomienda hacerlo primero:

  1. Bundle Fengshui — Explicación detallada de las vulnerabilidades de desajuste entre serialización y deserialización en Android: Tutorial clásico de nivel introductorio.
  2. Historia de ataque y defensa de vulnerabilidades de deserialización en Android: Un buen artículo resumen.
  3. TheLastBundleMismatch: Primer artículo sobre Bundle Mismatch en modo LazyValue.

Antecedentes

Recientemente estaba estudiando en detalle el artículo LeakValue de michalbednarski. Mientras discutía con Canyie, mencionó que en este artículo también se mencionaba un caso de Bundle auto-modificado en el escenario LazyValue. Así que fui a buscar el texto original, y efectivamente, había ese párrafo, pero yo lo había pasado por alto al leer el artículo de Michal. El texto original dice lo siguiente:

(También se puede usar LazyValue con una longitud negativa especificada (sin usar otros errores descritos en este informe) para crear un Bundle auto-modificado, lo mismo que LazyValue fue creado para eliminar. Pero esa es otra historia (y se informó por separado a Google); en este exploit apunto a algo más).

Michal se refiere a CVE-2022-20474 (boletín, parche). Le eché un vistazo al parche, pero la función en el enlace del parche no estaba completa. Después de completarla, la revisé con más detalle:

@@ -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 en el código es la length de LazyValue, pero de hecho esto es solo la longitud del objeto variable contenido en LazyValue, mientras que la longitud total de LazyValue es controlada por el campo mLength, es decir, valueLength en el código, que se asigna a mLength en el constructor de LazyValue.

Comparemos con el formato de diseño de LazyValue de la siguiente manera:

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

Basado en lo anterior, podemos obtener los siguientes hechos:

  1. mLength representa la longitud total de LazyValue; mLength = objectLength + 8 bytes.
  2. objectLength debería ser mayor o igual a 0.
  3. El objeto LazyValue solo guarda mLength, no objectLength, porque LazyValue hace copia de memoria basada en todo el objeto.
  4. Después de leer, el puntero avanza, y posiblemente se vuelva a leer LazyValue.

Luego, después de una reflexión profunda, coincidimos en que estos hechos no sirven de nada, porque basándose en lo anterior, solo se puede modificar una vez durante la lectura. Sabemos que la idea central de Bundle auto-modificado es modificar después de que la lectura haya terminado para eludir la verificación de seguridad.

Justo cuando estábamos a punto de rendirnos, de repente notamos algunos detalles en la descripción del parche:

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

Se menciona que cuando objectLength es -8, hay algunos problemas, lo que nos dio ideas adicionales. ¿Puede LazyValue todavía aplicar de manera normal en este caso?

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

Se puede ver que el readValue real comienza a leer desde mPosition, luego lee secuencialmente LazyType y objectLength, y luego entra en el flujo normal de lectura de Value, por ejemplo, Parcelable necesita leer ClassName y luego ejecutar createFromParcel. Después de la lectura, no hay diferencia con un Key-Value normal y no afecta la serialización posterior. Revisando de nuevo, la idea central de Bundle auto-modificado es modificar después de que la lectura haya terminado. Aquí solo es una lectura fuera de límites común; parece que este camino no funciona.

¿Y si LazyValue no se apply en este proceso? Es decir, si sigue participando en IPC como LazyValue, se llamará a su función 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);
        }
Descargar herramienta