VYPR
High severity7.3NVD Advisory· Published May 31, 2026

CVE-2026-10157

CVE-2026-10157

Description

Open5GS AMF in versions up to 2.7.6 unconditionally overwrites UE security capabilities during NGAP PathSwitchRequest, enabling a malicious gNB to inject arbitrary values.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Open5GS AMF in versions up to 2.7.6 unconditionally overwrites UE security capabilities during NGAP PathSwitchRequest, enabling a malicious gNB to inject arbitrary values.

Vulnerability

In Open5GS up to version 2.7.6, the AMF component fails to verify UE security capabilities received in NGAP PathSwitchRequest messages against its locally stored values. The vulnerability resides in src/amf/ngap-handler.c lines 2997–3007. Instead of comparing the received algorithm bitstrings with the stored values as mandated by 3GPP TS 33.501 §6.7.3.1, the code unconditionally overwrites the AMF's stored capabilities using memcpy and bit-shift operations [1]. The only check performed is a size validation (ensuring the bitstring fields are exactly 2 bytes), which prevents a buffer overflow but does not enforce security equivalence. This affects all deployments of Open5GS up to and including version 2.7.6 [1].

Exploitation

An attacker with control of a malicious gNB can send a crafted NGAP PathSwitchRequest message to the AMF during an Xn-handover. The attacker does not need prior authentication or special privileges beyond network access to the AMF endpoint. By including arbitrary values in the UESecurityCapabilities IE, the attacker can overwrite the AMF's stored security capabilities for the target UE. These overwritten values are then propagated to other legitimate gNBs in subsequent Handover Request and PathSwitchRequestAcknowledge messages [1]. The exploit is publicly available, and the attack is remotely triggerable [1].

Impact

A successful exploit allows the attacker to permanently alter the UE's security capabilities stored in the AMF. This leads to a persistent handover denial-of-service condition for the affected UE, as the mismatched or downgraded capabilities cause the target gNB to reject or mishandle subsequent handovers. The attacker does not directly gain code execution or data disclosure, but the integrity of the UE's security context is compromised, violating 3GPP security requirements [1].

Mitigation

The vulnerability is fixed in commit a188e36b1741ffc2252133f59b1bda4f14d3cb5c [2], which is included in pull request #4557 [4]. The fix introduces comparison logic: if the received UE security capabilities mismatch the locally stored values, the AMF retains its stored values and sends them in the PathSwitchRequestAcknowledge message, as required by the standard [2][4]. Users should update to a version that includes this patch. No workaround is available; the vulnerability is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog as of the publication date.

AI Insight generated on May 31, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
a188e36b1741

ngap, s1ap: preserve UE security capabilities on path switch

