CVE-2025-65093

Blind Faith: Uncovering SQL Injection in LibreNMS

Amit Schendel
Amit Schendel
Senior Security Researcher

Jan 21, 2026·7 min read·2 visits

Executive Summary (TL;DR)

LibreNMS prior to version 25.11.0 contains a Blind SQL Injection vulnerability. Although it requires administrator privileges, it allows for complete database enumeration via boolean inference. The flaw resides in the `/ajax_output.php` script where user input is directly concatenated into a SQL query.

A boolean-based Blind SQL Injection vulnerability in LibreNMS allows authenticated administrators to extract arbitrary database information via the `hostname` parameter in the `/ajax_output.php` endpoint.

The Hook: Who Watches the Watchers?

LibreNMS is the unsung hero of many SysAdmin dashboards. It is the all-seeing eye that monitors your network traffic, SNMP devices, and server health. It sits in the privileged center of your infrastructure, holding the keys to the kingdom—SNMP community strings, device credentials, and topology maps. Naturally, a tool with this much visibility is a prime target for attackers. If you can compromise the monitoring system, you don't just see the network; you own the narrative of what the admins think is happening.

Enter CVE-2025-65093. This isn't some complex memory corruption bug requiring a Ph.D. in heap feng shui. It is a good, old-fashioned SQL Injection (SQLi) sitting right in the PHP code. Specifically, it's a Boolean-based Blind SQLi. It’s the digital equivalent of playing "20 Questions" with a database that refuses to speak but will eagerly nod 'yes' or shake its head 'no' if you ask it the right logic puzzles.

The catch? You need administrator privileges to exploit it. Some might roll their eyes and say, "If you're already admin, game over anyway." But that misses the point of modern persistence. An attacker who compromises an admin account often wants to dump the database schema, steal hashed credentials for lateral movement, or plant backdoors without touching the filesystem. This vulnerability turns a compromised web session into a direct line to the database backend, bypassing application logic entirely.

The Flaw: A Game of True or False

The vulnerability lives in /ajax_output.php, a script designed to handle asynchronous requests for the frontend. Specifically, it chokes on the hostname parameter. In a perfect world, user input is treated like radioactive waste—contained, sanitized, and handled via prepared statements. In the world of CVE-2025-65093, the input is treated like a trusted friend and invited directly into the SQL query string.

Because this is a Blind SQL injection, the application doesn't just vomit the database contents onto the screen when you break the syntax. Instead, it suppresses the error. However, the logic flow of the application changes based on whether the SQL query returns rows or not. If the query is successful (True), the UI displays device data. If the query fails or returns empty (False), the UI shows nothing or a default state.

This binary behavior is all an attacker needs. By injecting logical tests (like AND 1=1 vs AND 1=2), we can ask the database true/false questions. "Does the first letter of the admin password start with 'A'?" If the page loads normally, the answer is yes. If it breaks, the answer is no. Repeat this a few thousand times with an automated script, and you can extract the entire database, byte by byte.

The Code: Concatenation Catastrophe

Let's look at the "smoking gun." The code in /opt/librenms/includes/html/output/capture.inc.php (and related logic) failed to use Laravel's Eloquent ORM or standard PDO prepared statements for this specific filter. Instead, it opted for string concatenation—the arch-nemesis of secure coding.

The Vulnerable Logic (Conceptual):

// The developer takes the input directly from the GET request
$hostname = $_GET['hostname'];
 
// And concatenates it into the query string
// This is the digital equivalent of leaving your front door unlocked
$sql = "SELECT * FROM devices WHERE disabled = 0 AND hostname LIKE '$hostname'";
 
$results = dbFetchRows($sql);

If a user passes 10.0.0.1 as the hostname, the query is fine. But if they pass 10.0.0.1' AND 1=1 AND 'a'='a, the query becomes valid SQL that alters the logic check. The patch in version 25.11.0 (Commit d9f57304498268476983662c5d584394927502b3) fixes this by enforcing parameter binding, ensuring that the database treats the input strictly as a literal string, not executable command code.

The Fix:

// The fixed code uses parameter binding
$hostname = $_GET['hostname'];
 
// The ? placeholder ensures the input is escaped safely
$sql = "SELECT * FROM devices WHERE disabled = 0 AND hostname LIKE ?";
$params = array($hostname);
 
