|
|
Message-ID: <CAK3hNHapgvw6Se3MrissbL8kJCF7oBbmvxDB6fQ6jjQdx70W_A@mail.gmail.com>
Date: Mon, 13 Apr 2026 20:29:13 -0700
From: Abhinav Agarwal <abhinavagarwal1996@...il.com>
To: oss-security@...ts.openwall.com
Subject: wolfSSL ML-DSA: same-process heap reuse exposes private signing
material, enabling signature forgery
Affected: wolfSSL v5.7.2 through v5.9.0-stable (native ML-DSA builds
with --enable-mldsa / --enable-dilithium; not included in
--enable-all)
Fixed in: PRs #10100, #10113 (development branch, no release)
CWE: CWE-244 / CWE-226
CVE: None assigned.
wolfSSL's ML-DSA-44 signing function (dilithium_sign_with_seed_mu() in
dilithium.c) frees a ~50KB heap block containing private signing
material (s1, s2, t0 in NTT form) without clearing it. For a
same-process attacker able to allocate and read a same-size heap
block, the key material is recoverable. On glibc tcache, the PoC
recovers s1 from the freed block and forges signatures on arbitrary
messages -- verified against the compiled libwolfssl binary on three
Linux distributions and macOS.
wolfSSL already fixed this exact pattern -- missing ForceZero before
free of private key material -- in dilithium keygen (643427040),
ed25519 signing (5f7bc0f3a), and ed448 signing (109e765b5). The ML-DSA
signing path was missed.
FIPS 204 Section 3.6.3 requires implementations "shall ensure that any
potentially sensitive intermediate data is destroyed as soon as it is
no longer needed."
Root Cause
----------
dilithium_sign_with_seed_mu() in wolfcrypt/src/dilithium.c (v5.9.0):
Line 8222: XMALLOC allocates a block holding y, w0, w1, c, z, ct0,
and -- with WC_DILITHIUM_CACHE_PRIV_VECTORS off (default) -- also s1,
s2, t0 in NTT form. For ML-DSA-44, s1 is at offset 21504.
Lines 8417, 8958: XFREE without ForceZero.
The fix is one line:
+ ForceZero(y, allocSz);
XFREE(y, key->heap, DYNAMIC_TYPE_DILITHIUM);
Attack Chain
------------
1. Application signs M1. wolfSSL allocates 50KB block with s1/s2/t0,
frees without zeroing.
2. Same-process code calls malloc(50176). On glibc tcache, this
returns the same block. Read s1 from offset 21504.
3. Forge a signature on different message M2 using s1 + public key
via hint reconstruction (no s2 or t0 needed).
4. wc_dilithium_verify_msg() accepts the forged signature.
s1 is the static signing key. One recovery = full signing key compromise.
Proof of Concept
----------------
poc_heap_forgery_v2.c -- end-to-end forgery (includes dilithium.c
for access to static NTT functions).
verify_forged.c -- companion verifier linked against the compiled
libwolfssl binary, not inlined code.
$ ./poc
--- heap reuse ---
got 12530/12544 nonzero dwords in block
s1 at offset 21504: 1024/1024 nonzero
--- forging sig on m2 ---
forged on attempt 2 (kf=4)
FORGERY OK - wc_dilithium_verify_msg accepted forged sig on m2
$ ./verify_forged
VERIFIED - linked libwolfssl accepted the forged signature
Tested on v5.9.0-stable, gcc -O2: Ubuntu 22.04 x86_64 (10/10), Amazon
Linux 2023 (5/5), Ubuntu 20.04 (5/5), macOS ARM64 (pass).
Source + build instructions + full advisory:
https://abhinavagarwal07.github.io/posts/wolfssl-mldsa-forgery/
Vendor Response
---------------
wolfSSL confirmed the finding, patched it within two days (PRs #10100,
#10113), and credited me in the commits. wolfSSL evaluated the
heap-reuse PoC and acknowledged it is "correct in using the data
obtained from the heap buffer." They classified the finding as a bug
rather than a vulnerability: "an attacker cannot simply call malloc()
after the signing operation and then read all that memory."
Timeline
--------
2026-03-28 Report sent to wolfSSL with forgery PoC
2026-03-30 wolfSSL confirmed. PR #10100.
2026-03-30 Heap block addendum + patch sent.
2026-03-31 PR #10113. Declined CVE.
2026-04-02 wolfSSL reaffirmed. Ticket closed.
2026-04-09 Heap-reuse PoC sent to wolfSSL.
2026-04-10 wolfSSL evaluated, acknowledged correctness, maintained
classification.
2026-04-13 Public disclosure.
References
----------
https://github.com/wolfSSL/wolfssl/pull/10100
https://github.com/wolfSSL/wolfssl/pull/10113
FIPS 204: https://csrc.nist.gov/pubs/fips/204/final (Section 3.6.3)
Abhinav Agarwal
https://github.com/abhinavagarwal07
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.