Skip to content
vulnerability in “misc.dnsmasq,”

vulnerability in “misc.dnsmasq,”

github.com September 21, 2026

The misc.dnsmasq_lines config option lets you add arbitrary lines to the generated dnsmasq configuration. Each entry is written verbatim into dnsmasq.conf and the only validation is a newline check, so exec-capable dnsmasq directives like dhcp-script= and dhcp-luascript= are accepted and honored. That gives arbitrary code execution as the pihole-FTL service user.

This is reachable through the web UI and the config API ( PATCH /api/config ) as an authenticated admin, and unauthenticated when no admin password is set.

The payload file itself can be staged over the API too (via a Teleporter import), so no prior shell access is needed.

conf-script= needs no payload file at all. dnsmasq hands that option to popen() , so the value is a shell command line rather than a path, and it runs during the --test pass FTL performs on the configuration it generates - inside the API request itself, with nothing on disk and no restart. That also makes POST /api/teleporter an execution path in its own right, not only a way to stage a payload.

Combined with the ambient CAP_CHOWN issue (GHSA-j8vh-6fp9-cjcx) this becomes root. I saw the recent newline-injection fixes when ensuring this wasn't a duplicate. This field (designed to pass raw directives) was left open.

The misc.dnsmasq_lines validator only rejects newlines:

src/config/config.c:1426

Each element is then written verbatim into the dnsmasq config:

src/config/dnsmasq_config.c:850-854

dhcp-script=/path and dhcp-luascript=/path contain no newline, so they pass and dnsmasq runs them:

dhcp-script runs an executable on DHCP/ARP/TFTP events. Adding script-arp causes it to run immediately in each of my tests even without a dhcp client.

dhcp-luascript is loaded via luaL_dofile at helper startup ( src/dnsmasq/helper.c:152 ) and its top-level code runs immediately, so no trigger is needed.

To stage the payload file entirely over the API, a Teleporter import ( POST /api/teleporter ) writes archive members verbatim to fixed paths. /etc/pihole/dhcp.leases is an obvious target on systems with DHCP disabled since dnsmasq never re-parses or rewrites it. custom.list gets a # header added on import which breaks Lua parsing, but the original copy is still reachable at /etc/pihole/migration_backup_v6/custom.list . Generates warnings on startup from the invalid hosts entries but things proceed normally.

conf-script reaches popen() during config validation

Reported independently by Michael-JRead and m19simmons.

conf-script is a known option ( src/dnsmasq/option.c:255 ), so it survives the --test parse, and one_file() runs it through popen() instead of fopen() :

write_dnsmasq_config() runs that pass whenever test_config is true, which every caller reachable over the API sets: both write paths in src/api/config.c , src/config/cli.c and src/zip/teleporter.c . The command therefore runs while the request is still being handled, before anything is persisted, and it runs even when the request is then answered 400 - an invalid line placed after the conf-script entry leaves no configuration trace behind.

The Teleporter route needs no Pi-hole credentials at all, only an administrator willing to import an archive, because readFTLtoml() never ran the validators and write_dnsmasq_config() is called on whatever the archive carried.

misc.readOnly is not a mitigation for it either: it refuses the PATCH , but an import reaches write_dnsmasq_config() before the read-only early return in writeFTLtoml() , so the command still executes.

Authenticate, upload crafted Teleport file to stage script, add dnsmasq_lines.

Using the API below for simplicity but it can be done via the web ui also.

FTL restarts on the config write and the script runs as the pihole-FTL user.

CapEff: 0000000002807401 includes cap_chown , so per the separately-disclosed CAP_CHOWN issue this escalates to root.

For a system that does not support LUA you can use a shellscript instead with dhcp-script .

I confirmed this all live on an Ubuntu26 system with the latest pihole:

Arbitrary code execution as the pihole-FTL service user via the config API/WebUI. Reachable by an authenticated admin, and unauthenticated when no admin password is set.

Combined with the ambient CAP_CHOWN issue reported separately (GHSA-j8vh-6fp9-cjcx) this is full root.

Affected: FTL >= v6.0.

Please let me know if you have any questions or I can do anything to help.

Should this lead to a CVE/advisory I would appreciate credit as Keith Linneman (LinnemanLabs).

Extracted Entities