Out-of-Bounds Read via Unvalidated IPv4 IHL Field in FastNetMon Community Edition
- CVE
- CVE-2026-48682
- CVSS
- 7.5 (High)
- CWE
- CWE-125 (Out-of-bounds Read), CWE-20 (Improper Input Validation)
- Affected
- FastNetMon Community Edition <= 1.2.9
- Component
- src/simple_packet_parser_ng.cpp, lines 130-164
- Attack Vector
- Remote (sFlow / NetFlow / PCAP packet capture)
- Discovered by
- Lorikeet Security
FastNetMon Community Edition is widely deployed by network operators, hosting providers, and ISPs as a fast flow-based DDoS detection and mitigation engine. It ingests NetFlow, sFlow, IPFIX, and raw mirrored traffic, parses every packet header to extract source, destination, ports, protocol, TCP flags, and packet sizes, and then matches that traffic against per-customer thresholds. Because the parser sits at the very front of the pipeline, any bug in it is reachable from anything that can deliver a packet to a capture interface — which, in production, is the entire internet upstream of the protected network.
During a comprehensive source code audit of FastNetMon Community Edition 1.2.9 in April 2026, Lorikeet Security identified an out-of-bounds read in the IPv4 parser. The bug is in src/simple_packet_parser_ng.cpp. The fault is small — one missing bounds check on a 4-bit field — but the consequences span denial of service, information disclosure of adjacent process memory, and, in protocol-aware downstream parsers, type confusion between an IPv4 header and a TCP or UDP header. MITRE assigned CVE-2026-48682 shortly after our request.
Disclosure status: Lorikeet Security reported this issue to FastNetMon LTD (Pavel Odintsov) on April 25, 2026, using the contact published in the project's SECURITY.md. CVE IDs were assigned by MITRE shortly thereafter. As of the publication of this report (May 23, 2026), no vendor response has been received and no fix has been released. Operators running FastNetMon Community Edition 1.2.9 or earlier should treat the parser as untrusted and apply the mitigations described below.
What the bug is
Every IPv4 header begins with a 4-bit Internet Header Length (IHL) field. Per RFC 791, IHL counts the number of 32-bit words in the IPv4 header, including any options. The legal range is 5 (a 20-byte header with no options) through 15 (a 60-byte header packed with options). Many fast-path parsers, including FastNetMon's, validate that the packet is at least the size of a minimal 20-byte IPv4 header before they touch IHL, and then advance their parsing pointer by 4 * IHL bytes to skip past any options and reach the L4 header.
The FastNetMon parser does the first half of that pattern but skips the second half. At line 164 of simple_packet_parser_ng.cpp, the local parsing pointer is advanced by 4 * ipv4_header->get_ihl() with no check that:
- IHL is at least 5 (the minimum legal value), and
4 * IHLbytes are actually present in the packet buffer.
Because IHL is 4 bits, the field can carry values 0 through 15. With only the minimum 20 bytes validated, an attacker who sends a packet with IHL=15 causes the pointer to advance by 60 bytes — 40 bytes past the validated boundary. Subsequent reads of what the parser believes to be a TCP, UDP, ICMP, or GRE header land squarely in heap memory or stack memory that does not belong to this packet.
The other failure mode is the inverse. If the attacker sets IHL to 0, 1, 2, 3, or 4, the pointer advances by 0, 4, 8, 12, or 16 bytes — never reaching the end of the validated 20-byte IPv4 header. The parser then dereferences the remaining bytes of the IPv4 header itself as if they were a TCP or UDP header. The protocol number (which lives in the IPv4 header at byte 9) is now reinterpreted as a source port. Total length, flags, and other IPv4 fields collide with the L4 layout. This is classic type confusion, and it produces wild values that propagate forward into thresholding, flow records, and any downstream consumer of FastNetMon's parsed metadata.
The code path, in one paragraph
The parsing function takes a pointer to a packet buffer and a length. It checks length >= sizeof(ipv4_header_t), casts the buffer to an ipv4_header_t*, and then advances a local pointer by 4 * ipv4_header->get_ihl(). From there it casts the new pointer location to tcp_header_t*, udp_header_t*, or other L4 structures depending on the protocol field. The bug is that the only length check ever performed compares against 20 bytes (the fixed IPv4 header), never against the actual ihl-adjusted offset.
The reachability surface
FastNetMon is a flow collector and a packet inspector. Every capture mechanism feeds the same simple_packet_parser_ng function. The reachability surface is therefore the union of every input plugin that delivers IPv4 packet data:
| Capture plugin | How an attacker reaches it |
|---|---|
| AF_PACKET / mirrored traffic | Any packet observable on the mirror port. In practice, this is anything the protected network handles. |
| PCAP capture | Same as above for live captures. Also reachable via crafted .pcap files if an analyst replays captures into the engine. |
| NetFlow v9 / IPFIX with sampled packet headers (UDP/2055 on 0.0.0.0) | Some templates carry raw L2/L3 bytes of a sampled packet. The parser is invoked on the sampled fragment. The collector binds on all interfaces by default. |
| sFlow raw_packet samples (UDP/6343 on 0.0.0.0) | sFlow agents on routers and switches forward raw header bytes of sampled packets. These bytes are attacker-influenced when the attacker is the originator of traffic that gets sampled. Collector listens on all interfaces by default. |
This matters because FastNetMon is rarely deployed in a position where the input is trusted. The entire point of a DDoS detector is to look at hostile traffic. There is no narrowing of the attack surface based on authentication or peer identity — if a packet header reaches the parser, the attacker has reached the parser.
Why this matters
The out-of-bounds read alone delivers two impacts that matter to operators.
Information disclosure. FastNetMon processes packets in long-running daemon threads. The heap memory that lies immediately past a recently-parsed packet buffer is whatever the allocator handed out recently — other packets, flow records, BGP peer state, configuration buffers, TLS session material if telemetry reporting is active. The over-read does not return that memory to the attacker directly, but the downstream parser uses those bytes to populate flow records (source port, destination port, TCP flags, packet length). Those flow records are exported to monitoring systems, written to disk, and may be visible in operator dashboards. A determined attacker who can observe FastNetMon's output (for example, through downstream IPFIX exports, ban_details captures, or compromised monitoring) can use the over-read as a slow read primitive against process memory.
Denial of service through corrupted flow accounting. Type confusion between an IPv4 header and a TCP/UDP header produces flow records with garbage source ports, destination ports, and packet sizes. FastNetMon's job is to threshold on aggregate flows and trigger mitigations — usually BGP blackhole announcements or flowspec rules. Corrupted accounting means the detector either fires on benign traffic (collateral damage when legitimate IPs get blackholed) or fails to fire on real attacks (because the attack traffic gets bucketed under fictitious source ports that never cross thresholds). Either outcome is a meaningful operational impact.
Crash potential. The 40-byte over-read past the validated boundary can land on an unmapped page if the buffer was allocated near the end of a page and subsequent pages are guard pages. This is rare on Linux because allocators tend to coalesce, but it is not zero. A repeated crash of the FastNetMon daemon during an active attack means the DDoS detector goes offline at exactly the moment it is most needed.
The combination of "always reachable from the network" and "always running with elevated privileges" puts CVE-2026-48682 squarely in the class of bugs that an attacker probing for soft targets in a hosting provider's infrastructure would happily turn into a permanent foothold opportunity.
How a fix should look
The correct fix is two lines of validation before the pointer advance:
uint8_t ihl = ipv4_header->get_ihl();
if (ihl < 5) {
return parse_result::malformed; // IHL below legal minimum
}
size_t header_length = 4 * ihl;
if (header_length > remaining_bytes) {
return parse_result::truncated; // ihl claims more than we have
}
local_pointer += header_length;
That is structurally identical to the validation that exists elsewhere in the same file for other variable-length headers. The original IHL block simply omitted it. A second occurrence of the same pattern exists at line 513 of the same file (the inner packet of a tunneled flow); both sites need the same treatment.
A defensive secondary improvement is to bound the maximum acceptable IHL to 15 explicitly, even though the field cannot exceed 15 by virtue of being 4 bits. The redundant check makes intent obvious to future maintainers and to static analyzers, and survives accidental refactors that widen the field.
What to do today
1. Identify your version
Run fastnetmon --version or check the package manager. If you are running 1.2.9 or earlier, you are running a vulnerable parser.
2. Watch for the upstream patch
FastNetMon LTD has been notified. Watch github.com/pavel-odintsov/fastnetmon and the FastNetMon release notes for a version newer than 1.2.9 that explicitly notes IPv4 parser hardening. The fix is mechanically small and is unlikely to require a major version bump.
3. Compensating controls until the patch ships
- Restrict NetFlow / sFlow / IPFIX collectors to known router IPs only. The capture plugins are reachable on UDP ports and accept any source that can route a packet to them; firewall them down to a strict allowlist of your own border devices.
- Run FastNetMon as a dedicated low-privilege user, not root. The Community Edition does not require root for ordinary AF_PACKET capture once
CAP_NET_RAWis set on the binary. Reducing the running uid does not prevent the parse error, but it limits what an information disclosure leak can yield from process memory. - Disable any sFlow raw_packet sampling from non-essential agents. Many switches default to sFlow agent enabled with raw_header export. If you do not need those samples, configure your sFlow agents to only export flow records, not raw headers.
- Watchdog the daemon. Add a systemd
Restart=on-failurewith a saneRestartSecso that a crashed parser does not leave detection offline. This does not prevent the bug; it limits the operational impact while you wait for the patch.
4. Long-term hygiene
Compile FastNetMon with AddressSanitizer in your test environment and replay a corpus of malformed IPv4 headers against it. The IHL field is small enough that exhaustively enumerating IHL=0..15 against a 20-byte packet is a trivial fuzz harness, and any future regression in this code path will be caught immediately. Operators who run their own packaging pipelines should consider this a permanent part of their CI.
The broader lesson: minimum bounds checks lie
The single most common pattern we see when reviewing C++ packet parsers is a length check against a struct's fixed size, followed by use of a variable-length field within that struct to compute a larger offset, with no further bounds check. The fixed-size check is necessary — you need to know you can dereference the field that tells you how long the real header is — but it is never sufficient on its own. The moment you compute a derived offset from an attacker-controlled field, that derived offset is the new bound you have to validate against.
This shows up in IPv4 (IHL), IPv6 (Hop-by-Hop options length, Routing extension lengths), TCP (data offset), and every length-prefixed TLV protocol in existence. It is the same bug class that produced Heartbleed in 2014, the same class that produces a steady drip of CVEs in BGP, NetFlow, and IPFIX parsers every year, and the same class that we expect to keep finding in flow inspection tools. The defensive coding pattern is to never advance a pointer by an attacker-controlled multiple without an immediately preceding check that the multiple stays inside the validated buffer.
For teams writing or reviewing network parsers, the heuristic is simple: every multiplication by a packet-derived field is a potential overflow, and every pointer advance after such a multiplication needs a bounds check on the next line. Static analyzers can find these patterns; AddressSanitizer can crash on them at runtime; fuzz harnesses can enumerate them. None of those tools cost anything compared to shipping a parser bug into a globally-deployed production daemon.
Disclosure timeline
| Date | Event |
|---|---|
| 2026-04-25 | Vulnerability identified during Lorikeet Security source code audit of FastNetMon Community Edition 1.2.9 |
| 2026-04-25 | CVE ID requested from MITRE |
| 2026-04-25 | Vendor (Pavel Odintsov / FastNetMon LTD) notified at the contact published in SECURITY.md |
| 2026-05-22 | CVE-2026-48682 assigned by MITRE |
| TBD | Vendor response |
| TBD | Fix release |
| 2026-05-23 | Lorikeet Security publishes responsible disclosure report |
Full Responsible Disclosure Report (PDF)
Complete writeup of all 16 FastNetMon Community Edition vulnerabilities Lorikeet Security identified, including vulnerable-code excerpts, impact analysis, and remediation guidance for each CVE.
Running flow-based DDoS detection in production?
Lorikeet Security audits the parsers, protocol stacks, and edge devices your network depends on. We find the bounds-check bugs that fuzzers miss because the input plugins are unauthenticated and the impact is operational, not a clean memory corruption.