ANT-2026-C6E98TYX · liboqs
other high
Severity Claude high · Security research firm high · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.
ANT-2026-C6E98TYX: 01-liboqs-ntru-uninit-fn-ptr
The NTRU constructors in src/kem/ntru/kem_ntru_.c call OQS_MEM_malloc(sizeof(OQS_KEM)) and assign twelve struct fields but omit encaps_derand at offset 88, leaving it with stale heap residue. The public API OQS_KEM_encaps_derand() at kem.c:645 checks only that kem != NULL before tail-jumping through kem->encaps_derand (compiled as jmp *0x58(%rax)). An attacker who can influence the 112-byte tcache bin before OQS_KEM_new("NTRU-") controls the jump target and all four pointer arguments passed to it. Demonstrated as a reliable (20/20) same-process control-flow hijack to system() on a Release build with glibc malloc; an ML-KEM-512 differential under identical heap groom confirms the NTRU constructor's missing initialization is the sole causal variable.
Target
Project: liboqs
Version: commit ab0e07c3 (upstream open-quantum-safe/liboqs)
Location: src/kem/kem.c:645 (OQS_KEM_encaps_derand); src/kem/ntru/kem_ntru_*.c:11 (six constructors)
Technical Details
ASAN: "SEGV on unknown address ... caused by a READ memory access ... dereference of a high value address" at kem.c:645 — the 0xbe poison pattern in encaps_derand proves the field was never written post-allocation. Root cause is a code-generation template gap: 33 of 39 KEM constructors set encaps_derand, but the six NTRU constructors predate the _derand API extension and were never updated. The dispatcher's guard checks only the struct pointer, not the function pointer it dereferences, so stale heap bytes become the target of a compiler-blessed indirect jump.
Crash trace:
AddressSanitizer:DEADLYSIGNAL
=================================================================
==74==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x564e3eba1e6b bp 0x7fffe2b6a6f0 sp 0x7fffe2b6a6e8 T0)
==74==The signal is caused by a READ memory access.
==74==Hint: this fault was caused by a dereference of a high value address (see register values below). Dissassemble the provided pc to learn which register was used.
#0 0x564e3eba1e6b in OQS_KEM_encaps_derand /workspace/repo/src/kem/kem.c:645
#1 0x564e3eba081f in main /tmp/poc.c:45
#2 0x7f6dd8e27d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#3 0x7f6dd8e27e3f in __libc_start_main_impl ../csu/libc-start.c:392
#4 0x564e3eba0544 in _start (/tmp/poc-asan+0x12544)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /workspace/repo/src/kem/kem.c:645 in OQS_KEM_encaps_derand
==74==ABORTING
Reproduction
- Allocate 112-byte chunks and write the desired jump target (e.g., &system) at offset 88 of each
- Free the chunks so they populate glibc's 112-byte tcache freelist (LIFO)
- Call OQS_KEM_new(OQS_KEM_alg_ntru_hps2048509) — constructor reclaims a groomed chunk via OQS_MEM_malloc and writes fields 0–87 and 96–111, leaving offset 88 untouched
- kem->encaps_derand now holds the attacker-planted pointer
- Call OQS_KEM_encaps_derand(kem, payload, ...) — dispatcher's NULL check on kem passes, tail-call
jmp *0x58(%rax)transfers to attacker target with payload in %rdi
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-C6E98TYX.
Reference: ANT-2026-C6E98TYX
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Ada Logics.
- Verdict
- true positive
- Severity
- high
Dates from discovery through public reveal.
- 2026-03-31 Reported to tracker
- 2026-05-07 Sent to maintainer
- 2026-05-12 Maintainer acknowledged
- 2026-06-11 Patch released
- 2026-08-17 Publicly revealed
SHA-3-512 hash:
036d8ef5dc823244713d07925545a3daa8e7f2b0dcb640d5e603e0b5af1ee56b0006b0a2cc12123774a58528d66c64171413eaaa9c6d62df3ede3fc27b30d600
Committed 2026-05-07 00:08 PT
Revealed 2026-08-17 10:47 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-C6E98TYX",
"bug_class": "Uninitialized Function Pointer (CWE-824)",
"claude_severity": "high",
"commit_sha": null,
"created_at": "2026-04-01T02:20:52+00:00",
"description": "The NTRU constructors in src/kem/ntru/kem_ntru_*.c call OQS_MEM_malloc(sizeof(OQS_KEM)) and assign twelve struct fields but omit encaps_derand at offset 88, leaving it with stale heap residue. The public API OQS_KEM_encaps_derand() at kem.c:645 checks only that kem != NULL before tail-jumping through kem->encaps_derand (compiled as `jmp *0x58(%rax)`). An attacker who can influence the 112-byte tcache bin before OQS_KEM_new(\"NTRU-*\") controls the jump target and all four pointer arguments passed to it. Demonstrated as a reliable (20/20) same-process control-flow hijack to system() on a Release build with glibc malloc; an ML-KEM-512 differential under identical heap groom confirms the NTRU constructor's missing initialization is the sole causal variable.",
"discovered_at": null,
"location": "src/kem/kem.c:645 (OQS_KEM_encaps_derand); src/kem/ntru/kem_ntru_*.c:11 (six constructors)",
"poc_sha256": null,
"preimage_version": 1,
"project": "liboqs",
"reproduction": [
"1. Allocate 112-byte chunks and write the desired jump target (e.g., &system) at offset 88 of each",
"2. Free the chunks so they populate glibc's 112-byte tcache freelist (LIFO)",
"3. Call OQS_KEM_new(OQS_KEM_alg_ntru_hps2048509) — constructor reclaims a groomed chunk via OQS_MEM_malloc and writes fields 0–87 and 96–111, leaving offset 88 untouched",
"4. kem->encaps_derand now holds the attacker-planted pointer",
"5. Call OQS_KEM_encaps_derand(kem, payload, ...) — dispatcher's NULL check on kem passes, tail-call `jmp *0x58(%rax)` transfers to attacker target with payload in %rdi"
],
"technical_details": "ASAN: \"SEGV on unknown address ... caused by a READ memory access ... dereference of a high value address\" at kem.c:645 — the 0xbe poison pattern in encaps_derand proves the field was never written post-allocation. Root cause is a code-generation template gap: 33 of 39 KEM constructors set encaps_derand, but the six NTRU constructors predate the _derand API extension and were never updated. The dispatcher's guard checks only the struct pointer, not the function pointer it dereferences, so stale heap bytes become the target of a compiler-blessed indirect jump.",
"title": "01-liboqs-ntru-uninit-fn-ptr",
"vendor_severity": "high"
}