Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
ReparcelBug2 — # CVE-2021-0928 के माध्यम से Android 12 Beta पर इंस्टॉल किए गए ऐप से सिस्टम विशेषाधिकार वृद्धि के लिए राइटअप और एक्सप्लॉइट, जो `OutputConfiguration` में `writeToParcel`/`createFromParcel` सीरियलाइज़ेशन बेमेल है | Kitploit
उपकरण/GitHubGitHub/michalbednarski/reparcelbug2
एंड्रॉइड सुरक्षाविशेषाधिकार वृद्धिभेद्यता विश्लेषणशोषणमोबाइल सुरक्षाबाइनरी विश्लेषण
GitHubmichalbednarski/reparcelbug2

ReparcelBug2

# CVE-2021-0928 के माध्यम से Android 12 Beta पर इंस्टॉल किए गए ऐप से सिस्टम विशेषाधिकार वृद्धि के लिए राइटअप और एक्सप्लॉइट, जो `OutputConfiguration` में `writeToParcel`/`createFromParcel` सीरियलाइज़ेशन बेमेल है

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें
रिपॉजिटरी देखें
12524544 साल पहलेKitploit द्वारा समीक्षित

CVE-2021-0928, android.hardware.camera2.params.OutputConfiguration में writeToParcel/createFromParcel सीरियलाइज़ेशन बेमेल

यह उस कमज़ोरी का उपयोग करने वाला एक्सप्लॉइट है, जो इंस्टॉल किए गए Android ऐप से Android Settings ऐप में विशेषाधिकार वृद्धि (privilege escalation) के लिए है (या किसी अन्य इंस्टॉल किए गए ऐप में, जिसे AndroidManifest.xml में घोषित <receiver> को भेजा जा सकता है; <activity> को भेजकर भी विशेषाधिकार वृद्धि संभव थी, हालाँकि यहाँ प्रस्तुत नहीं की गई है)

मुझे यह समस्या मूल रूप से Android 12 Developer Preview 3 पर मिली

इस रिपॉजिटरी में मौजूद एक्सप्लॉइट संस्करण Android 12 Beta 2 और 3 पर काम करता है

यह कमज़ोरी पहले आधिकारिक Android 12 रिलीज़ में ठीक कर दी गई थी

नीचे लिखा गया विवरण मूल रूप से Google के लिए लिखा गया था, ताकि इस रिपोर्ट को पूर्ण एक्सप्लॉइट श्रृंखला के रूप में विचार किया जा सके

लेखन के समय Android 12 AOSP में उपलब्ध नहीं था (Android Developer Preview/Beta रिलीज़ ओपन सोर्स नहीं हैं)

Settings ऐप से Android सूचना का स्क्रीनशॉट: Hello from uid=1000(system) gid=1000(system) groups=1000(system),1007(log),1065(reserved_disk),1077(external_storage),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct),9997(everybody) context=u:r:system_app:s0

Parcel का परिचय

Android पर अधिकांश IPC Parcel नामक क्लास के माध्यम से किया जाता है

Parcel का मूल उपयोग इस प्रकार है:```java Parcel p = Parcel.obtain(); p.writeInt(1); p.writeString("Hello");

root@kitploit:~
फिर `Parcel` को [Binder के माध्यम से](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) किसी अन्य प्रक्रिया में भेजा जाता है। वैकल्पिक रूप से, परीक्षण के लिए कोई [`p.setDataPosition(0)`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) को कॉल करके parcel को शुरुआती स्थिति में वापस ला सकता है और पढ़ना शुरू कर सकता है:```java
int a = p.readInt(); // a = 1
String b = p.readString(); // b = "Hello"

यह ध्यान दिया जाना चाहिए कि parcel आंतरिक रूप से उस स्थिति को रखता है जहाँ से reads किए जाते हैं। Parcel class के उपयोगकर्ता की ज़िम्मेदारी है कि वह सुनिश्चित करे कि read* methods पहले उपयोग किए गए write* methods से मेल खाते हों, अन्यथा बाद के reads buffer में गलत स्थितियों से होंगे

Parcel custom objects लिखने की क्षमता भी प्रदान करता है, ऐसा करने का पसंदीदा तरीका Parcelable interface को implement करना है

