Skip to content

fix(security): prevent session hijack via cleanup OOM and stale credential reuse#6

Draft
Copilot wants to merge 2 commits intomasterfrom
copilot/research-session-handling-vulnerabilities
Draft

fix(security): prevent session hijack via cleanup OOM and stale credential reuse#6
Copilot wants to merge 2 commits intomasterfrom
copilot/research-session-handling-vulnerabilities

Conversation

Copy link
Copy Markdown

Copilot AI commented May 6, 2026

Two security vulnerabilities in the NTLM upstream module's session/connection cache allow an authenticated upstream session to be handed to the wrong client.

Finding 1 — Session hijack via cleanup handler registration failure (Critical)

ngx_pool_cleanup_add returning NULL was silently ignored: the upstream connection was still inserted into the cache with a client_connection pointer that nginx may later recycle for a different client. get_ntlm_peer matches purely on that pointer, so the new client inherits the old authenticated session (ABA pointer-reuse hijack).

Fix: on NULL return, undo the cache insertion (clear in_cache, null both connection pointers, return item to free list) and goto invalid. Log at NGX_LOG_ERR instead of NGX_LOG_DEBUG so the failure is visible in production.

if (cln == NULL) {
    ngx_log_error(NGX_LOG_ERR, pc->log, 0,
                  "ntlm: failed to allocate cleanup handler,"
                  " not caching upstream connection");
    item->in_cache = 0;
    item->peer_connection = NULL;
    item->client_connection = NULL;
    ngx_queue_remove(q);
    ngx_queue_insert_head(&hndp->conf->free, q);
    goto invalid;
}

Finding 2 — Stale credential reuse on an established connection (High)

When a subsequent request on the same keep-alive client TCP connection arrives with new NTLM/Negotiate credentials, the module previously ignored them and silently reused the existing authenticated upstream session.

Fix: in get_ntlm_peer, before committing to a cache hit, evict and close the old session when ntlm_init == 1 (new credentials present) and c->requests >= 2 (handshake complete). The >= 2 threshold is intentional: the NTLM Type-3 message must reuse the same upstream TCP connection as the Type-1 exchange (c->requests == 1 at that point); only post-handshake re-authentication attempts are rejected.

if (hndp->ntlm_init && c->requests >= 2) {
    item->in_cache = 0;
    item->peer_connection = NULL;
    ngx_queue_remove(q);
    ngx_queue_insert_head(&hndp->conf->free, q);
    ngx_http_upstream_ntlm_close(c);
    break;  /* fall through → NGX_OK → fresh upstream connection */
}

Test

Adds TEST 5 to t/001-sanity.t: three pipelined requests on one client TCP connection — NTLM token_A, no auth, NTLM token_B — asserting the third response carries X-NGX-NTLM-AUTH: token_B, not token_A.

Copilot AI and others added 2 commits May 6, 2026 13:59
…tial reuse

Finding 1 (Critical): ngx_http_upstream_free_ntlm_peer - when
ngx_pool_cleanup_add returns NULL, undo the cache insertion and
goto invalid instead of silently continuing. Escalate log level
to NGX_LOG_ERR so the failure is visible in production logs.

Finding 2 (High): ngx_http_upstream_get_ntlm_peer - when a cached
upstream connection exists for the client but the current request
carries new NTLM/Negotiate credentials (ntlm_init==1) and the
upstream connection has already completed the handshake
(c->requests >= 2), evict and close the old authenticated session
rather than silently reusing it for a potentially different identity.
The c->requests >= 2 threshold lets the mid-handshake Type-3 message
still reuse the same upstream TCP connection (requests==1 at that
point) while catching any subsequent re-authentication attempt.

Add TEST 5 to t/001-sanity.t covering the re-auth eviction path.

Agent-Logs-Url: https://github.com/Securepoint/nginx-ntlm-module/sessions/97e86965-9ec8-4edf-8181-22e3722b03fd

Co-authored-by: dsabotta <262123149+dsabotta@users.noreply.github.com>
Copilot AI requested a review from dsabotta May 6, 2026 14:26
Copilot AI added a commit that referenced this pull request May 7, 2026
- Rewrites ngx_http_upstream_ntlm_module.c with four documented security invariants:
  I1. Atomic item release via ngx_http_upstream_ntlm_item_release() helper
  I2. c->read->data (not c->data) for cache item storage on idle connections
  I3. OOM guard: cleanup-add failure aborts cache insertion (session-hijack fix)
  I4. Stale-credential eviction on re-auth with established session

- Fixes from all open PRs (#3, #6, #7):
  - Synchronous cleanup handler (no posted events)
  - Session-hijack via cleanup-OOM (critical)
  - Stale-credential reuse on established connections (high)
  - client_connection NULL in eviction path
  - Fault-injection macro NGX_NTLM_TEST_CLEANUP_NULL for testing

- Adds ntlm_time and ntlm_requests directives
- Adds notify peer callback pass-through
- Updates README.md for v2 with security section
- Updates Docker files to nginx 1.28.0 / alpine 3.20
- Adds TEST 5 (stale credential eviction) and TEST 6 (OOM guard, SKIP)
  to t/001-sanity.t

Agent-Logs-Url: https://github.com/Securepoint/nginx-ntlm-module/sessions/3343cf49-d4a7-4188-89b8-5a89fad4a7fe

Co-authored-by: matthias-lay <163420385+matthias-lay@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants