{"id":"GHSA-fqx6-693c-f55g","summary":"LibreNMS has a Stored XSS in Custom OID - unit parameter missing strip_tags()","details":"### Summary\nThe `unit` parameter in Custom OID functionality lacks `strip_tags()` sanitization while other fields (`name`, `oid`, `datatype`) are sanitized. The unsanitized value is stored in the database and rendered without HTML escaping, allowing Stored XSS.\n\n### Details\n**Vulnerable Input Processing (`includes/html/forms/customoid.inc.php` lines 18-21):**\n```php\n$name = strip_tags((string) $_POST['name']);       // line 18 - SANITIZED\n$oid = strip_tags((string) $_POST['oid']);         // line 19 - SANITIZED\n$datatype = strip_tags((string) $_POST['datatype']);  // line 20 - SANITIZED\n$unit = $_POST['unit'];                            // line 21 - NOT SANITIZED!\n```\n\n**Vulnerable Output (`graphs/customoid.inc.php` lines 13-20):**\n```php\n$customoid_unit = $customoid['customoid_unit'];  // Retrieved from DB\n$customoid_current = \\LibreNMS\\Util\\Number::formatSi(...) . $customoid_unit;\necho \"...$customoid_current...\";  // ECHOED WITHOUT ESCAPING!\n```\n\n### PoC\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nXSS test for LibreNMS Custom OID - unit parameter\n\"\"\"\n\nimport html as html_module\nimport re\n\ndef strip_tags(value):\n    return re.sub(r'\u003c[^\u003e]*?\u003e', '', str(value))\n\n# Simulate form processing (customoid.inc.php lines 18-21)\ntest_inputs = {\n    'name': '\u003cscript\u003ealert(1)\u003c/script\u003eTest OID',\n    'oid': '1.3.6.1.4.1.2021.10.1.3.1',\n    'datatype': 'GAUGE',\n    'unit': '\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e',\n}\n\nname = strip_tags(test_inputs['name'])      # Sanitized\noid = strip_tags(test_inputs['oid'])        # Sanitized\ndatatype = strip_tags(test_inputs['datatype'])  # Sanitized\nunit = test_inputs['unit']                   # NOT SANITIZED!\n\nprint(\"Input Processing Analysis:\")\nprint(f\"  name (strip_tags):     {name}\")\nprint(f\"  oid (strip_tags):      {oid}\")\nprint(f\"  datatype (strip_tags): {datatype}\")\nprint(f\"  unit (NO strip_tags):  {unit}\")\nprint()\nprint(\"*** VULNERABILITY: 'unit' parameter has NO strip_tags()! ***\")\n\n# Test XSS payloads\npayloads = [\n    '\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e',\n    '\u003cimg src=x onerror=alert(1)\u003e',\n    '\u003csvg onload=alert(1)\u003e',\n]\n\nprint(\"\\nXSS Payload Tests:\")\nfor payload in payloads:\n    escaped = html_module.escape(payload)\n    has_xss = '\u003cscript\u003e' in payload or 'onerror=' in payload.lower()\n    print(f\"  Payload: {payload}\")\n    print(f\"    Raw (vulnerable): Contains executable code: {has_xss}\")\n    print(f\"    Escaped (safe):   {escaped}\")\n```\n\n### Expected Output\n\n```\nInput Processing Analysis:\n  name (strip_tags):     alert(1)Test OID\n  oid (strip_tags):      1.3.6.1.4.1.2021.10.1.3.1\n  datatype (strip_tags): GAUGE\n  unit (NO strip_tags):  \u003cscript\u003ealert(\"XSS\")\u003c/script\u003e\n\n*** VULNERABILITY: 'unit' parameter has NO strip_tags()! ***\n```\n### Impact\n- **Attack Vector:** User with device edit permissions sets malicious Unit value\n- **Exploitation:** XSS payload stored in database, executes for all users viewing device graphs\n- **Consequences:**\n  - Session hijacking via cookie theft\n  - Admin account takeover\n  - Malicious actions on behalf of victims\n  - Persistent attack affecting all users\n- **Affected Users:** All LibreNMS installations with Custom OID feature","aliases":["CVE-2026-27016"],"modified":"2026-02-20T17:02:57.110577Z","published":"2026-02-18T22:08:15Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-02-18T22:08:15Z","nvd_published_at":"2026-02-20T02:16:55Z","cwe_ids":["CWE-116","CWE-79"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/librenms/librenms/security/advisories/GHSA-fqx6-693c-f55g"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-27016"},{"type":"WEB","url":"https://github.com/librenms/librenms/pull/19040"},{"type":"WEB","url":"https://github.com/librenms/librenms/commit/3bea263e02441690c01dea7fa3fe6ffec94af335"},{"type":"PACKAGE","url":"https://github.com/librenms/librenms"},{"type":"WEB","url":"https://github.com/librenms/librenms/releases/tag/26.2.0"}],"affected":[{"package":{"name":"librenms/librenms","ecosystem":"Packagist","purl":"pkg:composer/librenms/librenms"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"24.10.0"},{"fixed":"26.2.0"}]}],"versions":["24.10.0","24.10.1","24.11.0","24.12.0","25.1.0","25.10.0","25.11.0","25.12.0","25.2.0","25.3.0","25.4.0","25.5.0","25.6.0","25.7.0","25.8.0","25.9.0","25.9.1","26.1.0","26.1.1"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/02/GHSA-fqx6-693c-f55g/GHSA-fqx6-693c-f55g.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N"}]}