All Outbound HTTPS Connections Accept Any Certificate, Including Self-Signed — Telemetry Stream Is MITM-able
- CVE
- CVE-2026-48697
- CVSS
- 7.4 (High)
- CWE
- CWE-295 (Improper Certificate Validation)
- Affected
- FastNetMon Community Edition <= 1.2.9
- Component
- src/fast_library.cpp, function
execute_web_request_secure(), lines 1639-1670 - Attack Vector
- Remote (network-adjacent, MITM)
- Discovered by
- Lorikeet Security
FastNetMon sends periodic telemetry to community-stats.fastnetmon.com every 3600 seconds. The telemetry payload includes CPU model, kernel version, configured features, traffic statistics, and software version. The connection is HTTPS, set up via Boost.Asio's SSL context.
The TLS setup is incomplete in a way that is a well-known Boost.Asio footgun: the code creates an ssl::context in tls_client mode, calls set_default_verify_paths() to load the system's CA certificates, and sets the SNI hostname — but never calls set_verify_mode(ssl::verify_peer). The default verify mode for an Asio SSL context is verify_none. Without the verify_peer call, OpenSSL completes the handshake without checking that the certificate chain validates against the loaded CAs, that the hostname matches, or that the certificate hasn't expired or been revoked.
The net effect: any network attacker on the path between FastNetMon and community-stats.fastnetmon.com can intercept, read, and modify the telemetry stream by presenting any certificate they like. Self-signed certificates, expired certificates, certificates for the wrong hostname, certificates from any CA — all accepted.
Disclosure status: Lorikeet Security notified FastNetMon LTD on April 25, 2026. CVE-2026-48697 was assigned by MITRE. No vendor response or fix as of May 23, 2026.
The vulnerable code
// src/fast_library.cpp, execute_web_request_secure(), lines 1639-1670
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls_client);
ctx.set_default_verify_paths(); // Loads system CA certificates.
// MISSING:
// ctx.set_verify_mode(boost::asio::ssl::verify_peer);
// ^^^ Without this, the default verify mode is verify_none.
// The handshake completes regardless of certificate validity.
// SNI is set (which doesn't trigger verification):
SSL_set_tlsext_host_name(stream.native_handle(), host.c_str());
// Handshake proceeds with no verification:
stream.handshake(ssl_socket::client);
This is the canonical "I set up TLS but I forgot the verify call" bug. Boost.Asio's API design makes the bug easy to ship: set_default_verify_paths() sounds like it should also enable verification, but it doesn't — it just makes the CA certificates available for use if verification is enabled. Loading CAs without enabling verification is like installing a lock without ever locking the door.
The same shape appears in many other Boost.Asio-using projects. Static analyzers have specific rules for it (CodeQL's cpp/boost-network-no-verify-peer, Coverity's TLS misuse detector), and OpenSSL's own documentation warns about exactly this failure mode. The reason it keeps appearing is that the working-but-insecure case looks correct: the TLS handshake completes, the data flows, the operator sees green checkmarks in the logs. Nothing fails, and so nothing tells the developer that they missed a step.
What an attacker can do
An attacker on the network path between FastNetMon and community-stats.fastnetmon.com can:
- Read the telemetry data in transit. The data includes CPU model (
cat /proc/cpuinfooutput), kernel version, distribution name, FastNetMon version, enabled plugins (NetFlow, sFlow, IPFIX, BGP), and aggregate traffic statistics. This is fingerprinting information that gives the attacker a complete picture of the FastNetMon deployment. - Modify the telemetry data. The attacker can rewrite the payload to a different format or to a different destination. If FastNetMon ever uses the telemetry response (which it currently does not, but future versions might), the attacker controls that response.
- Redirect the telemetry to a different server. By DNS-spoofing or BGP-hijacking the
community-stats.fastnetmon.comhostname, the attacker collects all the data themselves — from any FastNetMon deployment in the world that the attacker can intercept — without the operator noticing. - Use the open TLS channel as a launching point for protocol-downgrade attacks if FastNetMon ever performs HTTP-over-TLS to other endpoints. The
execute_web_request_securefunction is general-purpose; any future use that uses it with attacker-influenced URLs would be similarly vulnerable.
Threat model: who has the necessary position?
The attacker needs to be on the network path between the FastNetMon host and the destination server. Realistic scenarios:
- Untrusted upstream ISP. The traffic transits the operator's upstream provider; that provider can intercept. This is the standard threat model for TLS, and it's exactly the reason TLS exists.
- Compromised local network. If an attacker is on the same LAN as the FastNetMon host (via Wi-Fi compromise, malicious insider, or a compromised neighboring service), they can ARP-spoof the gateway and intercept.
- Nation-state-level network operator in jurisdictions where wholesale interception is performed.
- Internet routing attacker. BGP hijacks of the FastNetMon LTD prefix or upstream providers' prefixes can redirect traffic destined for
community-stats.fastnetmon.comto an attacker-controlled server. - DNS spoofing. If the FastNetMon host's DNS resolution is compromised, the attacker controls which IP
community-stats.fastnetmon.comresolves to and therefore which server the TLS connection is established with.
For most operators, the upstream ISP is the realistic threat. TLS is supposed to handle this; without certificate verification, it doesn't.
How a fix should look
boost::asio::ssl::context ctx(boost::asio::ssl::context::tls_client);
ctx.set_default_verify_paths();
// 1. Require certificate verification.
ctx.set_verify_mode(boost::asio::ssl::verify_peer);
// 2. Verify the hostname in the certificate matches the connection target.
ctx.set_verify_callback(boost::asio::ssl::host_name_verification(host));
// 3. (Defensive) Pin TLS minimum version.
ctx.set_options(boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::no_sslv3
| boost::asio::ssl::context::no_tlsv1
| boost::asio::ssl::context::no_tlsv1_1);
// 4. (Optional but stronger) Pin the expected certificate or CA.
// For the telemetry endpoint where the destination is known and stable:
// ctx.load_verify_file("/etc/fastnetmon/community-stats-ca.crt");
// SNI as before:
SSL_set_tlsext_host_name(stream.native_handle(), host.c_str());
// Handshake now verifies the certificate against the loaded CAs and the hostname.
stream.handshake(ssl_socket::client);
Four steps. The first two are the minimum required — set_verify_mode turns verification on, and set_verify_callback with host_name_verification binds the verified certificate to the expected hostname. The third (TLS version pinning) and fourth (CA pinning) are defense-in-depth measures appropriate for a connection to a known, fixed destination.
For the telemetry use case specifically, certificate pinning makes sense. The destination is community-stats.fastnetmon.com, which is controlled by FastNetMon LTD and doesn't change. Pinning the CA (or the certificate's public key) means even a successful attack against a trusted CA (issuing a fraudulent certificate for the domain) doesn't compromise the telemetry channel. This is the same defensive technique used by major browsers' HSTS preload + HPKP setups, scaled down to a single-application client.
Compensating controls
- Disable telemetry submission. The
community_stats_enableconfiguration directive infastnetmon.confcontrols whether telemetry gets sent at all. Set it tofalseto eliminate the outbound HTTPS connection entirely. This is the most reliable mitigation until the certificate verification is fixed. - Block outbound traffic to
community-stats.fastnetmon.com. Firewall the FastNetMon host's egress to that specific destination. If your environment has restrictive egress rules anyway, this is straightforward. The connection failing closed is the safe failure mode. - Audit the firewall logs for unexpected outbound HTTPS. The telemetry connection is the only HTTPS outbound from FastNetMon in the default configuration. Any other HTTPS outbound is unexpected and may indicate that the function has been called from somewhere else.
- Use a forced-egress proxy with its own TLS termination. If you run a forward proxy (Squid, Envoy) on the FastNetMon host's egress path, that proxy can perform its own certificate validation. FastNetMon's connection terminates at the proxy (which doesn't care about the missing verification), and the proxy makes its own properly-verified connection to the destination. This is a structural mitigation that protects against this CVE plus any other improperly-configured TLS clients running on the host.
The pattern: TLS APIs that fail open
Boost.Asio's SSL context defaulting to verify_none is a well-known API ergonomics issue. The library has chosen this default for backward compatibility — the API has been stable since Asio joined Boost in 2005, and changing the default would break every existing application that relies on the old behavior. So the unsafe default persists, and applications that follow the "obvious" setup path (create context, load CAs, handshake) end up with no verification.
Python's ssl module made the opposite choice in 2017 (PEP 543): the default SSLContext validates certificates. Most modern HTTP libraries (requests, httpx, libcurl) have the same default. The lesson is that defaults should fail closed, not fail open.
For Boost.Asio users specifically, the defensive coding pattern is to always pair the SSL context setup with an explicit set_verify_mode call. Make it a project linting rule: any ssl::context constructor invocation that isn't followed by a set_verify_mode(verify_peer) call somewhere in the same function should be a compile-time warning. This is a one-line change to a static analyzer's configuration and it catches every future instance of this bug.
For developers reviewing TLS code generally: "the TLS handshake succeeded" tells you nothing about whether the connection is secure. The handshake succeeding only means the cryptographic protocol completed without protocol-level errors. Certificate validation, hostname verification, and revocation checking are separate steps that have to be configured explicitly. If you're reading TLS setup code and you can't point to the specific lines that perform each of those three checks, the code is probably wrong.
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-48697 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.
Auditing the network infrastructure your business depends on
Lorikeet Security finds the parser bugs, protocol confusion, and unauthenticated-control-plane issues that move your DDoS detector, your BGP speaker, and your edge devices from "running" to "exploitable." Source code review, fuzz harness development, and adversarial protocol testing.