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'
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_fragmodule 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_reassfunction (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_markto obtain the payload (source).However, in the case where the payload is empty,
gnrc_pktbuf_marknot only sets the size of the packet to 0, but also the packet'sdatapointer to NULL (source).The implementation of
gnrc_ipv6_ext_frag_reassdoes not check if this is the case and might still dereference thedatapointer 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->datais set to NULL as explained above).Since it is the first packet, the implementation of
gnrc_ipv6_ext_frag_reassdetects that no reassembly buffer has been allocated yet (source).Therefore, the invalid packet with
pkt->data=NULLis 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
memcpytorbuf->pkt->data(which is still NULL), so thememcpywill 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_fragmodule 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->datapointer set to NULL) as early as possible.This can be done by adding a
pkt->size == 0check right after the call tognrc_pktbuf_markwhich strips the fragmentation header (source).Note that performing this check early on also fixes other potential NULL pointer dereferences such as in the
memcpyfor fragments withoffset>0(source).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_fragmodule and can be built using the following command:To start the application, use the following commands:
You can then run the
exploit.pyscript 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 -Eas 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: