free5GC NRF Discovery EncodeGroupId Function Panics on Malformed group-id-list Parameter
Description
free5GC is an open source 5G core network. free5GC NRF prior to version 1.4.2 has an Improper Input Validation vulnerability leading to Denial of Service. All deployments of free5GC using the NRF discovery service are affected. The EncodeGroupId function attempts to access array indices [0], [1], [2] without validating the length of the split data. When the parameter contains insufficient separator characters, the code panics with "index out of range". A remote attacker can cause the NRF service to panic and crash by sending a crafted HTTP GET request with a malformed group-id-list parameter. This results in complete denial of service for the NRF discovery service. free5GC NRF version 1.4.2 fixes the issue. There is no direct workaround at the application level. The recommendation is to apply the provided patch or restrict access to the NRF API to trusted sources only.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/free5gc/nrfGo | < 1.4.2 | 1.4.2 |
Affected products
1Patches
1dac77d8f8f2eMerge pull request #80 from Zach1113/fix/nf-discovery-parameter-validation
3 files changed · +76 −54
go.mod+1 −1 modified@@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/free5gc/openapi v1.2.3 - github.com/free5gc/util v1.3.2-0.20260107090449-c09baaf75b11 + github.com/free5gc/util v1.3.2-0.20260204030658-79d56f347175 github.com/gin-gonic/gin v1.10.0 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/google/uuid v1.6.0
go.sum+4 −2 modified@@ -29,8 +29,8 @@ github.com/free5gc/ngap v1.1.2 h1:AA+s9+eaE8fSTnmBbh1xiD3VPaszonCd7AQJGKX5PHM= github.com/free5gc/ngap v1.1.2/go.mod h1:4HYtB+msoTBc3cEPlSr4ZUUHn3BFZhdPuVASERMzVeI= github.com/free5gc/openapi v1.2.3 h1:w4TmYBR8TUE4ZgKo7eiMZbyZPURSBFlMWiFeX+OsiA8= github.com/free5gc/openapi v1.2.3/go.mod h1:fLvaBtUZrvrzkKrmn5Aza+JNbpWnp3kxKixu6kLSD3k= -github.com/free5gc/util v1.3.2-0.20260107090449-c09baaf75b11 h1:/UZetXmPa5T/TKKxqU3Osw9x3+nIuACTXJxOU+gYGTU= -github.com/free5gc/util v1.3.2-0.20260107090449-c09baaf75b11/go.mod h1:qsv/ez8YhI+pO8bjNiZWXc2xmRE3XuEIa0EDTCPkSy0= +github.com/free5gc/util v1.3.2-0.20260204030658-79d56f347175 h1:F1KLSNuHBQzQC2rjC0Zu0ehaAc1n2Gmkw52g8YgnsdY= +github.com/free5gc/util v1.3.2-0.20260204030658-79d56f347175/go.mod h1:qsv/ez8YhI+pO8bjNiZWXc2xmRE3XuEIa0EDTCPkSy0= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -59,6 +59,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
internal/sbi/processor/nf_discovery.go+71 −51 modified@@ -2,6 +2,7 @@ package processor import ( "encoding/json" + "fmt" "log" "math/big" "net/http" @@ -123,10 +124,18 @@ func (p *Processor) NFDiscoveryProcedure(c *gin.Context, queryParameters url.Val } } - // Check ComplexQuery (FOR REPORT PROBLEM!) - // Build Query Filter - filter := buildFilter(queryParameters) + filter, err := buildFilter(queryParameters) + if err != nil { + problemDetails := &models.ProblemDetails{ + Title: "Invalid Parameter", + Status: http.StatusBadRequest, + Cause: "INVALID_QUERY_PARAM", + Detail: err.Error(), + } + util.GinProblemJson(c, problemDetails) + return + } logger.DiscLog.Traceln("Query filter: ", filter) // Use the filter to find documents @@ -198,7 +207,7 @@ func (p *Processor) NFDiscoveryProcedure(c *gin.Context, queryParameters url.Val c.JSON(http.StatusOK, searchResult) } -func buildFilter(queryParameters url.Values) bson.M { +func buildFilter(queryParameters url.Values) (bson.M, error) { // build the filter filter := bson.M{ "$and": []bson.M{}, @@ -392,8 +401,8 @@ func buildFilter(queryParameters url.Values) bson.M { // [Query-12] dnn if queryParameters["dnn"] != nil { - dnn := queryParameters["dnn"][0] var dnnFilter bson.M + dnn := queryParameters["dnn"][0] switch targetNfType { case "SMF": dnnFilter = bson.M{ @@ -446,15 +455,16 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), dnnFilter) + if dnnFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), dnnFilter) + } } // [Query-13] smf-serving-area if queryParameters["smf-serving-area"] != nil { - var smfServingAreaFilter bson.M smfServingArea := queryParameters["smf-serving-area"][0] if targetNfType == "UPF" { - smfServingAreaFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "$or": []bson.M{ { "upfInfo.smfServingArea": smfServingArea, @@ -465,9 +475,8 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), smfServingAreaFilter) } // [Query-14] tai @@ -505,7 +514,9 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), taiFilter) + if taiFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), taiFilter) + } } // [Query-15] amf-region-id @@ -697,16 +708,17 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), supiFilter) + if supiFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), supiFilter) + } } // [Query-19] ue-ipv4-address if queryParameters["ue-ipv4-address"] != nil { - var ueIpv4AddressFilter bson.M if targetNfType == "BSF" { ueIpv4Address := queryParameters["ue-ipv4-address"][0] ueIpv4AddressNumber := nrf_context.Ipv4ToInt(ueIpv4Address) - ueIpv4AddressFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "$or": []bson.M{ { "bsfInfo.ipv4AddressRanges": bson.M{ @@ -726,17 +738,15 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), ueIpv4AddressFilter) } // [Query-20] ip-domain if queryParameters["ip-domain"] != nil { - var ipDomainFilter bson.M if targetNfType == "BSF" { ipDomain := queryParameters["ip-domain"][0] - ipDomainFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "$or": []bson.M{ { "bsfInfo.ipDomainList": ipDomain, @@ -747,18 +757,16 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), ipDomainFilter) } // [Query-21] ue-ipv6-prefix if queryParameters["ue-ipv6-prefix"] != nil { - var ueIpv6PrefixFilter bson.M if targetNfType == "BSF" { ueIpv6Prefix := queryParameters["ue-ipv6-prefix"][0] ueIpv6PrefixNumber := nrf_context.Ipv6ToInt(ueIpv6Prefix) - ueIpv6PrefixFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "$or": []bson.M{ { "bsfInfo.ipv6PrefixRanges": bson.M{ @@ -778,9 +786,8 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), ueIpv6PrefixFilter) } // [Query-22] pgw-ind @@ -894,14 +901,20 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), gpsiFilter) + if gpsiFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), gpsiFilter) + } } // [Query-25] external-group-identity if queryParameters["external-group-identity"] != nil { var externalGroupIdentityFilter bson.M externalGroupIdentity := queryParameters["external-group-identity"][0] + if err := validator.ValidateGroupIdFormat(externalGroupIdentity); err != nil { + return nil, fmt.Errorf("invalid external-group-identity: %w", err) + } + encodedGroupId := nrf_context.EncodeGroupId(externalGroupIdentity) switch targetNfType { @@ -966,15 +979,16 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), externalGroupIdentityFilter) + if externalGroupIdentityFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), externalGroupIdentityFilter) + } } // [Query-26] data-set if queryParameters["data-set"] != nil { - var dataSetFilter bson.M dataSet := queryParameters["data-set"] if targetNfType == "UDR" { - dataSetFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "$or": []bson.M{ { "udrInfo.supportedDataSets": dataSet, @@ -985,9 +999,8 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), dataSetFilter) } // [Query-27] routing-indicator @@ -1022,7 +1035,9 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), routingIndicatorFilter) + if routingIndicatorFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), routingIndicatorFilter) + } } // [Query-28] group-id-list @@ -1057,12 +1072,13 @@ func buildFilter(queryParameters url.Values) bson.M { }, } } - filter["$and"] = append(filter["$and"].([]bson.M), groupIdListFilter) + if groupIdListFilter != nil { + filter["$and"] = append(filter["$and"].([]bson.M), groupIdListFilter) + } } // [Query-29] dnai-list if queryParameters["dnai-list"] != nil { - var dnaiFilter bson.M dnaiList := queryParameters["dnai-list"][0] dnaiListSplit := strings.Split(dnaiList, ",") var dnaiListBsonArray bson.A @@ -1071,7 +1087,7 @@ func buildFilter(queryParameters url.Values) bson.M { dnaiListBsonArray = append(dnaiListBsonArray, v) } if targetNfType == "UPF" { - dnaiFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "upfInfo.sNssaiUpfInfoList": bson.M{ "$elemMatch": bson.M{ "dnnUpfInfoList": bson.M{ @@ -1083,26 +1099,22 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), dnaiFilter) } // [Query-30] upf-iwk-eps-ind if queryParameters["upf-iwk-eps-ind"] != nil { - var upfIwkEpsIndFilter bson.M // upfIwkEpsInd := queryParameters["upf-iwk-eps-ind"][0] if targetNfType == "UPF" { - upfIwkEpsIndFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "upfInfo.iwkEpsInd": true, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), upfIwkEpsIndFilter) } // [Query-31] chf-supported-plmn if queryParameters["chf-supported-plmn"] != nil { - var chfSupportedPlmnFilter bson.M chfSupportedPlmn := queryParameters["chf-supported-plmn"][0] chfSupportedPlmnStruct := &models.PlmnId{} err := json.Unmarshal([]byte(chfSupportedPlmn), chfSupportedPlmnStruct) @@ -1113,7 +1125,7 @@ func buildFilter(queryParameters url.Values) bson.M { encodedchfSupportedPlmn := chfSupportedPlmnStruct.Mcc + chfSupportedPlmnStruct.Mnc if targetNfType == "CHF" { - chfSupportedPlmnFilter = bson.M{ + filter["$and"] = append(filter["$and"].([]bson.M), bson.M{ "$or": []bson.M{ { "chfInfo.plmnRangeList": bson.M{ @@ -1133,9 +1145,8 @@ func buildFilter(queryParameters url.Values) bson.M { }, }, }, - } + }) } - filter["$and"] = append(filter["$and"].([]bson.M), chfSupportedPlmnFilter) } // [Query-32] preferred-locality @@ -1188,10 +1199,13 @@ func buildFilter(queryParameters url.Values) bson.M { if err != nil { logger.DiscLog.Warnln("Unmarshal Error in complexQuery: ", err) } - complexQueryFilter := complexQueryFilter(complexQueryStruct) + complexQueryFilter, err := complexQueryFilter(complexQueryStruct) + if err != nil { + return nil, err + } filter["$and"] = append(filter["$and"].([]bson.M), complexQueryFilter) } - return filter + return filter, nil } const ( @@ -1204,7 +1218,7 @@ type AtomElem struct { negative bool } -func complexQueryFilter(complexQueryParameter *models.ComplexQuery) bson.M { +func complexQueryFilter(complexQueryParameter *models.ComplexQuery) (bson.M, error) { complexQueryType := "" if complexQueryParameter.CnfUnits != nil { complexQueryType = COMPLEX_QUERY_TYPE_CNF @@ -1230,7 +1244,10 @@ func complexQueryFilter(complexQueryParameter *models.ComplexQuery) bson.M { value := string(valueJson) queryParameters[atom.Attr] = &AtomElem{value: value, negative: atom.Negative} } - cnfUnitFilter = complexQueryFilterSubprocess(queryParameters, complexQueryType) + cnfUnitFilter, err := complexQueryFilterSubprocess(queryParameters, complexQueryType) + if err != nil { + return nil, err + } filter["$and"] = append(filter["$and"].([]bson.M), cnfUnitFilter) } @@ -1239,10 +1256,10 @@ func complexQueryFilter(complexQueryParameter *models.ComplexQuery) bson.M { "$or": []bson.M{}, } } - return filter + return filter, nil } -func complexQueryFilterSubprocess(queryParameters map[string]*AtomElem, complexQueryType string) bson.M { +func complexQueryFilterSubprocess(queryParameters map[string]*AtomElem, complexQueryType string) (bson.M, error) { var filter bson.M var logicalOperator string @@ -1576,6 +1593,9 @@ func complexQueryFilterSubprocess(queryParameters map[string]*AtomElem, complexQ var taiFilter bson.M tai := queryParameters["tai"].value taiSplit := strings.Split(tai, ",") + if len(taiSplit) < 2 { + return nil, fmt.Errorf("invalid tai format: insufficient comma-separated values") + } tempTai := taiSplit[0] + "," + taiSplit[1] taiStruct := &models.Tai{} @@ -2274,5 +2294,5 @@ func complexQueryFilterSubprocess(queryParameters map[string]*AtomElem, complexQ filter[logicalOperator] = append(filter[logicalOperator].([]bson.M), supportedFeaturesFilter) } - return filter + return filter, nil }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-7c47-xr7q-p6hgghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-33062ghsaADVISORY
- github.com/free5gc/free5gc/issues/777ghsax_refsource_MISCWEB
- github.com/free5gc/free5gc/security/advisories/GHSA-7c47-xr7q-p6hgghsax_refsource_CONFIRMWEB
- github.com/free5gc/nrf/commit/dac77d8f8f2e0f041c5634fb3c685dcb9734b872ghsax_refsource_MISCWEB
- github.com/free5gc/nrf/pull/80ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.