यहाँ Parcelable interface के implementation का उदाहरण है (अप्रासंगिक कोड हटा दिया गया है, WindowContainerTransaction class का उपयोग exploit में gadget chain के हिस्से के रूप में किया जाता है, हालाँकि इसमें कुछ भी गलत नहीं है)```java package android.window; public final class WindowContainerTransaction implements Parcelable { private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>(); private final ArrayList mHierarchyOps = new ArrayList<>();

root@kitploit:~
private WindowContainerTransaction(Parcel in) {
    in.readMap(mChanges, null /* loader */);
    in.readList(mHierarchyOps, null /* loader */);
}

@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
    dest.writeMap(mChanges);
    dest.writeList(mHierarchyOps);
}

@NonNull
public static final Creator<WindowContainerTransaction> CREATOR =
        new Creator<WindowContainerTransaction>() {
            @Override
            public WindowContainerTransaction createFromParcel(Parcel in) {
                return new WindowContainerTransaction(in);
            }
        };

}

root@kitploit:~
जैसा कि ऊपर देखा जा सकता है, लिखते समय `writeToParcel()` विधि का उपयोग किया जाता है। फिर पढ़ते समय `CREATOR.createFromParcel()` फैक्ट्री विधि को कॉल किया जाता है। यह `Parcelable` कार्यान्वयन की जिम्मेदारी है कि यह सुनिश्चित करे कि `createFromParcel` उतना ही डेटा पढ़े जितना `writeToParcel` द्वारा लिखा गया था, अन्यथा उस `Parcel` से बाद में किए गए सभी रीड गलत ऑफसेट से डेटा पढ़ेंगे

ऐसी क्लास को `Parcel` में/से निम्नलिखित तरीकों से लिखा/पढ़ा जा सकता है:

* सीधे `obj.writeToParcel(parcel, 0)` / `obj = WindowContainerTransaction.CREATOR.createFromParcel()` कॉल करके, इसका उपयोग अक्सर तब किया जाता है जब क्लास का प्रकार ज्ञात हो, उदाहरण के लिए जब `Parcelable` में अलग `Parcelable` का फ़ील्ड हो या AIDL द्वारा जनरेट किए गए कोड में जब परिभाषित RPC विधि में `Parcelable` तर्क के रूप में हो
* `Parcel.writeParcelable`/`readParcelable` के माध्यम से। [`writeParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=1909;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) पहले क्लास का नाम लिखता है और फिर `Parcelable` इंटरफ़ेस से `writeToParcel` विधि को कॉल करता है। [`readParcelable`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=3282;drc=58787794eb5c879f6e39ee58c0071b36e337f8e3) लिखा गया क्लास नाम पढ़ता है, प्रदान किए गए `ClassLoader` में उस नाम वाली क्लास ढूंढता है या यदि null प्रदान किया गया था तो `BOOTCLASSPATH` में। एक बार क्लास मिल जाने पर उसका स्टैटिक फ़ील्ड `CREATOR` का उपयोग [`Parcelable.Creator`](https://developer.android.com/reference/android/os/Parcelable.Creator) इंस्टेंस प्राप्त करने के लिए किया जाता है जो उस क्लास को पढ़ने के लिए उपयोग की जाने वाली फैक्ट्री है। यह ध्यान रखना महत्वपूर्ण है कि जब `readParcelable` विधि का उपयोग किया जाता है तो यह क्लास पथ में उपलब्ध किसी भी `Parcelable` को पढ़ सकती है क्योंकि बनाई जाने वाली वस्तु का नाम उसी `Parcel` से पढ़ा जाता है
* `readParcelable` का उपयोग कई अन्य `Parcel` विधियों द्वारा किया जाता है, उदाहरण के लिए ऊपर दिए गए उदाहरण में देखा गया `readList` तत्वों को `readValue` के माध्यम से पढ़ता है, जो `Parcel` में वस्तुओं को स्थानांतरित करने का सबसे सामान्य तरीका है और इसके उपयोग के तरीकों में से एक `readParcelable` के माध्यम से है। साथ ही ऊपर दिए गए उदाहरण में Java के Type Erasure के कारण `ArrayList<HierarchyOp> mHierarchyOps` फ़ील्ड वास्तव में Parcel द्वारा समर्थित कोई भी वस्तु रख सकता है, न कि केवल वे जो जेनेरिक प्रकार घोषणा में निर्दिष्ट प्रकार के अनुकूल हैं

# `writeToParcel`/`createFromParcel` बेमेल

जैसा कि ऊपर बताया गया है, यह `Parcelable` इंटरफ़ेस कार्यान्वयन की जिम्मेदारी है कि यह सुनिश्चित करे कि `createFromParcel` `Parcel` से उतना ही डेटा पढ़े जितना मेल खाते `writeToParcel` ने पहले लिखा था। जब भी `BOOTCLASSPATH` में कोई `Parcelable` होता है जो इस अनुबंध का उल्लंघन कर सकता है, यह एक भेद्यता पैदा करता है क्योंकि यह निम्नलिखित परिदृश्य की अनुमति देता है:

1. एक दुर्भावनापूर्ण ऐप `system_server` को एक `Bundle` या `Parcelable` भेजता है जिसमें दोषपूर्ण `Parcelable` इंस्टेंस के साथ-साथ विशेष रूप से निर्मित डेटा होता है जो वास्तव में चरण 3 में पढ़ा जाएगा लेकिन चरण 2 के दौरान शब्दशः पारित किया जाता है
2. `system_server` सत्यापित करता है कि `Bundle` सुरक्षित है और फिर इसे आगे भेजता है या `system_server` प्रदान किए गए `Parcelable` को AIDL विधि में पास करता है जिसमें अगले पैरामीटर में महत्वपूर्ण डेटा भी पारित होता है (यदि उस पैरामीटर में प्राप्त डेटा को संशोधित किया जा सकता है तो यह सुरक्षा समस्या पैदा करेगा)
3. एक अन्य ऐप `system_server` से डेटा प्राप्त करता है और उस पर भरोसा करता है, हालांकि दोषपूर्ण सीरियलाइज़ेशन के कारण वास्तव में जो डेटा वह देखता है वह उस डेटा से भिन्न होता है जो `system_server` भेजने का इरादा रखता था

मैंने उपरोक्त चरणों में "OR" का उपयोग किया है क्योंकि ये चरण दोनों का वर्णन करते हैं: एक [पुराना एक्सप्लॉइट वेरिएंट जो मनमाना Activity शुरू करने की ओर ले जाता है जिसे मैंने 2017 में प्रकाशित किया था](https://github.com/michalbednarski/ReparcelBug) ("OR" के बाईं ओर) और एक नया वेरिएंट जिसका मैं अगले अनुभाग में यहाँ वर्णन करूंगा

# ऐप में `BroadcastReceiver` कैसे निष्पादित होता है

Android SDK में उपलब्ध APIs का उपयोग करने वाले एप्लिकेशन डेवलपर के दृष्टिकोण से, [`BroadcastReceiver`](https://developer.android.com/guide/components/broadcasts) जिस तरह से काम करता है वह यह है कि एक एप्लिकेशन [`sendBroadcast`](https://developer.android.com/reference/android/content/Context#sendBroadcast(android.content.Intent)) को कॉल करता है (हालांकि अक्सर ऐप्स सिस्टम से Broadcasts प्राप्त करना चाहते हैं, ऐप से नहीं) और फिर प्रसारित Intent को `AndroidManifest.xml` में परिभाषित `<receiver>` से मिलान किया जाता है, जब ऐसा होता है तो सिस्टम प्राप्त करने वाले एप्लिकेशन की प्रक्रिया शुरू करता है, `<receiver android:name>` विशेषता में परिभाषित `BroadcastReceiver` उपवर्ग को इंस्टैंशिएट करता है और फिर [`onReceive`](https://developer.android.com/reference/android/content/BroadcastReceiver#onReceive(android.content.Context,%20android.content.Intent)) विधि को कॉल करता है

आइए broadcast प्राप्त करने वाली प्रक्रिया में होने वाले `system_server` के साथ संचार पर एक नज़र डालें:

* जब एप्लिकेशन प्रक्रिया शुरू में शुरू होती है तो यह [कॉल करती है `IActivityManager.attachApplication()`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java;l=7340;drc=f53e23b917aa0f6a6310e46a233a29b6d6226b2c), ऐसा करके यह [`IApplicationThread`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/IApplicationThread.aidl) हैंडल पास करती है जिसका उपयोग सिस्टम द्वारा एप्लिकेशन प्रक्रिया को बताने के लिए किया जाता है कि क्या करना है
* जब सिस्टम एप्लिकेशन प्रक्रिया में मैनिफेस्ट-पंजीकृत `BroadcastReceiver` को निष्पादित करना चाहता है, तो यह पिछले बिंदु में वर्णित `IApplicationThread` का उपयोग करके [`scheduleReceiver`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java;l=950;drc=f53e23b917aa0f6a6310e46a233a29b6d6226b2c) विधि को कॉल करता है। इस विधि के कई तर्क हैं लेकिन यहाँ सबसे महत्वपूर्ण पहले दो हैं:
  1. `Intent intent`, जो पहले सिस्टम को तब पारित किया गया था जब `sendBroadcast()` को कॉल किया गया था
  2. `ActivityInfo info`, जिसमें उस घटक के बारे में जानकारी होती है जिसे निष्पादित किया जाना है। इस पैरामीटर का मान सिस्टम द्वारा Package Manager Service से लिया जाता है। सबसे महत्वपूर्ण बात, इस पैरामीटर में पारित डेटा में उस फ़ाइल का पथ शामिल होता है जिससे प्राप्त broadcast को संभालने वाली Java क्लास लोड की जाएगी

इस बिंदु पर आप शायद अनुमान लगा सकते हैं कि यह नया एक्सप्लॉइट पथ क्या है: `sendBroadcast()` को एक ऐसा `Intent` पारित करके कॉल करें जो यह कारण बनेगा कि जब सिस्टम `scheduleReceiver` को कॉल करने का प्रयास करेगा तो यह उस एप्लिकेशन में छेड़छाड़ किए गए `ActivityInfo` को देखेगा जिसमें `scheduleReceiver` को लागू किया गया है

यह ध्यान दिया जाना चाहिए कि यह नया एक्सप्लॉइट पथ Android 12 में व्यवहार्य हो गया क्योंकि पहले `Intent` में मनमाना `Parcelable` डालने का कोई तरीका नहीं था ([Intent extras](https://developer.android.com/reference/android/content/Intent#putExtra(java.lang.String,%20android.os.Parcelable)) गिनती नहीं करते क्योंकि वे `Bundle` में डाले जाते हैं जिसकी पूरी लंबाई Parcel में लिखी जाती है और [एकल ब्लॉब के रूप में पढ़ी जाती है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/BaseBundle.java;l=1671-1681;drc=5d123b67756dffcfdebdb936ab2de2b29c799321), इसलिए extras उन्हें समाहित करने वाले `Intent` ऑब्जेक्ट की गलत व्याख्या नहीं कर सकते)

# `writeToParcel`/`createFromParcel` बेमेल को ट्रिगर करना

अधिकांश समय `writeToParcel`/`createFromParcel` बेमेल ऐसे मामले होते हैं जहां इन विधियों में से एक में फ़ील्ड में से एक भूल जाता है या दो बार लिखा जाता है, ऐसे मामले में ऐसी वस्तु भेजना हमेशा बेमेल को ट्रिगर करेगा। (अधिकांश समय ऐसा तब होता है जब वस्तु `Parcelable` होते हुए भी वास्तव में प्रक्रियाओं के बीच उपयोग नहीं की जाती है, अन्यथा सामान्य उपयोग के दौरान यह जल्दी से ध्यान में आ जाता)

हालांकि इस बार ऐसा नहीं था और बेमेल को ट्रिगर करना स्पष्ट नहीं है

आइए भेद्य क्लास पर एक नज़र डालें ([मूल यहाँ था](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/hardware/camera2/params/OutputConfiguration.java;drc=46c390a1c695e2dc458cb889e40559f259f60aed), `// New in Android 12` चिह्नित पंक्तियाँ मैन्युअल रूप से जोड़ी गई थीं क्योंकि वे लिखने के समय AOSP में मौजूद नहीं थीं) ([यहाँ वह कमिट है जिसने मूल रूप से भेद्यता पेश की थी](https://android.googlesource.com/platform/frameworks/base/+/a69b1bc58b0838e06deefb190e226774a34671e6%5E%21/#F10), हालांकि यह Android 12 जारी होने के बाद प्रकाशित किया गया था)```java
package android.hardware.camera2.params;

public final class OutputConfiguration implements Parcelable {
    private OutputConfiguration(@NonNull Parcel source) {
        int rotation = source.readInt();
        int surfaceSetId = source.readInt();
        int surfaceType = source.readInt();
        int width = source.readInt();
        int height = source.readInt();
        boolean isDeferred = source.readInt() == 1;
        boolean isShared = source.readInt() == 1;
        ArrayList<Surface> surfaces = new ArrayList<Surface>();
        source.readTypedList(surfaces, Surface.CREATOR);
        String physicalCameraId = source.readString();
        boolean isMultiResolution = source.readInt() == 1; // New in Android 12
        ArrayList<Integer> sensorPixelModesUsed = new ArrayList<Integer>(); // New in Android 12
        source.readList(sensorPixelModesUsed, Integer.class.getClassLoader()); // New in Android 12

		// SNIP: copy values from variables set above to fields of this class
    }

    public static final @android.annotation.NonNull Parcelable.Creator<OutputConfiguration> CREATOR =
            new Parcelable.Creator<OutputConfiguration>() {
        @Override
        public OutputConfiguration createFromParcel(Parcel source) {
            try {
                OutputConfiguration outputConfiguration = new OutputConfiguration(source);
                return outputConfiguration;
            } catch (Exception e) {
                Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
                return null;
            }
        }

        @Override
        public OutputConfiguration[] newArray(int size) {
            return new OutputConfiguration[size];
        }
    };

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        if (dest == null) {
            throw new IllegalArgumentException("dest must not be null");
        }
        dest.writeInt(mRotation);
        dest.writeInt(mSurfaceGroupId);
        dest.writeInt(mSurfaceType);
        dest.writeInt(mConfiguredSize.getWidth());
        dest.writeInt(mConfiguredSize.getHeight());
        dest.writeInt(mIsDeferredConfig ? 1 : 0);
        dest.writeInt(mIsShared ? 1 : 0);
        dest.writeTypedList(mSurfaces);
        dest.writeString(mPhysicalCameraId);
        dest.writeInt(mIsMultiResolution ? 1 : 0); // New in Android 12
        dest.writeList(mSensorPixelModesUsed); // New in Android 12
    }

    private ArrayList<Surface> mSurfaces;
    private final int mRotation;
    private final int mSurfaceGroupId;
    private final int mSurfaceType;
    private final Size mConfiguredSize;
    private final int mConfiguredFormat;
    private final int mConfiguredDataspace;
    private final int mConfiguredGenerationId;
    private final boolean mIsDeferredConfig;
    private boolean mIsShared;
    private String mPhysicalCameraId;
    private boolean mIsMultiResolution; // New in Android 12
    private ArrayList<Integer> mSensorPixelModesUsed; // New in Android 12
}

तो यहाँ क्या गलत है और यह बदलाव भेद्यता क्यों पेश करता है? जैसा कि मैंने WindowContainerTransaction उदाहरण Parcelable पर चर्चा करते हुए कहा था, readList वास्तव में सूची को Parcel द्वारा समर्थित किसी भी ऑब्जेक्ट से भर सकता है, न कि केवल उन्हीं से जो जेनेरिक घोषणा (ArrayList<Integer>) से मेल खाते हैं। हालाँकि, चूँकि हम इस क्लास का उपयोग केवल सीरियलाइज़ेशन गैजेट श्रृंखला के हिस्से के रूप में कर रहे हैं और वास्तव में इसका उपयोग नहीं कर रहे हैं, वह फ़ील्ड Parcel में पढ़ने और लिखने के अलावा किसी और चीज़ के लिए उपयोग नहीं की जाएगी (और ArrayList से उन तत्वों का उपयोग करने का प्रयास जो इसकी जेनेरिक घोषणा से मेल नहीं खाते, वैसे भी केवल ClassCastException की ओर ले जाएगा), यह अपने आप में कोई समस्या नहीं है

इस क्लास में createFromParcel के भीतर एक try-catch भी है, जिसका अर्थ है कि यदि पढ़ने के दौरान कोई Exception फेंका जाता है, तो OutputConfiguration का पढ़ना रोक दिया जाएगा और OutputConfiguration वाले ऑब्जेक्ट का पढ़ना आगे बढ़ेगा। जब ऐसा होता है, तो पूरा OutputConfiguration Parcel में लिखा जाएगा, लेकिन इसे केवल उस बिंदु तक पढ़ा जाएगा जहाँ Exception हुआ था। यह एक बेमेल पैदा करता है क्योंकि OutputConfiguration.writeToParcel के भीतर लिखा गया अप्रयुक्त डेटा वास्तव में उस ऑब्जेक्ट द्वारा पढ़ा जाएगा जो OutputConfiguration.CREATOR.createFromParcel को कॉल कर रहा था

अब, इन दोनों का संयोजन (Parcel द्वारा समर्थित मनमाने ऑब्जेक्ट्स को नेस्ट करने की अनुमति देना और उसे बिना rethrow के try-catch में लपेटना) एक ऐसा Parcelable बनाने की क्षमता देता है जिसे system_server द्वारा लिखा जा सकता है और बाद में इस तरह से पढ़ा जा सकता है जो उस ऐप द्वारा नियंत्रित होता है जिसने शुरू में Parcelable का निर्माण किया था और जिसे system_server द्वारा आगे भेजा जा रहा है

ठीक है, इसलिए ऐसा Parcelable बनाने के लिए अब हमें mSensorPixelModesUsed में रखने के लिए कुछ ऐसा खोजना होगा जो system_server में सफलतापूर्वक पढ़ा जा सके (क्योंकि यह ऑब्जेक्ट हमलावर ऐप से Parcel के माध्यम से प्राप्त हो रहा है), system_server द्वारा सफलतापूर्वक लिखा जा सके और फिर पीड़ित ऐप में unparcel करने में विफल होकर एक Exception फेंक दे

ऐसा करने के तरीकों में से एक ऐसी क्लास का उपयोग करना है जो system_server के भीतर मौजूद है लेकिन ऐप्स में नहीं, ताकि इसे deserialize करने का प्रयास ClassNotFoundException की ओर ले जाए। हालाँकि, मैं system_server से एक Parcelable नहीं चुन सकता क्योंकि बिना स्पष्ट रूप से निर्दिष्ट ClassLoader के readParcelable केवल BOOTCLASSPATH खोजेगा जिसमें system_server विशिष्ट क्लासेस नहीं होंगी। उस समस्या का समाधान Serializable क्लासेस में से एक का उपयोग करना है क्योंकि ObjectInputStream स्टैक ट्रेस में पहली गैर-BOOTCLASSPATH विधि से ClassLoader चुनेगा

मैंने PackageManagerException चुना है, हालाँकि इसका उपयोग करने से पहले हमें एक और काम करना होगा। OutputConfiguration कंस्ट्रक्टर में जब readList कॉल किया जाता है, तो loader तर्क स्पष्ट रूप से Integer.class.getClassLoader() पर सेट होता है। वह loader मान readValue() में प्रचारित होता है, फिर to readSerializable() और readSerializable() के भीतर यदि loader पैरामीटर null नहीं है तो इसका उपयोग ObjectInputStream से के बजाय किया जाता है (वह जाँच कुछ नहीं करती क्योंकि जब को क्लास नहीं मिलती है तो यह null लौटाने के बजाय exception फेंकेगा)। हालाँकि, इसका तरीका काफी सरल है, हमें बस को किसी ऐसे में लपेटना होगा जो बिना निर्दिष्ट किए करता है। यहीं पर ऊपर वर्णित क्लास आती है

तो, इस बिंदु पर हमारे पास निम्नलिखित ऑब्जेक्ट है:

  • OutputConfiguration
    • mSensorPixelModesUsed.get(0) = WindowContainerTransaction
      • mHierarchyOps.get(0) = PackageManagerException

अब ऐसा ऑब्जेक्ट system_server के भीतर सफलतापूर्वक deserialized हो सकता है: जब WindowContainerTransaction readList कॉल करता है तो यह सिस्टम सर्वर के ClassLoader (न कि BootClassLoader) का उपयोग करके PackageManagerException क्लास खोजने का प्रयास करेगा, क्योंकि यह इसे स्टैक ट्रेस में पा सकता है। वह क्लास लोडर स्टैक ट्रेस में मौजूद होता है क्योंकि जबकि निम्नलिखित सभी विधियाँ सिस्टम सर्वर क्लास पथ से नहीं थीं: Binder#execTransact(), AIDL द्वारा उत्पन्न IActivityManager$Stub#onTransact() और सभी उपयोग की गई Parcelable क्लासेस की विधियाँ, स्टैक ट्रेस में सिस्टम सर्वर के भीतर घोषित एक विधि थी: ActivityManagerService के भीतर एक ओवरराइड किया गया onTransact। इसलिए system_server ऐसी वस्तु को में पढ़ और बाद में लिख सकता है और जब लक्षित ऐप इसे पढ़ने का प्रयास करता है तो क्लास उपलब्ध नहीं होगी और इसलिए फेंका जाएगा, और फिर

इसलिए हमने यह बेमेल ट्रिगर किया। खैर, इस बिंदु पर अभी तक वास्तव में नहीं क्योंकि exception तब पकड़ा गया था जब OutputConfiguration.writeToParcel द्वारा कोई अपठित डेटा नहीं बचा था, लेकिन हम आसानी से mSensorPixelModesUsed List में एक और आइटम जोड़ सकते हैं और वह आइटम Parcel.writeValue के माध्यम से लिखा जाएगा और OutputConfiguration पढ़ने के बाद अपठित छोड़ दिया जाएगा

इसे Intent में रखना

जैसा कि ऊपर बताया गया है, मैं Intent ऑब्जेक्ट से बेमेल ट्रिगर करना चाहूँगा, क्योंकि इसे system_server द्वारा एक AIDL विधि में पारित किया जाएगा जिसमें पहले पैरामीटर में Intent और दूसरे पैरामीटर में निष्पादन जानकारी होती है, ताकि पहले पैरामीटर में पारित Intent का सीरियलाइज़ेशन/डिसीरियलाइज़ेशन दूसरे पैरामीटर में मान के संशोधन की ओर ले जाए

Intent.readFromParcel() में सभी मान समर्पित टाइप की गई विधियों के माध्यम से पढ़े जाते हैं इसलिए वहाँ हम कस्टम Parcelable क्लास निर्दिष्ट नहीं कर सकते

हालाँकि, Intent के भीतर, नेस्टेड ClipData है और Android 12 के बाद से ClipData$Item में एक नया फ़ील्ड ActivityInfo mActivityInfo है (प्रारंभिक लेखन के समय AOSP में मौजूद नहीं था, यहाँ वह commit है जो उस फ़ील्ड को पेश करता है, यह फ़ील्ड ClipData(Parcel in) कंस्ट्रक्टर के अंदर in.readTypedObject(ActivityInfo.CREATOR) के माध्यम से पढ़ा जाता है)

फिर ActivityInfo(Parcel source) कंस्ट्रक्टर के भीतर फिर से कस्टम Parcelable रखने का कोई तरीका नहीं है, लेकिन चूँकि ActivityInfo ComponentInfo से विस्तारित होता है, इसमें applicationInfo फ़ील्ड है

अंत में ApplicationInfo में SparseArray<int[]> splitDependencies फ़ील्ड है, जो readSparseArray के माध्यम से पढ़ा जाता है, जो बदले में SparseArray आइटम पढ़ने के लिए readValue का उपयोग करता है

इस बिंदु पर हम splitDependencies के भीतर OutputConfiguration रख सकते हैं, हालाँकि splitDependencies पढ़ने के बाद कुछ readString8() कॉल होते हैं और बेमेल होने के बाद अप्रयुक्त डेटा पर पूर्ण नियंत्रण रखना अच्छा होगा ताकि हम सीधे वहाँ खाली स्ट्रिंग्स रख सकें और अप्रयुक्त डेटा की विभिन्न व्याख्या के बारे में चिंता न करें

ऐसा करने के लिए, पहले हमें OutputConfiguration.mSensorPixelModesUsed के भीतर कुछ कच्चा डेटा कंटेनर रखना होगा जो writeValue के माध्यम से लिखा जाएगा। मैंने Bundle चुना है। इस तरह अप्रयुक्त डेटा में हमारे पास बचा होगा:

  1. writeValue VAL_BUNDLE टैग
  2. कच्चे डेटा की लंबाई (यह लिंक इस सूची के शेष आइटमों पर भी लागू होता है)
  3. BUNDLE_MAGIC
  4. कच्चा डेटा Parcel.appendFrom के माध्यम से शब्दशः पारित किया गया

इसलिए हमारे पास तीन Parcel.writeInt आइटम हैं जो अप्रयुक्त होंगे, हम OutputConfiguration को किसी ऐसे Parcelable में लपेटकर उनसे छुटकारा पा सकते हैं जो इसे पढ़ते समय मनमाना Parcelable मान पढ़ता है और उसके बाद तीन ints पढ़ता है। मैंने इसे ZenPolicy CREATOR में पाया है

संक्षेप में हमारे पास निम्नलिखित ऑब्जेक्ट पदानुक्रम है (जो system_server में मौजूद है और जिसे यह scheduleReceiver को पारित करने का प्रयास करता है)

  • Intent
    • mClipData = ClipData
      • mItems.get(0).mActivityInfo = ActivityInfo
        • applicationInfo = ApplicationInfo
          • splitDependencies.get(0) = ZenPolicy
            • mVisualEffects.get(0) = OutputConfiguration
              • mSensorPixelModesUsed.get(0) = WindowContainerTransaction
                • mHierarchyOps.get(0) = PackageManagerException
              • mSensorPixelModesUsed.get(1) = Bundle

यह system_server द्वारा लिखा जाता है। फिर प्राप्त करने वाला ऐप सब कुछ सामान्य रूप से पढ़ता है (PackageManagerException के readSerializable डेटा तक और सहित), हालाँकि PackageManagerException के लिए Serializable डेटा पढ़े जाने के बाद एक exception फेंका जाता है और OutputConfiguration के नीचे की हर चीज़ का पढ़ना रद्द कर दिया जाता है, जिससे Bundle अपठित रह जाता है। पढ़ना ZenPolicy पर आगे बढ़ता है जो Bundle के भीतर कच्चे डेटा से पहले आने वाले तीन ints का उपभोग करता है। फिर ApplicationInfo पढ़ना उस डेटा को पढ़ने के साथ आगे बढ़ता है जो पहले Bundle में शब्दशः पारित कच्चा डेटा था। उस कच्चे डेटा का पढ़ना इस स्टैक में शेष ऑब्जेक्ट्स (ApplicationInfo, ActivityInfo, ClipData और ) के साथ जारी रहेगा और फिर उस कच्चे डेटा का उपयोग अगले विधि पैरामीटर को पढ़ने के लिए किया जाएगा

फिर handleReceiver के भीतर क्या होता है

जैसा कि मैंने अभी कहा, अब शेष scheduleReceiver पैरामीटर हमलावर द्वारा नियंत्रित बफर से पढ़े जाते हैं।

आइए देखें कि एक बार उस विधि को लागू करने के बाद क्या होता है।

पहले scheduleReceiver सभी तर्कों से मान पैक करता है और मुख्य थ्रेड पर निष्पादन पारित करने के लिए sendMessage() का उपयोग करता है

अगला, मुख्य थ्रेड पर handleReceiver कॉल किया जाता है

handleReceiver getPackageInfoNoCheck को कॉल करता है, इसे ApplicationInfo पारित करता है जो इसे ActivityInfo के हिस्से के रूप में प्राप्त हुआ था जो scheduleReceiver तर्क को पारित किया गया था

getPackageInfo जाँचता है कि दिए गए नाम वाला पैकेज पहले से कैश में मौजूद है या नहीं और यदि नहीं है तो यह नया LoadedApk इंस्टेंस बनाता है, इसे पहले प्राप्त ApplicationInfo ऑब्जेक्ट पारित करता है (चूँकि हमलावर नया LoadedApk बनाना चाहता है, उस पैकेज का packageName उपयोग किया जाता है जो इस प्रक्रिया में पहले नहीं देखा गया था)

फिर ContextImpl.getClassLoader() विधि का उपयोग किया जाता है, जो पहली बार चलने पर mPackageInfo.getClassLoader() को सौंपता है, जिसमें mPackageInfo एक LoadedApk है जो पिछले पैराग्राफ में बनाया गया था

फिर createOrUpdateClassLoaderLocked है, जो [zipPaths को आबाद करने के लिए makePaths कॉल करता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/LoadedApk.java;l=802;drc=3e308c62708443e24ba44ecac683a7fe4d9a7ac2) ClassLoaderमें उपयोग किए जाने वाले पथों के साथ, फिर उन्हें [जोड़करzipचर को सौंपा जाता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/LoadedApk.java;l=881;drc=3e308c62708443e24ba44ecac683a7fe4d9a7ac2) और वहcreateClassLoader` को पारित किया जाता है

makePaths ApplicationInfo से जानकारी का उपयोग करके zipPaths भरता है, सबसे महत्वपूर्ण रूप से इसमें sourceDir शामिल है। हमलावर ऐप इंजेक्ट किए गए ApplicationInfo को sourceDir के साथ अपने स्वयं के apk के पथ पर सेट करके बनाता है, इसलिए receiver क्लास वास्तव में हमलावर apk से लोड की जाएगी। यह सीधे तौर पर प्रसारण प्राप्त करने वाले ऐप के भीतर हमलावर-नियंत्रित कोड के निष्पादन की ओर ले जाता है

छिपी API जाँचों पर नोट

एक और चीज़ थी जिसे बायपास करने की आवश्यकता थी: छिपी API जाँचें। ये कभी भी सुरक्षा सीमा बनने के लिए नहीं थीं (क्योंकि ऐप हमेशा NDK का उपयोग कर सकता है और अंतर्निहित syscall को सीधे कॉल कर सकता है), लेकिन इस मामले में उन्हें Parcel में मैन्युअल रूप से डेटा लिखकर और फिर readParcelable का उपयोग करके निर्मित ClipData बनाकर बायपास किया गया था। ऐसा ClipData फिर सामान्य रूप से Intent से जोड़ा जा सकता था और फिर sendBroadcast() को पारित किया जा सकता था ताकि प्रसारण भेजना स्वयं केवल सार्वजनिक APIs का उपयोग करके किया गया था

सुधार

उपरोक्त विवरण मूल रूप से Google को भेजा गया था और ऐसा लगता है कि उन्होंने इसका उपयोग किया है क्योंकि इसके परिणामस्वरूप कई सुधार हैं (मुझे लगता है, मेरे पास प्रत्यक्ष कारणता का कोई प्रमाण नहीं है)

Android 12 के साथ जारी:

  • OutputConfiguration और संबंधित क्लासेस से exception निगलना हटा दिया गया
  • OutputConfiguration#mSensorPixelModesUsed अब writeValue के माध्यम से नहीं लिखा जाता है
  • ClipData#mActivityInfo अब Parcel में नहीं लिखा जाता है जब तक कि लेखन के दौरान स्पष्ट रूप से अनुरोध न किया जाए (इसलिए Intent अब BOOTCLASSPATH से मनमाने Parcelables नहीं रख सकता, इस शोषण तकनीक को समाप्त करता है)

लेखन के समय केवल master शाखा पर मौजूद, जारी संस्करणों में नहीं, संभवतः Android 13 में दिखाई देगा (12L में नहीं):

  • Parcel पर नई List पढ़ने की विधियाँ हैं जो आइटम के प्रकार की जाँच करती हैं और अटाइप्ड संस्करणों को deprecated के रूप में चिह्नित किया गया है
  • नई विधि Parcel#enforceNoDataAvail() है जो जाँचती है कि Parcel में कोई अपठित डेटा नहीं बचा है, जो स्पष्ट रूप से RPC कॉल तर्कों को पढ़ने के बाद AIDL द्वारा उपयोग की जाएगी। आमतौर पर मेरे शोषण इस तथ्य पर निर्भर करते थे कि Parcel से सभी डेटा पढ़े जाने के बाद बाकी सब कुछ अनदेखा कर दिया जाता था, अब ऐसा नहीं होगा, हालाँकि मुझे लगता है कि कई मामलों में कोई ऐसा डेटा बना सकता है जो अंत में अंतिम स्थिति में seek करने का कारण बनेगा इसलिए यह मजबूत शमन नहीं है। वैसे भी कभी-कभी ऐसी समस्याएँ स्वाभाविक रूप से अनजाने में होती हैं इसलिए यह उन्हें पकड़ लेगा। इसके बारे में आगे की चर्चा issue #3 में है
  • Bundle में प्रत्येक आइटम की लंबाई अलग से सहेजी जाएगी। यह लगभग पूरी बग क्लास को मार देता है जिससे मैं 2014 से Google को निजी तौर पर बग रिपोर्ट कर रहा हूँ, 2017 में प्रकाशित विवरण और कोड स्वयं लगभग एक साल बाद। यदि मैं सही गिनती कर रहा हूँ तो यह बग क्लास का 8 साल का जीवन होगा (मुझे ईमानदारी से नहीं पता कि यह बहुत है या नहीं, हालाँकि अभी भी गैर-Bundle शोषण वेरिएंट हो सकते हैं, जैसे कि यह वाला (हालाँकि यह वाला पहले ही ठीक हो चुका था))
टूल डाउनलोड करें
resolveClass
c != null
Class.forName
PackageManagerException
Parcelable
ClassLoader
readList
WindowContainerTransaction
Parcel
PackageManagerException
ClassNotFoundException
जो RuntimeException में लपेटा जाता है
OutputConfiguration CREATOR द्वारा पकड़ा जाता है
Intent
handleReceiver