Pi-hole FTL v6.8, Web v6.6.1 and Core v6.5 released
Pi-hole FTL v6.8, Web v6.6.1 and Core v6.5 are now available.
This release fixes a number of security advisories across FTL, Web and Core. Thank you to everyone who reported them: @abdoghazy2015, @KimiSecurityTeam, @keithlinneman, @Pedromdsn, @Jgarcg40, @Rakan78, @dagadjno, @010011110, @ChrisLundquist and @KHr00t.
Before updating, please read the changelogs below. Update with pihole -up. If you want a safety net, take a backup of your configuration first with Teleporter (Settings -> Teleporter in the web interface, or pihole-FTL --teleporter).
The Docker image is tagged 2026.10.0.
Security fixes
High
- Core: command execution via a crafted PID (GHSA-cf36-4hhc-8398, CVSS 7.8):
getFTLPID()now only accepts a PID naming a runningpihole-FTL, and logrotate uses it. Reported by @abdoghazy2015. - Core: root writing to config-supplied paths (GHSA-mff6-cjc4-5wq9, CVSS 7.3):
gravity.shre-executes aspihole, and the log flush writes aspihole. Reported by @KimiSecurityTeam, @keithlinneman and @Pedromdsn. - Core: root following a symlink when writing the version file (GHSA-xch2-4qxw-g5vj, CVSS 7.8): the update checker and FTL prestart script no longer create or chmod files in the
pihole-owned/etc/piholeas root. Reported by @keithlinneman and @KimiSecurityTeam. - Core:
/opt/piholehardening (GHSA-j8vh-6fp9-cjcx, CVSS 7.8): read-only for the FTL service. Reported by @keithlinneman. - FTL: document root relocation via Teleporter import / symlink (GHSA-2h7g-g549-hf74, CVSS 7.7): an authenticated session could move the document root to
/and, withwebserver.serve_all, serve files unauthenticated. Reported by @abdoghazy2015 and @Jgarcg40.
Medium
- FTL: local file contents echoed when parsing a
file://adlist (GHSA-667h-gpmh-7gxc, CVSS 6.5): an authenticated user could read files thepiholeuser can open. Needs the matching Core change (#6706). Reported by @Rakan78. - FTL: app sessions could read password hashes and the TOTP secret (GHSA-v53r-4564-3449, CVSS 6.5; duplicate GHSA-846w-3j3v-2hx8): read-only app sessions could read both via
/api/configand/api/teleporter. Reported by @Pedromdsn and @010011110; fix by @010011110. - FTL: out-of-bounds write in the API TXT buffer (GHSA-8fc6-q9x4-892v, CVSS 5.9): needs a long upstream server list, no authentication. Reported by @dagadjno.
- FTL: EDNS(0) data leaking between clients’ queries (GHSA-xj4x-gf2g-r6m7, CVSS 4.2): data from one packet could be applied to another client’s query. Reported by @010011110.
Low
- Core:
file://adlist read check and curl could resolve different files (GHSA-g63w-24cr-p3w6, CVSS 2.2): a bypass of the read check that the FTLfile://fix relies on, needing an admin who can add adlists plus a symlink and decoy on the filesystem. Local lists are now read withcatas the user whose access was checked. - FTL: cross-site requests and DNS rebinding against a passwordless install (GHSA-322h-f873-hp54): with no password set, any website opened on the network could reach the API through the visitor’s browser. FTL now refuses cross-site browser requests and unrecognised
Hostnames when no password is set. Reported by @ChrisLundquist. - FTL: race when deleting a DHCP lease (GHSA-v8mj-cqgw-63rj, CVSS 2.2): needs an authenticated client and the DHCP server enabled. Reported by @010011110.
- FTL: crash when searching domains during a list reload (GHSA-9828-2f2q-2c8v, CVSS 2.2): needs a valid session. Reported by @010011110.
- FTL: crash on a malformed POSTed regex array (GHSA-mqr7-j2p5-5495, CVSS 2.7): needs a valid session. Reported by @010011110.
- Web: stored XSS on three settings pages (GHSA-rmr4-hqc8-825f, GHSA-3vhj-5284-xrmm, GHSA-qfpp-28mj-pj6p, CVSS 3.5 / 3.1 / 3.1): Settings > All settings, Local DNS Records and DNS. Reported by @KHr00t; fixed by @rdwebdesign.
FTL v6.8
This is a minor release rather than v6.7.2: there are enough visible behaviour changes that a patch version would undersell it. It backports the bug fixes that landed on development since v6.7.1 and updates the embedded dnsmasq (v2.93+30) and SQLite (v3.53.3).
Improvements
- The live log is now stored as a ring buffer instead of moving the whole buffer for every new line, which costs far less CPU per cached answer.
- Internationalised domain names on lists are converted to punycode with libidn2 and block, instead of being rejected as invalid.
- Long-term statistics are grouped before joining the name tables (faster), and two unused in-memory indexes are gone, saving about 22 MB per million queries.
Notable fixes
- Flushing the logs deleted the whole long-term history, not only the last 24 hours, and moved the graph a day into the future.
- Crashes with 257+ alias-clients, with a no-longer-forced
FTLCONF_array, and withdatabase.DBinterval = 0. - Group and list changes not reaching a client until the next reload, stale forced replies, and missing EDE on the first block.
- A file path containing
+(or any other printable ASCII) was reset to its default on start-up. - An open redirect in the
.lpcompatibility redirect, and/ignoringwebserver.serve_all. - Pi-hole’s own certificate for a custom
webserver.domainwas never renewed, and a domain longer than 64 characters ended up as an oversized CN. - The empty-DS DNSSEC loop, via dnsmasq, plus its double-free and information disclosure fixes.
- An API session could enable
misc.readOnlythrough a Teleporter archive, andpihole.toml.lockfailed on NFS. - Any LAN client could make FTL add a new PTR record for every spelling of its own address until a restart.
- The CLI password file and
pihole-tmp.dbwere briefly or permanently readable by everyone. - On ARM and RISC-V the MAC address of a new device was never looked up at query time, so its MAC-based groups only applied later.
- Raising
misc.privacylevelat runtime did not cover domains and clients already seen, and anntp.sync.intervalabove about 24.8 days made the NTP client sync back to back. - DNS stalls on list reloads and rate limiting, GC double processing, lost queries at shutdown, and wrong long-term statistics.
- Old queries were never deleted from
pihole-FTL.dbon a host that restarts FTL at least once a day. dhcp-discoverprinted the DNSSL labels of Router Advertisements unescaped.- Clients without a MAC address (e.g. a WireGuard peer) stayed in the default group until the next list reload, because the groups of a new client were resolved before its interface was known.
- The load check counted only the CPUs a container was limited to, while
/proc/loadavgshows the host’s load, so the long-term load warning fired spuriously and/api/info/systeminflated the load percentage in LXC/Docker with fewer cores. FTL now counts all present host CPUs. - Assorted API fixes: CORS headers on 204 and error responses, the reply shape of
/api/queriesat privacy level 3,/api/history/clientsat every privacy level, 64-bit swap figures in/api/info/system, and a rejectedPOST /api/teleporterupload whose import field does not parse. FTLCONF_files_log_ftlwas not used for log lines on early startup.
Behaviour changes to be aware of
- Invalid values found in
pihole.tomlare rejected and reset to the default:database.DBintervalof 0, awebserver.tls.certbelow the webroot, a non-base32webserver.api.totp_secret, relative paths for files FTL writes, and afiles.tmp_dbthat names another file Pi-hole keeps (e.g.pihole-FTL.db). Awebserver.api.maxHistoryabove 86400 is reduced to 86400. A list (e.g.dns.hosts) now loses only its invalid entries instead of being reset as a whole. - With
webserver.serve_all = false,/under a host other thanwebserver.domainnow returns a 404, so a customindex.htmlin the webroot no longer shows up athttp://<ip>/. Behind a proxy that does not stripwebserver.paths.prefix, the prefixed path returns a 404 as well. - A renewed self-signed certificate comes with a new CA, so an imported
tls_ca.crthas to be imported again. - FTL no longer hands a symbolic link (e.g.
pihole-FTL.dborFTL.loglinked to another disk) to thepiholeuser, and warns about it on every start. Such a target has to be owned bypiholealready. - When FTL runs as root it only changes ownership or permissions of regular files directly in
/etc/piholeand/var/log/pihole, and refuses a symbolic link as log file (includingfiles.log.dnsmasq). Started aspihole, a linked log file keeps working. - A blocking timer on an unchanged status now switches to the opposite mode when it expires.
- The host’s own interfaces show up in the network table, and shutdown may wait up to 30 seconds for a busy database thread.
- dnsmasq adds the
option4:syntax and DHCPv6 vendor classes. - Changing
ntp.sync.intervalrestarts FTL,ntp.sync.count = 0is rejected, andPOST /api/action/gravityanswers 409 while a run is in progress. - Queries older than
database.maxDBdaysare deleted in the first night after a start, 10000 at a time. A database that kept growing will lose its whole backlog in the first night after the update. - A v5 Teleporter import replaces the upstreams, reverse servers and excluded domains/clients instead of adding to them.
Web v6.6.1
A patch release for the stable line while development is mid-rework (AdminLTE 4 / Bootstrap 5).
- Query Log: show the actual API error (e.g. “The item is already present”) instead of a generic network error.
- Conditional Forwarding: reject CIDR and otherwise invalid server IPs.
- Static DHCP leases: fix the
tag:validation regex and allow internal underscores in hostnames. hexEncode()now handles code points aboveU+FFFF.- Login no longer blames the password for a refusal that is not about it.
- Query Log: the API URL is no longer encoded twice.
- Bundled Moment.js bumped to 2.31.0.
- DNS settings: clearer “local requests” wording, and an
emptyTabletypo fixed.
Core v6.5
- Stop double-rotating logs now that
piholeis in/etc/logrotate.d. - Remove the deprecated
arpflushcommand in favour ofnetworkflush. - Improve gravity error handling.
- Fix unquoted variable expansion in
rmpaths. - Send
USR2after logrotate. - Keep
/etc/pihole/versionsreadable by all users. - Remove Debian 11 from the CI distro matrix.
- Fix URL parsing in
gravity.sh. - Improve the gravity database schema for performance:
WITHOUT ROWIDjunction tables, new indexes for group lookups and cheaper triggers. Existing databases are migrated to schema version 21 on update. - Store the
listsCachedirectory next togravity.dbinstead of always in/etc/pihole. - Run the update checker and the FTL prestart file fix-ups as the
piholeuser, and only trust git repositories thepiholeuser cannot modify. - Read local
file://adlists as the user whose access was checked, instead of via curl. - Parse local
file://adlist URIs with parameter expansion and path canonicalisation (RFC 8089), by @mwoolweaver. - Pass the adlist source URL to FTL’s
parseList(needed by the FTL v6.8file://adlist fix). - Flush the last 24 hours of queries through the FTL API instead of touching the database directly.
- Stop granting
CAP_CHOWNto everythingpihole-FTLexecutes (kept in the ambient set of the FTL service).
New contributor: welcome and thank you to @candygoogy for their first contribution!
Thanks
Thank you to everyone who contributed code, reported issues and tested: @010011110, @DL6ER, @PromoFaux, @yubiuser, @rdwebdesign, @darkexplosiveqwx, @mwoolweaver, @simonkelley, @dschaper, @candygoogy, @10adnan75, @zhouyuefu, @TheKhanj, @pankpan, @ooonea, @luizluca, @davidnich, @niklas-e, @mordyovits and @RMA1313.