A missing check for self-routing indicated by IPv6 routing headers may lead to an infinite recursion, which results in a crash and potential RCE.
Proposed Security Advisory Text:
# Impact
While processing IPv6 packets, the IPv6 stack parses different IPv6 Extension headers, including Routing Headers. After its processing of the routing header-indicated destination, uip_process (os/net/ipv6/uip6.c) forwards the IPv6 packet by calling tcpip_ipv6_output.
However, before calling the output function, the logic does not verify that the destination indicated in the routing header is in fact an outside IPv6 address. A crafted IPv6 packet thus leads to deeply nested recursive calls, which leads to a stack overflow. This results in the corruption of OS-internal data structures.
When Receiving an input packet in uip_input, IPv6 logic allows routing a packet forward according to routing headers:
This leaves the uip_len set, which packet_input
uses as a signal to forward the packet to the output loop:
- https://github.com/contiki-ng/contiki-ng/blob/release/v4.8/os/net/ipv6/tcpip.c#L185
->
tcpip_ipv6_output
gets called
tcpip_ipv6_output
checks whether the packet is directed towards an own address, and in that case re-introduces the packet as input:
The condition of forwarding a self-directed packet is checked while processing the input in one location, but not in another:
- Check performed: https://github.com/contiki-ng/contiki-ng/blob/release/v4.8/os/net/ipv6/uip6.c#L1240
- Check not performed (routing headers): https://github.com/contiki-ng/contiki-ng/blob/release/v4.8/os/net/ipv6/uip6.c#L1358
If a routing header indicates a forward to the own address, then an infinite recursion between packet_input
and tcpip_ipv6_output
occurs, and the stack overflows due to recursion.
packet_input
callingtcpip_ipv6_output
: https://github.com/contiki-ng/contiki-ng/blob/release/v4.8/os/net/ipv6/uip6.c#L1363tcpip_ipv6_output
callingpacket_input
: https://github.com/contiki-ng/contiki-ng/blob/release/v4.8/os/net/ipv6/tcpip.c#L669
NOTE: The ipv6 layer may make assumptions about some guarantees of the routing layer. It calls:
if(NETSTACK_ROUTING.ext_header_srh_update()) {
...
Maybe it is assumed that when routing errors, then the destination address will be unset or similar sanitization is done in that layer. However, I am currently not aware of this type of implementation (and it did not catch the condition during our fuzzing).
Using a crafted IPv6 packet and attacker is able to cause an infinite recursion in the IPv6 layer. Due to the way embedded systems organize memory (the stack grows towards the heap and the data section), this may cause the corruption of heap objects and global variables. As a result, an attacker may cause the firmware to crash in an uncontrolled manner (denial of service) or control this corruption in a was to hijack firmware control flow (RCE).