Skip to content

NULL pointer dereference in gnrc_ipv6_ext_frag_reass

Low
Teufelchen1 published GHSA-v8gx-q9m6-5xm9 Dec 17, 2025

Package

RIOT-OS (RIOT-OS)

Affected versions

<=2025.07

Patched versions

>=2025.10

Description

Reported and written by Nils Bernsdorf. Slightly edited by @Teufelchen1 on GitHub import.

Summary

A vulnerability was discovered in the IPv6 fragmentation reassembly implementation of RIOT OS v2025.07.
When receiving an fragmented IPv6 packet with fragment offset 0 and an empty payload, the payload pointer is set to NULL.
However, the implementation still tries to copy the payload into the reassembly buffer, resulting in a NULL pointer dereference which crashes the OS (DoS).
To trigger the vulnerability, the gnrc_ipv6_ext_frag module must be enabled and the attacker must be able to send arbitrary IPv6 packets to the victim.

Description

A vulnerability was found in the gnrc_ipv6_ext_frag_reass function (source).
To trigger the vulnerability, an attacker must send an IPv6 packet with the payload length field in the IPv6 header set to 8 and a fragmentation header.
Since the fragmentation header is exactly 8 bytes long, this means that the packet ends right after the header.
The implementation will try to remove this header using gnrc_pktbuf_mark to obtain the payload (source).
However, in the case where the payload is empty, gnrc_pktbuf_mark not only sets the size of the packet to 0, but also the packet's data pointer to NULL (source).
The implementation of gnrc_ipv6_ext_frag_reass does not check if this is the case and might still dereference the data pointer while copying the payload into the reassembly buffer.
This can be used to crash the OS (DoS).

We have verified that this is the case for the following scenario:
Assume the attacker sends two IPv6 packets that each have a fragmentation header that has the offset field set to 0.
For the first packet, the attacker additionally sets the payload length field of the IPv6 header to 8 (so pkt->data is set to NULL as explained above).
Since it is the first packet, the implementation of gnrc_ipv6_ext_frag_reass detects that no reassembly buffer has been allocated yet (source).
Therefore, the invalid packet with pkt->data=NULL is used as the reassembly buffer here.

Once a second packet with fragment offset 0 is received (but this time with a valid payload), the implementation will try to copy it into the reassembly buffer (source).
This is implemented using a memcpy to rbuf->pkt->data (which is still NULL), so the memcpy will crash.

Impact

This vulnerability can only be used for Denial of Service, as it crashes the firmware immediately.
To trigger it, the gnrc_ipv6_ext_frag module must be enabled in the configuration and the attacker must be able to send arbitrary IPv6 packets to the victim.

Proposed Fix

To fix this vulnerability, we propose to reject any fragmented packet that does not have a payload (and therefore has its pkt->data pointer set to NULL) as early as possible.
This can be done by adding a pkt->size == 0 check right after the call to gnrc_pktbuf_mark which strips the fragmentation header (source).
Note that performing this check early on also fixes other potential NULL pointer dereferences such as in the memcpy for fragments with offset>0 (source).

diff --git a/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c b/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c
index c57407729e..0b77eeee87 100644
--- a/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c
+++ b/sys/net/gnrc/network_layer/ipv6/ext/frag/gnrc_ipv6_ext_frag.c
@@ -423,6 +423,10 @@ gnrc_pktsnip_t *gnrc_ipv6_ext_frag_reass(gnrc_pktsnip_t *pkt)
         DEBUG("ipv6_ext_frag: unable to mark fragmentation header\n");
         goto error_release;
     }
+    else if (pkt->size == 0) {
+        DEBUG("ipv6_ext_frag: fragment empty after removing header\n");
+        goto error_release;
+    }
     fh = fh_snip->data;
     /* search IPv6 header */
     ipv6_snip = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);

Reproducer

reproducer_1.zip

I have provided an example application and an exploit script to reproduce this bug in reproducer/.
The example application only enables the gnrc_ipv6_ext_frag module and can be built using the following command:

cd reproducer/
make RIOTBASE=<path_to_riot_repo> BUILD_IN_DOCKER=1

To start the application, use the following commands:

# create a tap interface
sudo ip tuntap add tap0 mode tap user ${USER}
sudo ip link set tap0 up

# start the compiled binary
./bin/native64/reproducer.elf -w tap0

You can then run the exploit.py script to send the required packets over the tap interface.
As a first command line option, this script requires the IPv6 address which was printed by the reproducer.
Note: You might have to execute the script with sudo -E as sending over a tap interface requires elevated privileges.
After the exploit script exits, the reproducer should crash with a Segmentation fault.

For reference, here are the two packets sent by the exploit:

###[ IPv6 ]###
  version   = 6
  tc        = 0
  fl        = 0
  plen      = 8
  nh        = Fragment Header
  hlim      = 64
  src       = fe80::140a:4eff:feca:b6a7
  dst       = ScopedIP('fe80::140a:4eff:feca:b6a8', scope='tap0')
###[ IPv6 Extension Header - Fragmentation header ]###
     nh        = Hop-by-Hop Option Header
     res1      = 0
     offset    = 0
     res2      = 0
     m         = 1
     id        = 43981


###[ IPv6 ]###
  version   = 6
  tc        = 0
  fl        = 0
  plen      = 24
  nh        = Fragment Header
  hlim      = 64
  src       = fe80::140a:4eff:feca:b6a7
  dst       = ScopedIP('fe80::140a:4eff:feca:b6a8', scope='tap0')
###[ IPv6 Extension Header - Fragmentation header ]###
     nh        = Hop-by-Hop Option Header
     res1      = 0
     offset    = 0
     res2      = 0
     m         = 1
     id        = 43981
###[ Raw ]###
        load      = b'AAAAAAAAAAAAAAAA'

Severity

Low

CVE ID

CVE-2025-66646

Weaknesses

NULL Pointer Dereference

The product dereferences a pointer that it expects to be valid but is NULL. Learn more on MITRE.

Credits