Skip to content

Stored HTML Injection in Pi-hole Web Interface via Local DNS Records (CNAME/Hosts) in data-tag Attribute

Moderate
PromoFaux published GHSA-6xp4-jw73-f4qp Feb 17, 2026

Package

Pi-Hole Web

Affected versions

<=6.4

Patched versions

None

Description

Summary

Security Researcher Julio Ángel Ferrari (aka T0X1CX) discovered that the Pi-hole web interface contains a stored HTML injection vulnerability (Stored HTML Injection) in the local DNS records configuration page (/admin/settings/dns). This vulnerability allows an authenticated administrator to inject arbitrary HTML code that is stored in the Pi-hole configuration and rendered every time the DNS records table is viewed.

Details

The file scripts/js/settings-dns-records.js contains the populateDataTable() function, which configures the DataTables used to display DNS records. Within this function, the rowCallback callback dynamically generates a delete button for each row. The vulnerable code is located at lines 100–110:

rowCallback(row, data) {
  $(row).attr("data-id", data);
  const button = `<button type="button"
                  class="btn btn-danger btn-xs"
                  id="delete${endpoint}${utils.hexEncode(data)}"
                  data-tag="${data}"
                  data-type="${endpoint}"
                  ${setByEnv ? "disabled" : ""}>
                  <span class="far fa-trash-alt"></span>
                </button>`;
  $(`td:eq(${endpoint === "hosts" ? 2 : 3})`, row).html(button);
},

The issue lies on line 105:

data-tag="${data}"

The data variable contains the full DNS record value exactly as entered by the user and returned by the API. This value is inserted directly into the data-tag HTML attribute without any escaping or sanitization of special characters.

When an attacker supplies a value containing double quotes ("), they can prematurely “close” the data-tag attribute and inject additional HTML attributes into the element.

The JavaScript template string generates the button’s HTML by directly concatenating the value of data. Let’s analyze what happens with a malicious payload.

Value entered by the attacker:

test" style="background:green" x="

Concatenation in the template string:

`data-tag="${data}"`
// Becomes:
`data-tag="test" style="background:green" x=""`

Resulting HTML of the full button:

<button type="button"
        class="btn btn-danger btn-xs"
        id="deletecnameRecords..."
        data-tag="test" style="background:green" x=",target.com"
        data-type="cnameRecords">
    <span class="far fa-trash-alt"></span>
</button>

As can be seen, the attribute style="background:green" has been successfully injected into the

element, modifying its visual appearance.

It is important to note that the code shows an inconsistency in how the data variable is handled:

Line 104: utils.hexEncode(data) is used for the button’s id attribute, which prevents injection in that context.

Line 105: No escaping is applied to the data-tag attribute, leaving it vulnerable.

This inconsistency suggests that the developer was aware of the need to sanitize the data for the id attribute, but failed to apply the same protection to data-tag.

Why existing protections do not work?

The DataTables configuration includes a general protection in columnDefs (lines 88–93):

columnDefs: [
  {
    targets: "_all",
    render: $.fn.dataTable.render.text(),
  },
],

The setting render: $.fn.dataTable.render.text() applied to targets: "_all" automatically escapes the content of data cells. However, this protection does not apply to the button generated in rowCallback because:

The button is manually generated using a template string.

It is inserted into the DOM using $().html(), which interprets the content as HTML.

DataTables’ render function only affects column data, not content generated programmatically in callbacks.

PoC

Log in to Pi-hole via the web interface, and navigate to Settings > Local DNS Records.

image

On the right-hand side, where it says List of local CNAME records, enter the following payload in the Domain field:

test" style="background:yellow" title="VULNERABLE" x="

Then, in the Target field, enter any value, for example: test.com

When you click the Add button, you will see that the delete button for the record appears in yellow the style we injected.

image

Impact

Exploitation of this vulnerability allows an authenticated attacker to inject arbitrary HTML attributes into the buttons of the DNS records table. Since Pi-hole implements a Content Security Policy (CSP) that blocks inline JavaScript, the impact is limited to pure HTML injection: UI spoofing by modifying the visual appearance of buttons (changing colors, sizes, or making dangerous buttons appear safe), manipulation of tooltips via the title attribute to display misleading messages on hover, alteration of accessibility by modifying aria-* attributes that affect users of screen readers, and partial defacement of the administration interface.

In environments where CSP is disabled or less restrictive, this vulnerability could escalate to stored Cross-Site Scripting (XSS), allowing arbitrary JavaScript execution, session cookie theft, and full control over the administrator’s account.

Severity

Moderate

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
Low
Integrity
Low
Availability
None

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:L/I:L/A:N

CVE ID

CVE-2026-26952

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 Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

Improper Encoding or Escaping of Output

The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved. Learn more on MITRE.

Credits