GHSA-FQX6-693C-F55G
Vulnerability from github – Published: 2026-02-18 22:08 – Updated: 2026-02-20 16:48
VLAI?
Summary
LibreNMS has a Stored XSS in Custom OID - unit parameter missing strip_tags()
Details
Summary
The 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.
Details
Vulnerable Input Processing (includes/html/forms/customoid.inc.php lines 18-21):
$name = strip_tags((string) $_POST['name']); // line 18 - SANITIZED
$oid = strip_tags((string) $_POST['oid']); // line 19 - SANITIZED
$datatype = strip_tags((string) $_POST['datatype']); // line 20 - SANITIZED
$unit = $_POST['unit']; // line 21 - NOT SANITIZED!
Vulnerable Output (graphs/customoid.inc.php lines 13-20):
$customoid_unit = $customoid['customoid_unit']; // Retrieved from DB
$customoid_current = \LibreNMS\Util\Number::formatSi(...) . $customoid_unit;
echo "...$customoid_current..."; // ECHOED WITHOUT ESCAPING!
PoC
#!/usr/bin/env python3
"""
XSS test for LibreNMS Custom OID - unit parameter
"""
import html as html_module
import re
def strip_tags(value):
return re.sub(r'<[^>]*?>', '', str(value))
# Simulate form processing (customoid.inc.php lines 18-21)
test_inputs = {
'name': '<script>alert(1)</script>Test OID',
'oid': '1.3.6.1.4.1.2021.10.1.3.1',
'datatype': 'GAUGE',
'unit': '<script>alert("XSS")</script>',
}
name = strip_tags(test_inputs['name']) # Sanitized
oid = strip_tags(test_inputs['oid']) # Sanitized
datatype = strip_tags(test_inputs['datatype']) # Sanitized
unit = test_inputs['unit'] # NOT SANITIZED!
print("Input Processing Analysis:")
print(f" name (strip_tags): {name}")
print(f" oid (strip_tags): {oid}")
print(f" datatype (strip_tags): {datatype}")
print(f" unit (NO strip_tags): {unit}")
print()
print("*** VULNERABILITY: 'unit' parameter has NO strip_tags()! ***")
# Test XSS payloads
payloads = [
'<script>alert("XSS")</script>',
'<img src=x onerror=alert(1)>',
'<svg onload=alert(1)>',
]
print("\nXSS Payload Tests:")
for payload in payloads:
escaped = html_module.escape(payload)
has_xss = '<script>' in payload or 'onerror=' in payload.lower()
print(f" Payload: {payload}")
print(f" Raw (vulnerable): Contains executable code: {has_xss}")
print(f" Escaped (safe): {escaped}")
Expected Output
Input Processing Analysis:
name (strip_tags): alert(1)Test OID
oid (strip_tags): 1.3.6.1.4.1.2021.10.1.3.1
datatype (strip_tags): GAUGE
unit (NO strip_tags): <script>alert("XSS")</script>
*** VULNERABILITY: 'unit' parameter has NO strip_tags()! ***
Impact
- Attack Vector: User with device edit permissions sets malicious Unit value
- Exploitation: XSS payload stored in database, executes for all users viewing device graphs
- Consequences:
- Session hijacking via cookie theft
- Admin account takeover
- Malicious actions on behalf of victims
- Persistent attack affecting all users
- Affected Users: All LibreNMS installations with Custom OID feature
Severity ?
5.4 (Medium)
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "librenms/librenms"
},
"ranges": [
{
"events": [
{
"introduced": "24.10.0"
},
{
"fixed": "26.2.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-27016"
],
"database_specific": {
"cwe_ids": [
"CWE-116",
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-18T22:08:15Z",
"nvd_published_at": "2026-02-20T02:16:55Z",
"severity": "MODERATE"
},
"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[\u0027name\u0027]); // line 18 - SANITIZED\n$oid = strip_tags((string) $_POST[\u0027oid\u0027]); // line 19 - SANITIZED\n$datatype = strip_tags((string) $_POST[\u0027datatype\u0027]); // line 20 - SANITIZED\n$unit = $_POST[\u0027unit\u0027]; // line 21 - NOT SANITIZED!\n```\n\n**Vulnerable Output (`graphs/customoid.inc.php` lines 13-20):**\n```php\n$customoid_unit = $customoid[\u0027customoid_unit\u0027]; // 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\u0027\u003c[^\u003e]*?\u003e\u0027, \u0027\u0027, str(value))\n\n# Simulate form processing (customoid.inc.php lines 18-21)\ntest_inputs = {\n \u0027name\u0027: \u0027\u003cscript\u003ealert(1)\u003c/script\u003eTest OID\u0027,\n \u0027oid\u0027: \u00271.3.6.1.4.1.2021.10.1.3.1\u0027,\n \u0027datatype\u0027: \u0027GAUGE\u0027,\n \u0027unit\u0027: \u0027\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e\u0027,\n}\n\nname = strip_tags(test_inputs[\u0027name\u0027]) # Sanitized\noid = strip_tags(test_inputs[\u0027oid\u0027]) # Sanitized\ndatatype = strip_tags(test_inputs[\u0027datatype\u0027]) # Sanitized\nunit = test_inputs[\u0027unit\u0027] # 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: \u0027unit\u0027 parameter has NO strip_tags()! ***\")\n\n# Test XSS payloads\npayloads = [\n \u0027\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e\u0027,\n \u0027\u003cimg src=x onerror=alert(1)\u003e\u0027,\n \u0027\u003csvg onload=alert(1)\u003e\u0027,\n]\n\nprint(\"\\nXSS Payload Tests:\")\nfor payload in payloads:\n escaped = html_module.escape(payload)\n has_xss = \u0027\u003cscript\u003e\u0027 in payload or \u0027onerror=\u0027 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: \u0027unit\u0027 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",
"id": "GHSA-fqx6-693c-f55g",
"modified": "2026-02-20T16:48:24Z",
"published": "2026-02-18T22:08:15Z",
"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"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "LibreNMS has a Stored XSS in Custom OID - unit parameter missing strip_tags()"
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
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…
Loading…