| Título | Magma Magma 1.8.0-era master @ 76d9dc0ebc (tested) Cross Site Scripting |
|---|
| Descripción | ================================================================================
REPORT ID : MAGMA-2026-002
CHAIN ROOT : MAGMA-2026-001 (#1043997) - Host Routes RBAC Bypass
TITLE : Stored XSS via Organization Name (NMS Pug template)
VENDOR : Magma (Linux Foundation)
PRODUCT : Magma NMS (magmalte)
COMPONENT : nms/server/views/index.pug, nms/server/main/routes.ts,
nms/server/host/routes.ts
CWE : CWE-79, CWE-80
GHSA : GHSA-cww9-5hqf-996q (private)
CVSS 3.1 : 5.2 MEDIUM
CVSS 4.0 : 6.9 MEDIUM
REPORTER : Aymen Benlamari (GitHub: aymencdm)
AFFECTED : Magma 1.8.0-era master @ 76d9dc0ebc (tested)
EMBARGO : 2026-11-09
RELATED : MAGMA-2026-001 (#1043997) (reachability), MAGMA-2026-005 (chain)
================================================================================
--------------------------------------------------------------------------------
0. CHAIN RELATIONSHIP
--------------------------------------------------------------------------------
The write primitive for this finding is the host organization routes. Where the
RBAC bypass in MAGMA-2026-001 is present, a role=0 host-org user reaches those
routes, so the two findings chain: role=0 -> SUPERUSER -> inject the
organization name -> every member of that organization executes the payload.
MAGMA-2026-002 is nevertheless a distinct root cause. The defect is the
unescaped rendering sink and the lack of output encoding, not the missing role
check. It can be triggered by any actor who can set an organization name,
including a legitimate SUPERUSER or a compromised administrator account,
without using MAGMA-2026-001.
--------------------------------------------------------------------------------
1. SUMMARY
--------------------------------------------------------------------------------
The NMS stores an organization's name without validation and later embeds it in
a JSON blob inside an HTML <script> block using Pug's unescaped interpolation
!{configJson}. JSON.stringify does not escape the sequence </script>, so an
organization name containing </script><script>...</script> closes the script
element early and injects attacker-controlled JavaScript.
Because the value is persisted in the Organizations table, the script executes
for every member of that organization on every visit to /nms. The payload can
read the page, exfiltrate session cookies and CSRF tokens, and perform actions
as the victim, including as a SUPERUSER.
--------------------------------------------------------------------------------
2. AFFECTED COMPONENT / VERSIONS
--------------------------------------------------------------------------------
Product : Magma NMS (magmalte / nms/server)
Sinks : nms/server/views/index.pug:24 !{configJson}
nms/server/views/login.pug:24 same pattern
nms/server/views/host.pug:24 same pattern
Source : nms/server/main/routes.ts:32-56 handleReact() builds appData
(tenant: organization?.name at line 40, configJson at line 53)
Write : nms/server/host/routes.ts POST/PUT /host/organization/async
Tested on : master @ 76d9dc0ebc (Magma 1.8.0-era), Docker lab, Chromium
Likely : every release containing the unescaped !{configJson} sink.
--------------------------------------------------------------------------------
3. ROOT CAUSE
--------------------------------------------------------------------------------
nms/server/views/index.pug:24 uses Pug's unescaped interpolation:
script.
var CONFIG = !{configJson};
nms/server/main/routes.ts:32-56 builds that value. handleReact() puts the
organization name into appData.user.tenant (line 40) and embeds appData as
configJson (line 53):
const appData: EmbeddedData = {
...
user: {
tenant: organization?.name || '',
...
},
...
};
res.render('index', {
configJson: JSON.stringify({appData}),
});
JSON.stringify escapes quotes and backslashes, but it does not escape the
characters that matter in an HTML script context. A name such as
xss-</script><script>alert(document.cookie)</script>
produces:
var CONFIG = {"appData":{...,"user":{"tenant":"xss-</script><script>alert(document.cookie)</script>",...}}};
The browser terminates the script element at the first </script> and executes
the injected script. This is the classic "JSON embedded in HTML" mistake: JSON
encoding is not HTML encoding, and the output context is HTML, not JSON.
The organization name is accepted with no validation at
POST /host/organization/async and can also be changed through
PUT /host/organization/async/:name, which performs a mass-assignment update
(organization.update(req.body)). There is no allow-list of fields and no
character filtering or length restriction that would prevent the payload.
--------------------------------------------------------------------------------
4. ATTACKER MODEL / PRECONDITIONS
--------------------------------------------------------------------------------
- An actor who can create or rename an organization. In the default role model
that is a SUPERUSER. Where MAGMA-2026-001 is present, a role=0 host-org user
reaches the same route through that bypass.
- A victim who is a member of the poisoned organization and visits /nms. The
victim needs no other action; loading the page is enough.
- No network position beyond normal access to the NMS.
The payload is persistent: it survives restarts and fires on every page load
until the organization name is removed.
--------------------------------------------------------------------------------
5. IMPACT (standalone)
--------------------------------------------------------------------------------
- Theft of the victim's session cookie and CSRF token, leading to account
takeover. If the victim is a SUPERUSER, the attacker inherits full NMS
administrative control.
- Actions performed in the victim's browser with the victim's privileges,
including user creation, network changes and configuration updates.
- Because the payload runs on /nms for every member of the organization, one
poisoned name compromises the whole tenant.
Availability impact is None: the payload does not destroy data by itself.
Integrity impact is Low-to-High depending on what the victim can do; this report
scores the generic case as Low for integrity.
--------------------------------------------------------------------------------
6. CVSS 3.1 - 5.2 MEDIUM
--------------------------------------------------------------------------------
Vector: CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:U/C:H/I:L/A:N
Metric Value Why
------ ----- ----------------------------------------------------------------
AV N The payload is delivered by the NMS web application over the
network.
AC L No special conditions: the name is stored verbatim and the sink
is always rendered. No race or memory grooming.
PR H Creating or renaming an organization requires SUPERUSER in the
default model. Via MAGMA-2026-001 the effective privilege is
lower, but this report scores the XSS root cause on its own.
UI R A victim must visit /nms (or another page that embeds CONFIG).
The victim does not have to click the payload, but a page load
is required.
S U The vulnerable component and the impacted component are the same
NMS origin; the script runs in the NMS security context.
C H Session cookie and CSRF token theft, and read access to all data
the victim can see.
I L The attacker can act as the victim, but the payload itself does
not grant additional server-side privileges beyond the victim's.
A N No availability impact from the payload.
If the chained privilege level is used instead of the root-cause privilege,
PR becomes L and the score is 6.3. The 5.2 score is the conservative,
standalone reading and is the one submitted for this entry.
--------------------------------------------------------------------------------
7. CVSS 4.0 - 6.9 MEDIUM
--------------------------------------------------------------------------------
Vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:P/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N
Metric Value Why
------ ----- ----------------------------------------------------------------
AV N Network-delivered web content.
AC L Deterministic: stored value, unescaped sink, no mitigation to
bypass.
AT N No additional attack requirements.
PR H SUPERUSER required to write the organization name in the
standalone case.
UI P Passive: the victim only has to load the page. CVSS 4.0 uses P
(passive) for this, unlike 3.1's R (required).
VC H Cookie/CSRF theft and full read of the victim's data.
VI L Att |
|---|
| Fuente | ⚠️ https://github.com/aymencdm/magma_pocs/blob/main/02-xss-poc.sh |
|---|
| Usuario | aymencdm (UID 101800) |
|---|
| Sumisión | 2026-10-19 22:06 (hace 26 días) |
|---|
| Moderación | 2026-11-14 07:50 (25 days later) |
|---|
| Estado | Aceptado |
|---|
| Entrada de VulDB | 437421 [Linux Foundation Magma hasta 1.9.0 NMS routes.ts handleReact secuencias de comandos en sitios cruzados] |
|---|
| Puntos | 20 |
|---|