Two Bugs in One File: Shell Injection in _log() + Hardcoded MikroTik Credentials
- CVE
- CVE-2026-48695
- CVSS
- 8.1 (High)
- CWE
- CWE-78 (OS Command Injection); CWE-798 (Use of Hard-coded Credentials, compounding)
- Affected
- FastNetMon Community Edition <= 1.2.9
- Component
- src/mikrotik_plugin/fastnetmon_mikrotik.php, lines 105-109 (_log); lines 31-33 (credentials)
- Attack Vector
- Indirect remote (via the attack notification pipeline)
- Discovered by
- Lorikeet Security
The MikroTik plugin is a sibling to the Juniper plugin. FastNetMon ships both because the two router platforms have different APIs for pushing configuration; the MikroTik version uses the MikroTik API protocol (a custom binary protocol) instead of NETCONF. The plugin is structurally similar: it accepts an attacker IP via argv, logs the action, and pushes a configuration change to the router to install a discard route.
The bug pattern in the MikroTik plugin's _log() function is identical to CVE-2026-48687 — attack data concatenated into exec("echo ...") without escaping. Same root cause, same impact ceiling, different file. Both plugins were written in the same style and both inherit the same security flaw.
What makes CVE-2026-48695 worse than its Juniper twin is the second bug in the same file: hardcoded MikroTik router credentials at lines 31-33, with the literal username api and password api123. Any operator who installed the plugin without changing these defaults has a publicly-known username/password pair sitting in their router configuration.
Disclosure status: Lorikeet Security notified FastNetMon LTD on April 25, 2026. CVE-2026-48695 was assigned by MITRE. No vendor response or fix as of May 23, 2026.
The vulnerable code
// src/mikrotik_plugin/fastnetmon_mikrotik.php, lines 105-109 (same shape as CVE-2026-48687)
function _log( $msg ) {
$FILE_LOG_TMP = "/tmp/fastnetmon_api_mikrotik.log";
exec( "echo `date` \"- [FASTNETMON] - " . $msg . " \" >> " . $FILE_LOG_TMP );
}
// Lines 31-33 -- the hardcoded credentials
$user = "api";
$pass = "api123";
Two separate footguns in one file. The shell-injection mechanics are exactly the ones discussed in CVE-2026-48687 — refer there for the detailed walkthrough. The credentials are the new bug to talk about here.
The hardcoded credentials issue
The constants $user = "api" and $pass = "api123" are defaults that the plugin ships with. The expectation is presumably that operators will edit the file to change these to their own router credentials before deploying. In practice, three things go wrong with this expectation:
- Operators forget. The plugin is installed from a package, the operator skims the README, and the file lives at
/etc/fastnetmon/scripts/fastnetmon_mikrotik.phpwhere it doesn't get reviewed. The default credentials stay in place. - Operators set up the matching router-side credentials. To get the plugin to work at all, the operator has to create a user called
apiwith passwordapi123on the MikroTik router. Now both the plugin and the router are using the publicly-known credentials. The plugin's defaults effectively dictate the router's credentials. - Package updates overwrite local changes. If the operator did edit the file to change the credentials, a future
apt upgradeoryum updateon the FastNetMon package overwrites the file and restores the defaults. The operator's credentials are silently reset on every package update.
Any attacker who has read access to the plugin file (or who is running a vulnerability scanner against the FastNetMon repository) knows the credentials. If they can reach the MikroTik router's API port (TCP/8728 or TLS/8729 by default), they can authenticate and push configuration directly — bypassing FastNetMon entirely.
What an attacker with the router credentials can do
The MikroTik api user, if granted the privileges needed to install discard routes (which the plugin requires), has at minimum read, write, and policy rights. With those rights:
- Add additional discard routes to blackhole arbitrary traffic.
- Modify firewall rules to allow or deny arbitrary traffic.
- Enable, disable, or reconfigure NAT, routing, and VPN.
- Create new users on the router with full privileges.
- Enable services (SSH, Telnet, FTP) that operators thought were disabled.
- Modify SNMP and logging to hide future malicious activity.
In short: the hardcoded credentials are a router compromise if the attacker can reach the router's API port. Many MikroTik deployments expose the API port to the internet or to insufficiently-segmented internal networks, especially in smaller operator environments where the platform's default-deny is overridden for convenience.
How an attacker reaches the shell-injection bug
Same chain as CVE-2026-48687: any path that lets the attacker influence the $IP_ATTACK argument. The current C++ core uses inet_ntoa(), which produces safe values; this CVE becomes live the moment any of the conditions discussed in the Juniper writeup change. The two CVEs share the same notify-pipeline reachability story.
How a fix should look
Both bugs need separate fixes.
For the shell injection: replace exec() with PHP's file_put_contents(), identical to the recommended fix in CVE-2026-48687:
function _log( $msg ) {
$FILE_LOG_TMP = "/tmp/fastnetmon_api_mikrotik.log";
$timestamp = date("Y-m-d H:i:s");
file_put_contents(
$FILE_LOG_TMP,
"$timestamp - [FASTNETMON] - $msg\n",
FILE_APPEND | LOCK_EX
);
}
For the hardcoded credentials: externalize them. The plugin should read credentials from a configuration file outside the script, with appropriate permissions:
// At plugin entry, load credentials from a secured config file.
$credentials_file = '/etc/fastnetmon/mikrotik.conf';
if (!is_readable($credentials_file)) {
fwrite(STDERR, "FATAL: MikroTik credentials file not readable: $credentials_file\n");
exit(1);
}
$config = parse_ini_file($credentials_file);
if (!isset($config['username']) || !isset($config['password'])) {
fwrite(STDERR, "FATAL: MikroTik credentials missing keys\n");
exit(1);
}
$user = $config['username'];
$pass = $config['password'];
// /etc/fastnetmon/mikrotik.conf should be owned root:fastnetmon with mode 0640.
Two improvements: (1) the credentials are no longer in the script (so they're not in the package, not in the repository, and not exposed by world-readable file permissions), and (2) the script refuses to run without credentials, eliminating the "default values" failure mode.
A defensive secondary improvement is to use a Linux secret-storage mechanism instead of a flat file. systemd-creds, HashiCorp Vault Agent, or a tiny custom service that exposes credentials via Unix socket are all reasonable choices for environments that have credential-management infrastructure already.
Compensating controls
- Change the MikroTik router credentials immediately. If you have not already done so, log in to your MikroTik router and create a new user with a non-default name and a strong unique password. Update the FastNetMon plugin file with the new values. Delete the old
apiuser. - Restrict the MikroTik API port. The MikroTik API port (TCP/8728 unencrypted or TCP/8729 TLS) should be reachable only from your FastNetMon host, not from any other source. Configure MikroTik's IP service list and firewall to enforce this.
- Use the TLS API port (8729), not the plaintext one. The plaintext API protocol sends credentials over the wire; even with non-default credentials, any network attacker on the path can intercept them.
- Audit MikroTik logs for activity from the
apiuser. If unexpected commands appear from that user, assume compromise and treat it as an incident. - Apply the same Juniper-style validation for
$IP_ATTACKat the top of the script (see CVE-2026-48694). The bug pattern is the same; the fix is the same. - Disable the plugin if you don't use it. If you don't push MikroTik configurations, comment the plugin out of
fastnetmon.conf.
The pattern: hardcoded credentials are unforgivable in 2026
This kind of bug used to be common — default passwords like cisco/cisco or admin/admin shipped on routers well into the 2000s. The industry moved away from default passwords roughly a decade ago after a series of public incidents (Mirai notably hit IoT devices with default credentials at scale in 2016). Hardcoded credentials in a 2026 codebase, especially in a security tool, are an indicator of code that hasn't been reviewed against current best practices.
The general principle: code should not contain credentials. Credentials live in configuration files (with appropriate file permissions), in environment variables (with appropriate process isolation), or in secret-management systems (Vault, AWS Secrets Manager, sealed-secrets). The code reads them at runtime. If the code can run without anyone explicitly supplying credentials, that's a sign that there are credentials inside the code.
For maintainers grepping their own codebases: grep -r 'password\s*=\|pass\s*=\|secret\s*=\|api_key\s*=' in any project's source tree will surface candidates. Each one needs to be verified as either a default placeholder (which still needs to go), a test value (which needs to be moved to test-only files), or a real credential (which is the bug). Make this a routine review activity, not a one-off audit.
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-48695 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.