VYPR
High severity8.5NVD Advisory· Published Apr 23, 2026· Updated Apr 27, 2026

CVE-2026-41230

CVE-2026-41230

Description

Froxlor is open source server administration software. Prior to version 2.3.6, 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. Version 2.3.6 fixes the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
froxlor/froxlorPackagist
< 2.3.62.3.6

Affected products

1

Patches

1
47a8af5d9523

add validation for DNS NAPTR record content

https://github.com/froxlor/froxlorMichael KaufmannMar 29, 2026via ghsa
4 files changed · +61 15
  • lib/Froxlor/Api/Commands/DomainZones.php+4 12 modified
    @@ -213,9 +213,6 @@ public function add()
     		} elseif ($type == 'LOC' && !empty($content)) {
     			if (!Validate::validateDnsLoc($content)) {
     				$errors[] = lng('error.dns_loc_invalid');
    -			} else {
    -				// keep content
    -				$content = $content;
     			}
     		} elseif ($type == 'MX') {
     			if ($prio === null || $prio < 0) {
    @@ -244,6 +241,10 @@ public function add()
     			if ($content == '.' && $prio != 0) {
     				$prio = 0;
     			}
    +		} elseif ($type == 'NAPTR' && !empty($content)) {
    +			if (!Validate::validateDnsNaptr($content)) {
    +				$errors[] = lng('error.dns_naptr_invalid');
    +			}
     		} elseif ($type == 'NS') {
     			// check for trailing dot
     			if (substr($content, -1) == '.') {
    @@ -258,9 +259,6 @@ public function add()
     		} elseif ($type == 'RP' && !empty($content)) {
     			if (!Validate::validateDnsRp($content)) {
     				$errors[] = lng('error.dns_rp_invalid');
    -			} else {
    -				// keep content
    -				$content = $content;
     			}
     		} elseif ($type == 'SRV') {
     			if ($prio === null || $prio < 0) {
    @@ -300,16 +298,10 @@ public function add()
     		} elseif ($type == 'SSHFP' && !empty($content)) {
     			if (!Validate::validateDnsSshfp($content)) {
     				$errors[] = lng('error.dns_sshfp_invalid');
    -			} else {
    -				// keep content
    -				$content = $content;
     			}
     		} elseif ($type == 'TLSA' && !empty($content)) {
     			if (!Validate::validateDnsTlsa($content)) {
     				$errors[] = lng('error.dns_tlsa_invalid');
    -			} else {
    -				// keep content
    -				$content = $content;
     			}
     		} elseif ($type == 'TXT' && !empty($content)) {
     			// check that TXT content is enclosed in " "
    
  • lib/Froxlor/Validate/Validate.php+55 3 modified
    @@ -62,8 +62,8 @@ public static function validate(
     		string $str,
     		string $fieldname,
     		string $pattern = '',
    -			   $lng = '',
    -			   $emptydefault = [],
    +		       $lng = '',
    +		       $emptydefault = [],
     		bool   $throw_exception = false
     	)
     	{
    @@ -440,7 +440,7 @@ public static function validateDnsRp(string $input)
     
     		// remove trailing dot if any
     		$mboxDname = rtrim($mboxDname, '.');
    -		$txtDname  = rtrim($txtDname, '.');
    +		$txtDname = rtrim($txtDname, '.');
     
     		if (!self::validateDomain($mboxDname)) {
     			return false;
    @@ -557,4 +557,56 @@ public static function validateDnsTlsa(string $input)
     
     		return $input;
     	}
    +
    +	public static function validateDnsNaptr(string $input): bool
    +	{
    +		// Split respecting quoted strings
    +		$pattern = '/^
    +        (\d{1,5})\s+                # order
    +        (\d{1,5})\s+                # preference
    +        "([^"]*)"\s+                # flags
    +        "([^"]*)"\s+                # services
    +        "([^"]*)"\s+                # regexp
    +        (\S+)                      # replacement
    +    $/x';
    +
    +		if (!preg_match($pattern, $input, $matches)) {
    +			return false;
    +		}
    +
    +		[, $order, $preference, $flags, $services, $regexp, $replacement] = $matches;
    +
    +		// 1. order & preference: 0–65535
    +		if ($order < 0 || $order > 65535 || $preference < 0 || $preference > 65535) {
    +			return false;
    +		}
    +
    +		// 2. flags: allowed chars (RFC says single letters typically, but allow multiple)
    +		if (!preg_match('/^[A-Za-z0-9]*$/', $flags)) {
    +			return false;
    +		}
    +
    +		// 3. services: usually like "E2U+sip"
    +		if (!preg_match('/^[A-Za-z0-9+:\-]*$/', $services)) {
    +			return false;
    +		}
    +
    +		// 4. regexp: delimiter-based substitution (very loose validation)
    +		// Example: !^.*$!sip:info@example.com!
    +		if ($regexp !== '') {
    +			$delim = $regexp[0];
    +			if (substr_count($regexp, $delim) < 3) {
    +				return false;
    +			}
    +		}
    +
    +		// 5. replacement: must be "." or valid domain
    +		if ($replacement !== '.') {
    +			if (!filter_var($replacement, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) {
    +				return false;
    +			}
    +		}
    +
    +		return true;
    +	}
     }
    
  • lng/de.lng.php+1 0 modified
    @@ -981,6 +981,7 @@
     		'dns_rp_invalid' => 'Ungültiger RP Eintrag',
     		'dns_sshfp_invalid' => 'Ungültiger SSHFP Eintrag',
     		'dns_tlsa_invalid' => 'Ungültiger TLSA Eintrag',
    +		'dns_naptr_invalid' => 'Ungültiger NAPTR Eintrag',
     		'domain_nopunycode' => 'Die Eingabe von Punycode (IDNA) ist nicht notwendig. Die Domain wird automatisch konvertiert.',
     		'domain_noipaddress' => 'Eine IP-Adresse kann nicht als Domain angelegt werden',
     		'dns_record_toolong' => 'Records/Labels können maximal 63 Zeichen lang sein',
    
  • lng/en.lng.php+1 0 modified
    @@ -1052,6 +1052,7 @@
     		'dns_rp_invalid' => 'The RP record has invalid content',
     		'dns_sshfp_invalid' => 'The SSHFP record has invalid content',
     		'dns_tlsa_invalid' => 'The TLSA record has invalid content',
    +		'dns_naptr_invalid' => 'The NAPTR record has invalid content',
     		'domain_nopunycode' => 'You must not specify punycode (IDNA). The domain will automatically be converted',
     		'domain_noipaddress' => 'Cannot add an IP address as domain',
     		'dns_record_toolong' => 'Records/labels can only be up to 63 characters',
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.