https://github.com/open5gs/open5gsSukchan LeeMay 11, 2026via nvd-ref
6 files changed · +168 20
  • src/amf/context.h+3 0 modified
    @@ -444,6 +444,9 @@ struct amf_ue_s {
         /* Security Context */
         ogs_nas_ue_security_capability_t ue_security_capability;
         ogs_nas_ue_network_capability_t ue_network_capability;
    +
    +    /* Transient Path Switch state */
    +    bool send_ue_security_capability_in_path_switch_ack;
     #define CHECK_5G_AKA_CONFIRMATION(__aMF) \
         ((__aMF) && ((__aMF)->confirmation_for_5g_aka.resource_uri))
     #define STORE_5G_AKA_CONFIRMATION(__aMF, __rESOURCE_URI) \
    
  • src/amf/ngap-build.c+50 0 modified
    @@ -2000,15 +2000,21 @@ ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue)
         NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL;
         NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL;
         NGAP_SecurityContext_t *SecurityContext = NULL;
    +    NGAP_UESecurityCapabilities_t *UESecurityCapabilities = NULL;
         NGAP_PDUSessionResourceSwitchedList_t *PDUSessionResourceSwitchedList;
         NGAP_AllowedNSSAI_t *AllowedNSSAI = NULL;
    +    bool send_ue_security_capability = false;
     
         ogs_assert(amf_ue);
         ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id);
         ogs_assert(ran_ue);
     
         ogs_debug("PathSwitchAcknowledge");
     
    +    send_ue_security_capability =
    +        amf_ue->send_ue_security_capability_in_path_switch_ack;
    +    amf_ue->send_ue_security_capability_in_path_switch_ack = false;
    +
         memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t));
         pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
         pdu.choice.successfulOutcome = CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t));
    @@ -2077,6 +2083,50 @@ ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue)
         PDUSessionResourceSwitchedList =
             &ie->value.choice.PDUSessionResourceSwitchedList;
     
    +    if (send_ue_security_capability) {
    +        ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestAcknowledgeIEs_t));
    +        ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie);
    +
    +        ie->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities;
    +        ie->criticality = NGAP_Criticality_reject;
    +        ie->value.present =
    +            NGAP_PathSwitchRequestAcknowledgeIEs__value_PR_UESecurityCapabilities;
    +
    +        UESecurityCapabilities = &ie->value.choice.UESecurityCapabilities;
    +
    +        UESecurityCapabilities->nRencryptionAlgorithms.size = 2;
    +        UESecurityCapabilities->nRencryptionAlgorithms.buf =
    +            CALLOC(UESecurityCapabilities->
    +                        nRencryptionAlgorithms.size, sizeof(uint8_t));
    +        UESecurityCapabilities->nRencryptionAlgorithms.bits_unused = 0;
    +        UESecurityCapabilities->nRencryptionAlgorithms.buf[0] =
    +            (amf_ue->ue_security_capability.nr_ea << 1);
    +
    +        UESecurityCapabilities->nRintegrityProtectionAlgorithms.size = 2;
    +        UESecurityCapabilities->nRintegrityProtectionAlgorithms.buf =
    +            CALLOC(UESecurityCapabilities->
    +                        nRintegrityProtectionAlgorithms.size, sizeof(uint8_t));
    +        UESecurityCapabilities->nRintegrityProtectionAlgorithms.bits_unused = 0;
    +        UESecurityCapabilities->nRintegrityProtectionAlgorithms.buf[0] =
    +            (amf_ue->ue_security_capability.nr_ia << 1);
    +
    +        UESecurityCapabilities->eUTRAencryptionAlgorithms.size = 2;
    +        UESecurityCapabilities->eUTRAencryptionAlgorithms.buf =
    +            CALLOC(UESecurityCapabilities->
    +                        eUTRAencryptionAlgorithms.size, sizeof(uint8_t));
    +        UESecurityCapabilities->eUTRAencryptionAlgorithms.bits_unused = 0;
    +        UESecurityCapabilities->eUTRAencryptionAlgorithms.buf[0] =
    +            (amf_ue->ue_security_capability.eutra_ea << 1);
    +
    +        UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.size = 2;
    +        UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.buf =
    +            CALLOC(UESecurityCapabilities->
    +                    eUTRAintegrityProtectionAlgorithms.size, sizeof(uint8_t));
    +        UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.bits_unused = 0;
    +        UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.buf[0] =
    +            (amf_ue->ue_security_capability.eutra_ia << 1);
    +    }
    +
         ogs_list_for_each(&amf_ue->sess_list, sess) {
             OCTET_STRING_t *transfer = NULL;
             NGAP_PDUSessionResourceSwitchedItem_t *PDUSessionItem = NULL;
    
  • src/amf/ngap-handler.c+45 13 modified
    @@ -2750,7 +2750,8 @@ void ngap_handle_path_switch_request(
         NGAP_EUTRAintegrityProtectionAlgorithms_t
             *eUTRAintegrityProtectionAlgorithms = NULL;
         uint16_t nr_ea = 0, nr_ia = 0, eutra_ea = 0, eutra_ia = 0;
    -    uint8_t nr_ea0 = 0, nr_ia0 = 0, eutra_ea0 = 0, eutra_ia0 = 0;
    +    uint8_t received_nr_ea = 0, received_nr_ia = 0;
    +    uint8_t received_eutra_ea = 0, received_eutra_ia = 0;
     
         NGAP_PDUSessionResourceToBeSwitchedDLItem_t *PDUSessionItem = NULL;
         OCTET_STRING_t *transfer = NULL;
    @@ -2861,6 +2862,8 @@ void ngap_handle_path_switch_request(
             return;
         }
     
    +    amf_ue->send_ue_security_capability_in_path_switch_ack = false;
    +
         ogs_info("    [OLD] RAN_UE_NGAP_ID[%lld] AMF_UE_NGAP_ID[%lld] ",
             (long long)ran_ue->ran_ue_ngap_id, (long long)ran_ue->amf_ue_ngap_id);
         ogs_info("    [OLD] TAC[%d] CellID[0x%llx]",
    @@ -3014,30 +3017,59 @@ void ngap_handle_path_switch_request(
             ogs_assert(r != OGS_ERROR);
             return;
         }
    +    /*
    +     * TS 33.501 6.7.3.1:
    +     *
    +     * The AMF shall verify that the UE's 5G security capabilities
    +     * received from the target gNB/ng-eNB are the same as the UE's
    +     * 5G security capabilities locally stored in the AMF.
    +     *
    +     * If there is a mismatch, the AMF shall send its locally stored
    +     * UE security capabilities to the target gNB/ng-eNB in the
    +     * Path Switch Request Acknowledge message.
    +     *
    +     * Therefore, do not overwrite amf_ue->ue_security_capability
    +     * with the value received in PathSwitchRequest.
    +     */
         memcpy(&nr_ea, nRencryptionAlgorithms->buf, sizeof(nr_ea));
         nr_ea = be16toh(nr_ea);
    -    nr_ea0 = amf_ue->ue_security_capability.nr_ea0;
    -    amf_ue->ue_security_capability.nr_ea = nr_ea >> 9;
    -    amf_ue->ue_security_capability.nr_ea0 = nr_ea0;
    +    received_nr_ea = nr_ea >> 9;
     
         memcpy(&nr_ia, nRintegrityProtectionAlgorithms->buf, sizeof(nr_ia));
         nr_ia = be16toh(nr_ia);
    -    nr_ia0 = amf_ue->ue_security_capability.nr_ia0;
    -    amf_ue->ue_security_capability.nr_ia = nr_ia >> 9;
    -    amf_ue->ue_security_capability.nr_ia0 = nr_ia0;
    +    received_nr_ia = nr_ia >> 9;
     
         memcpy(&eutra_ea, eUTRAencryptionAlgorithms->buf, sizeof(eutra_ea));
         eutra_ea = be16toh(eutra_ea);
    -    eutra_ea0 = amf_ue->ue_security_capability.eutra_ea0;
    -    amf_ue->ue_security_capability.eutra_ea = eutra_ea >> 9;
    -    amf_ue->ue_security_capability.eutra_ea0 = eutra_ea0;
    +    received_eutra_ea = eutra_ea >> 9;
     
         memcpy(&eutra_ia,
                 eUTRAintegrityProtectionAlgorithms->buf, sizeof(eutra_ia));
         eutra_ia = be16toh(eutra_ia);
    -    eutra_ia0 = amf_ue->ue_security_capability.eutra_ia0;
    -    amf_ue->ue_security_capability.eutra_ia = eutra_ia >> 9;
    -    amf_ue->ue_security_capability.eutra_ia0 = eutra_ia0;
    +    received_eutra_ia = eutra_ia >> 9;
    +
    +    if (received_nr_ea != (amf_ue->ue_security_capability.nr_ea & 0x7f) ||
    +        received_nr_ia != (amf_ue->ue_security_capability.nr_ia & 0x7f) ||
    +        received_eutra_ea !=
    +            (amf_ue->ue_security_capability.eutra_ea & 0x7f) ||
    +        received_eutra_ia !=
    +            (amf_ue->ue_security_capability.eutra_ia & 0x7f)) {
    +        amf_ue->send_ue_security_capability_in_path_switch_ack = true;
    +
    +        ogs_warn("[%s] UE Security Capability mismatch in "
    +                "PathSwitchRequest",
    +                amf_ue->supi ? amf_ue->supi : "Unknown");
    +        ogs_warn("    Stored  NR_EA[0x%x] NR_IA[0x%x] "
    +                "EUTRA_EA[0x%x] EUTRA_IA[0x%x]",
    +                amf_ue->ue_security_capability.nr_ea,
    +                amf_ue->ue_security_capability.nr_ia,
    +                amf_ue->ue_security_capability.eutra_ea,
    +                amf_ue->ue_security_capability.eutra_ia);
    +        ogs_warn("    Received NR_EA[0x%x] NR_IA[0x%x] "
    +                "EUTRA_EA[0x%x] EUTRA_IA[0x%x]",
    +                received_nr_ea, received_nr_ia,
    +                received_eutra_ea, received_eutra_ia);
    +    }
     
         /* Update Security Context (NextHop) */
         amf_ue->nhcc++;
    
  • src/mme/mme-context.h+4 0 modified
    @@ -564,6 +564,10 @@ struct mme_ue_s {
     
         /* Security Context */
         ogs_nas_ue_network_capability_t ue_network_capability;
    +
    +    /* Transient Path Switch state */
    +    bool send_ue_security_capability_in_path_switch_ack;
    +
         ogs_nas_ms_network_capability_t ms_network_capability;
         ogs_nas_ue_additional_security_capability_t
             ue_additional_security_capability;
    
  • src/mme/s1ap-build.c+35 0 modified
    @@ -1803,6 +1803,8 @@ ogs_pkbuf_t *s1ap_build_path_switch_ack(
         S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL;
         S1AP_E_RABToBeSwitchedULList_t *E_RABToBeSwitchedULList = NULL;
         S1AP_SecurityContext_t *SecurityContext = NULL;
    +    S1AP_UESecurityCapabilities_t *UESecurityCapabilities = NULL;
    +    bool send_ue_security_capability = false;
     
         mme_sess_t *sess = NULL;
         mme_bearer_t *bearer = NULL;
    @@ -1814,6 +1816,10 @@ ogs_pkbuf_t *s1ap_build_path_switch_ack(
     
         ogs_debug("PathSwitchAcknowledge");
     
    +    send_ue_security_capability =
    +        mme_ue->send_ue_security_capability_in_path_switch_ack;
    +    mme_ue->send_ue_security_capability_in_path_switch_ack = false;
    +
         memset(&pdu, 0, sizeof (S1AP_S1AP_PDU_t));
         pdu.present = S1AP_S1AP_PDU_PR_successfulOutcome;
         pdu.choice.successfulOutcome = CALLOC(1, sizeof(S1AP_SuccessfulOutcome_t));
    @@ -1909,6 +1915,35 @@ ogs_pkbuf_t *s1ap_build_path_switch_ack(
         memcpy(SecurityContext->nextHopParameter.buf,
                 mme_ue->nh, SecurityContext->nextHopParameter.size);
     
    +    if (send_ue_security_capability) {
    +        ie = CALLOC(1, sizeof(S1AP_PathSwitchRequestAcknowledgeIEs_t));
    +        ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie);
    +
    +        ie->id = S1AP_ProtocolIE_ID_id_UESecurityCapabilities;
    +        ie->criticality = S1AP_Criticality_ignore;
    +        ie->value.present =
    +            S1AP_PathSwitchRequestAcknowledgeIEs__value_PR_UESecurityCapabilities;
    +
    +        UESecurityCapabilities = &ie->value.choice.UESecurityCapabilities;
    +
    +        UESecurityCapabilities->encryptionAlgorithms.size = 2;
    +        UESecurityCapabilities->encryptionAlgorithms.buf =
    +            CALLOC(UESecurityCapabilities->encryptionAlgorithms.size,
    +                        sizeof(uint8_t));
    +        UESecurityCapabilities->encryptionAlgorithms.bits_unused = 0;
    +        UESecurityCapabilities->encryptionAlgorithms.buf[0] =
    +            (mme_ue->ue_network_capability.eea << 1);
    +
    +        UESecurityCapabilities->integrityProtectionAlgorithms.size = 2;
    +        UESecurityCapabilities->integrityProtectionAlgorithms.buf =
    +            CALLOC(UESecurityCapabilities->
    +                            integrityProtectionAlgorithms.size,
    +                        sizeof(uint8_t));
    +        UESecurityCapabilities->integrityProtectionAlgorithms.bits_unused = 0;
    +        UESecurityCapabilities->integrityProtectionAlgorithms.buf[0] =
    +            (mme_ue->ue_network_capability.eia << 1);
    +    }
    +
         return ogs_s1ap_encode(&pdu);
     }
     
    
  • src/mme/s1ap-handler.c+31 7 modified
    @@ -2537,7 +2537,7 @@ void s1ap_handle_path_switch_request(
         S1AP_EncryptionAlgorithms_t    *encryptionAlgorithms = NULL;
         S1AP_IntegrityProtectionAlgorithms_t *integrityProtectionAlgorithms = NULL;
         uint16_t eea = 0, eia = 0;
    -    uint8_t eea0 = 0, eia0 = 0;
    +    uint8_t received_eea = 0, received_eia = 0;
     
         enb_ue_t *enb_ue = NULL;
         mme_ue_t *mme_ue = NULL;
    @@ -2711,6 +2711,8 @@ void s1ap_handle_path_switch_request(
             return;
         }
     
    +    mme_ue->send_ue_security_capability_in_path_switch_ack = false;
    +
         if (!SECURITY_CONTEXT_IS_VALID(mme_ue)) {
             ogs_error("No Security Context");
             s1apbuf = s1ap_build_path_switch_failure(
    @@ -2836,11 +2838,21 @@ void s1ap_handle_path_switch_request(
             ogs_assert(r != OGS_ERROR);
             return;
         }
    +    /*
    +     * The MME shall verify that the UE security capabilities received
    +     * from the target eNB are the same as the UE security capabilities
    +     * locally stored in the MME.
    +     *
    +     * If there is a mismatch, the MME shall send its locally stored
    +     * UE security capabilities to the target eNB in the
    +     * Path Switch Request Acknowledge message.
    +     *
    +     * Therefore, do not overwrite mme_ue->ue_network_capability
    +     * with the value received in PathSwitchRequest.
    +     */
         memcpy(&eea, encryptionAlgorithms->buf, sizeof(eea));
         eea = be16toh(eea);
    -    eea0 = mme_ue->ue_network_capability.eea0;
    -    mme_ue->ue_network_capability.eea = eea >> 9;
    -    mme_ue->ue_network_capability.eea0 = eea0;
    +    received_eea = eea >> 9;
     
         if (integrityProtectionAlgorithms->size != sizeof(eia)) {
             ogs_error("Invalid integrityProtectionAlgorithms->size = %d "
    @@ -2857,9 +2869,21 @@ void s1ap_handle_path_switch_request(
         }
         memcpy(&eia, integrityProtectionAlgorithms->buf, sizeof(eia));
         eia = be16toh(eia);
    -    eia0 = mme_ue->ue_network_capability.eia0;
    -    mme_ue->ue_network_capability.eia = eia >> 9;
    -    mme_ue->ue_network_capability.eia0 = eia0;
    +    received_eia = eia >> 9;
    +
    +    if (received_eea != (mme_ue->ue_network_capability.eea & 0x7f) ||
    +        received_eia != (mme_ue->ue_network_capability.eia & 0x7f)) {
    +        mme_ue->send_ue_security_capability_in_path_switch_ack = true;
    +
    +        ogs_warn("[%s] UE Security Capability mismatch in "
    +                "PathSwitchRequest",
    +                MME_UE_HAVE_IMSI(mme_ue) ? mme_ue->imsi_bcd : "Unknown");
    +        ogs_warn("    Stored  EEA[0x%x] EIA[0x%x]",
    +                mme_ue->ue_network_capability.eea,
    +                mme_ue->ue_network_capability.eia);
    +        ogs_warn("    Received EEA[0x%x] EIA[0x%x]",
    +                received_eea, received_eia);
    +    }
     
         /* Update Security Context (NextHop) */
         mme_ue->nhcc++;
    

Vulnerability mechanics

Root cause

"Missing verification of UE Security Capabilities in NGAP PathSwitchRequest allows a malicious gNB to overwrite the AMF's stored values."

Attack vector

A malicious gNB sends a crafted NGAP PathSwitchRequest message containing arbitrary UE Security Capabilities (e.g., zeroed integrity-protection algorithms). The AMF overwrites its locally stored values without verification, as required by 3GPP TS 33.501 §6.7.3.1. The corrupted capabilities are then echoed back in PathSwitchRequestAcknowledge and used in subsequent HandoverRequest messages to legitimate gNBs, which reject the handover because no matching security algorithms exist (TS 38.413 §8.4.2.4). This results in a persistent denial-of-service for the affected UE until a new registration occurs. [ref_id=1] [CWE-358]

Affected code

The vulnerability resides in `src/amf/ngap-handler.c` (lines 2997–3007) where the AMF unconditionally overwrites stored UE security capabilities with values received in a PathSwitchRequest. The corrupted values are later propagated by `ngap_build_handover_request()` in `src/amf/ngap-build.c` (line 2265). [ref_id=1]

What the fix does

The patch in commit a188e36b1741ffc2252133f59b1bda4f14d3cb5c stops overwriting `amf_ue->ue_security_capability` with the values from PathSwitchRequest. Instead, it stores the received values in temporary variables (`received_nr_ea`, `received_nr_ia`, etc.) and compares them against the stored capabilities. On mismatch, it sets a flag (`send_ue_security_capability_in_path_switch_ack`) and logs a warning, so the AMF returns its own stored (correct) capabilities in the PathSwitchRequestAcknowledge as mandated by TS 33.501 §6.7.3.1. [ref_id=2]

Preconditions

  • networkThe attacker must control a gNB that is allowed to send NGAP PathSwitchRequest messages to the Open5GS AMF.
  • authA UE must be registered with the AMF and have established a UE security context.
  • configThe AMF must be running Open5GS v2.7.6 or earlier (unpatched).

Reproduction

1. Start Open5GS AMF and register a UE through a gNB (standard NG Setup → Registration → Authentication → Security Mode → PDU Session Setup). 2. Send a normal HandoverRequired message from the gNB and confirm the HandoverRequest shows `nRintegrityProtectionAlgorithms = 0xe000`. 3. Send a PathSwitchRequest with `nRintegrityProtectionAlgorithms = 0x0000`. 4. Send another HandoverRequired message; the HandoverRequest now carries `nRintegrityProtectionAlgorithms = 0x0000`, confirming the corruption. [ref_id=1]

Generated on May 31, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.