GHSA-43gm-9rr3-cx7g: XSS

Published Aug 18, 2026
·
Updated

Summary

A stored Cross-Site Scripting (XSS) vulnerability in Froxlor's DNS editor allows an authenticated user with DNS editor access (customer role) to inject arbitrary JavaScript into any administrator's browser session. When an administrator views the DNS configuration of an affected domain, the payload executes automatically — enabling complete admin account takeover, credential theft, and full server compromise.

---

Details

Three code locations combine to create this vulnerability:

1. Input validation does not strip HTML special characters — lib/Froxlor/Api/Commands/DomainZones.php:158

php // Only strips non-printable chars. < and > (0x3C/0x3E) pass through unmodified. $content = pregreplace('/[^\x09\x20-\x7E]/', '', $content); $content = Dns::encloseTXTContent($content); // only wraps in quotes, no HTML encoding

2. Display callback returns raw HTML without escaping — lib/Froxlor/UI/Callbacks/Text.php:95

php public static function wordwrap(array $attributes): string { return wordwrap($attributes['data'], 100, '<br>', true); // no htmlspecialchars() }

3. Twig template renders the callback output with |raw — templates/Froxlor/table/table.html.twig:57

twig {% else %} {{ td.data|raw }} {# string from wordwrap() — rendered without escaping #} {% endif %}

The DNS editor table assigns [Text::class, 'wordwrap'] as the callback for the content column (lib/tablelisting/tablelisting.dns.php:58). The callback returns a non-iterable string, so the template falls to the |raw branch.

Additionally, the Content Security Policy header (lib/Froxlor/UI/Panel/UI.php:140) includes 'unsafe-inline', rendering CSP completely ineffective as a mitigation:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; ...

---

PoC <img width="2025" height="1144" alt="image" src="https://github.com/user-attachments/assets/f6808b24-c4b6-4bd2-9673-d7ddc4939794" />

Prerequisites: Froxlor running with DNS enabled (system.dnsenabled = 1), at least one domain with DNS editor enabled, and a user account (customer or admin) with DNS editor access.

Step 1 — Inject the payload (via web UI or API as any DNS-enabled user):

Navigate to the DNS editor for any domain, add a TXT record with: - Record: @ - Type: TXT - Content: <img src=x onerror=alert(document.domain)>

Step 2 — Trigger:

No interaction is required beyond page navigation. The payload fires automatically on page load the moment any logged-in administrator visits:

http://TARGET/admindomains.php?page=domaindnseditor&domainid=<id>

This URL is part of the normal admin workflow (domain management → DNS editor). No clicking, no form submission, no special conditions — visiting the URL is sufficient.

Verify via command line (login + fetch in one line):

bash T=$(curl -sc /tmp/c http://TARGET/index.php | grep -oP 'csrf-token" content="\K[^"]+') && \ curl -sc /tmp/c -b /tmp/c http://TARGET/index.php \ -d "loginname=admin&password=PASS&dologin=1&send=send&csrftoken=$T" -o /dev/null && \ curl -sb /tmp/c "http://TARGET/admindomains.php?page=domaindnseditor&domainid=ID" \ | grep -o '<img src=x[^>]>'

Expected output confirming unescaped payload in page source:

<img src=x onerror=alert(document.domain)>

In a browser session the alert() fires immediately — no clicks required.

---

Impact

Type: Stored Cross-Site Scripting (Stored XSS)

Who is impacted: Any Froxlor installation with DNS editor functionality enabled. The attack requires a low-privilege customer account with dnsenabled = 1 — a standard feature granted to hosting customers. The victim is any administrator who views the affected domain's DNS configuration.

A real-world attacker would replace alert() with a payload that silently exfiltrates the admin session cookie, then uses it to create a backdoor admin account, read all customer credentials, or execute arbitrary commands on the underlying server through Froxlor's system configuration interface.

---

Fix

Apply one of the following:

Option A (recommended) — Remove |raw from the table template:

twig {# templates/Froxlor/table/table.html.twig:57 #} {{ td.data }} {# Twig auto-escaping handles it #}

Callbacks that intentionally return HTML (e.g. action buttons) should return a structured array with a macro key instead of a raw string.

Option B — Escape in the callback:

php // lib/Froxlor/UI/Callbacks/Text.php public static function wordwrap(array $attributes): string { return wordwrap(htmlspecialchars($attributes['data'], ENTQUOTES, 'UTF-8'), 100, '<br>', true); }

Option C — Sanitize at input:

php // lib/Froxlor/Api/Commands/DomainZones.php after line 160 $content = htmlspecialchars($content, ENTQUOTES, 'UTF-8');

Also remove 'unsafe-inline' and 'unsafe-eval' from the CSP header in lib/Froxlor/UI/Panel/UI.php:140.

--- If possible, please apply for a CVE when publishing.

Affected Software

1 affected componentFixes available
composer/froxlor/froxlor<=2.3.7
2.3.8

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/froxlor/froxlor to a version that resolves this vulnerability.

    Fixed in 2.3.8
  2. Configuration

    In `templates/Froxlor/table/table.html.twig:57`, remove the `|raw` branch so callback output for the `content` column is rendered with Twig auto-escaping (use `{{ td.data }}` instead of `{{ td.data|raw }}`).

    Froxlor DNS editor (table template / Twig) Twig rendering filter for TXT content = remove `|raw` (use escaped output, i.e., render `{{ td.data }}` instead of `{{ td.data|raw }}`)
  3. Configuration

    In `lib/Froxlor/UI/Callbacks/Text.php:95` (and where the DNS editor uses `[Text::class, 'wordwrap']` for the `content` column), ensure the callback output escapes HTML characters (e.g., use `htmlspecialchars($attributes['data'], ENT_QUOTES, 'UTF-8')` before `wordwrap(...)`) so `<` and `>` do not pass through unmodified.

    Froxlor UI callback text/wordwrap HTML escaping in `Text::wordwrap` callback = wrap with `htmlspecialchars(..., ENT_QUOTES, 'UTF-8')` before `wordwrap()`
  4. Configuration

    In `lib/Froxlor/UI/Panel/UI.php:140`, update the Content-Security-Policy header to remove `'unsafe-inline'` and `'unsafe-eval'` from `script-src` (CSP is currently ineffective because it includes `'unsafe-inline'`).

    Froxlor CSP header Content-Security-Policy 'script-src' directives = remove `'unsafe-inline'` and `'unsafe-eval'` from the CSP header in `lib/Froxlor/UI/Panel/UI.php:140`

Event History

Aug 18, 2026
Advisory Published
via GitHub·08:47 PM
Data Sourced
via GitHub·08:47 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which users and workflows are exposed?

Froxlor deployments that allow customer-role users to access the DNS editor are exposed. A malicious customer can place a payload in DNS record content for a domain, and it executes when an administrator views that domain's DNS configuration.

2

What access and interaction are required for exploitation?

The attacker needs an authenticated account with customer-level DNS editor access and must be able to persuade or wait for an administrator to view the affected domain's DNS configuration. No administrator interaction beyond viewing that configuration is required for the stored payload to execute.

3

How can I determine whether a fix is available?

The advisory identifies release 2.3.8 and references the commit a1d8f425b11ef7597949018814afa056a842cba0. Use those references to determine whether your installed Froxlor version includes the fix.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203