$results = dbFetchRows($sql, $params);

The Exploit: Extracting Data Bit by Bit

To exploit this, we assume we have an Administrator session (Cookie laravel_session=...). We target the hostname parameter. The goal is to verify we can control the boolean logic of the query.

Step 1: Verify the TRUE Condition

We inject a condition that is mathematically always true (1=1).

GET /ajax_output.php?id=capture&format=text&type=discovery&hostname=10.0.5.4'+AND+1=1+AND+'1'='1 HTTP/1.1
Host: target-librenms.local
Cookie: laravel_session=[ADMIN_COOKIE]

Result: The server returns the device capture data for 10.0.5.4. The application is happy.

Step 2: Verify the FALSE Condition

Now we inject a condition that is always false (1=2).

GET /ajax_output.php?id=capture&format=text&type=discovery&hostname=10.0.5.4'+AND+1=2+AND+'1'='1 HTTP/1.1

Result: The server returns no data or an empty response. The application logic broke because 1 does not equal 2.

Step 3: Weaponization

Now we script it. We want to find the current database version. We iterate through characters using ASCII values.

-- Pseudo-injection to guess the first character of version()
10.0.5.4' AND ascii(substring(version(),1,1)) > 50 AND '1'='1

If the server responds with data, we know the version string starts with a character greater than ASCII 50. We binary search our way down until we have the exact character. Rinse and repeat for the entire schema, user table, and hashed passwords.

The Impact: Why Admin SQLi Matters

A common rebuttal to this vulnerability is "CVSS 5.5? Admin required? Who cares?" This is dangerous complacency. In many organizations, the 'Admin' of LibreNMS might be a junior network engineer, while the database behind it might be shared or running as a high-privilege user (e.g., root or postgres).

1. Lateral Movement: LibreNMS stores SNMP community strings (often reused as passwords), SSH keys for device config backups, and API tokens. Dumping the devices or config tables via SQLi could hand an attacker the keys to every router and switch in the datacenter.

2. Web Shell Upload: If the database user has FILE privileges (a common misconfiguration), an attacker could use the SQLi to write a PHP web shell to the webroot using SELECT ... INTO OUTFILE. This turns a data leak vulnerability into full Remote Code Execution (RCE).

3. Stealth: Unlike a noisy vulnerability scanner, a slow, low-frequency Blind SQLi attack can be very hard to detect in logs without specific WAF rules looking for SQL syntax. An intruder can slowly bleed the database dry over days without triggering high-priority alarms.

The Mitigation: Patch or Perish

The remediation is straightforward: Update to LibreNMS v25.11.0. The developers have patched the specific injection point by implementing proper input handling.

If you cannot update immediately, you are playing with fire, but you can mitigate the risk via a Web Application Firewall (WAF). You should block requests to ajax_output.php that contain common SQL keywords in the query string, such as UNION, SELECT, and logic operators like AND 1=1.

Lessons Learned for Developers:

  1. Trust No One: Not even your admins. Authorization checks (Admin role) do not replace Input Validation.
  2. ORM is your friend: Modern frameworks like Laravel provide excellent ORMs (Eloquent) that handle binding automatically. When you bypass the ORM to write 'raw' SQL, you are bypassing the safety nets.
  3. Linting: Static analysis tools (SAST) can often catch concatenation inside SQL strings. Use them in your CI/CD pipeline.

Fix Analysis (1)

Technical Appendix

CVSS Score
5.5/ 10
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:L/A:N
EPSS Probability
0.00%
Top 100% most exploited

Affected Systems

LibreNMS < 25.11.0

Affected Versions Detail

Product
Affected Versions
Fixed Version
LibreNMS
LibreNMS
< 25.11.025.11.0
AttributeDetail
CWE IDCWE-89 (SQL Injection)
Attack VectorNetwork (Admin Auth Required)
CVSS v3.15.5 (Medium)
Exploit StatusPoC Available
ImpactHigh Confidentiality Loss
Vulnerable Paramhostname
CWE-89
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

The software constructs all or part of an SQL command using an externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.

Vulnerability Timeline

Vulnerability Published
2025-11-18
Patch Released (v25.11.0)
2025-11-18
Last Modified
2025-11-20

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.