Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
Tools/GitHubGitHub/cxxsheng/cve-2022-20474
Android SecurityVulnerability AnalysisExploitationPapers & ResearchLearning & EducationBinary Exploitation
GitHubcxxsheng/cve-2022-20474

CVE-2022-20474

Detailed technical analysis and proof-of-concept for Android CVE-2022-20474, a Bundle mismatch vulnerability exploiting LazyValue with negative length to achieve self-changing Bundle behavior.

View Repository
20141 year agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2022-20474 Analysis — Self-changed Bundle under LazyValue

Preface

A friendly reminder: before reading this article, you should have a basic understanding of Bundle Mismatch vulnerabilities. If you haven't read the following references, it is recommended to read them first:

  1. Bundle Feng Shui — Detailed Explanation of Android Serialization and Deserialization Mismatch Vulnerability: A classic introductory tutorial.
  2. History of Android Deserialization Vulnerability Offense and Defense: A good summary article.
  3. TheLastBundleMismatch: The first article on Bundle Mismatch in the LazyValue mode.

Background

Recently I have been carefully studying michalbednarski's LeakValue article. While discussing with Canyie, he mentioned that this article also describes a case of Self-changing Bundle in the LazyValue scenario. I then searched for the original text, and indeed there was such a paragraph that I directly missed when reading Michal's article. The original text says:

(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 is probably referring to CVE-2022-20474 (bulletin, patch). I took a look at the patch, but the function in the patch link was not very complete. After completing it, take a closer look:

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

The objectLength in the code is the length in LazyValue. In fact, this is only the length of the mutable object contained in LazyValue, while the entire LazyValue length is controlled by the mLength field, i.e., valueLength in the code. In the constructor of LazyValue, this value is passed to mLength.

Let's compare the layout format of LazyValue as follows:

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

Based on the above content, we can derive the following facts:

  1. mLength represents the total length of the entire LazyValue. mLength = objectLength + 8 bytes.
  2. objectLength should be greater than or equal to 0.
  3. The LazyValue object only saves mLength, not objectLength, because when LazyValue does memory copy, it copies based on the entire object.
  4. After reading, the pointer moves forward, and the LazyValue might be read again.

Then, after careful consideration, we all agreed that these facts are not very useful! Because based on the above facts, it can only be modified once during reading. We know that the core idea of Self-changed Bundle is to modify after reading is complete, in order to bypass security checks.

Just as we were about to give up, we suddenly noticed some details in the patch description:

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.

Abnormal objectLength

It mentions that when objectLength is -8, some problems occur. This gave us some additional insight. Can LazyValue still apply normally at this point?

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

The actual readValue starts reading from mPosition, then sequentially reads LazyType and objectLength, and enters the normal Value reading flow, e.g., Parcelable needs to read ClassName and then execute createFromParcel. After reading, it is no different from a normal Key-Value and does not affect subsequent serialization. Revisiting, the core idea of Self-changed Bundle is to modify after reading is complete. This is just an ordinary out-of-bounds read, so this approach seems not to work.

What if LazyValue does not apply during this process? In other words, it continues to participate in IPC as a LazyValue. At this point, its writeToParcel function is called:

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

The entire LazyValue is directly copied over, unless mLength = 0. Wait! As mentioned above, mLength = objectLength + 8 bytes. From the patch information, to trigger the vulnerability, objectLength should be -8, so mLength = 0 holds true. In other words, in this scenario, the entire LazyValue is simply gone, only String Key is copied, resulting in a missing write, and the condition for Self-changed Bundle is directly satisfied.

Knowing the cause, we can start reproducing. However, before that, we still need a few more details.

Detail 1: Two Types of Bundle

Download Tool