By Nicolas Bidron, and Nicolas Guigo.
U-boot is a popular boot loader for embedded systems with implementations for a large number of architectures and prominent in most Linux based embedded systems such as ChromeOS and Android Devices.
Two vulnerabilities were uncovered in the IP Defragmentation algorithm implemented in U-Boot, with the associated technical advisories below:
Proof of concept code will be made available once the fixes have been published.
Project | U-Boot |
Project URL | https://github.com/u-boot/u-boot |
Versions affected | all versions up to commit TBD |
Systems affected | All systems defining CONFIG_IP_DEFRAG |
CVE identifier | CVE-2022-30790 |
Advisory URL | TBD |
Risk | Critical 9.6 (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) |
Authors | Nicolas Guigo, Nicolas Bidron |
U-boot is a popular boot loader for embedded systems with implementations for a large number of architectures and prominent in most linux based embedded systems.
In u-boot/net/net.c
the __net_defragment
function line 900 through 1018.
The U-Boot implementation of RFC815 IP DATAGRAM REASSEMBLY ALGORITHMS is susceptible to a Hole Descriptor overwrite attack which ultimately leads to an arbitrary write primitive.
In compiled versions of U-Boot that define CONFIG_IP_DEFRAG, a value of ip->ip_len
(IP packet header’s total Length) higher than IP_HDR_SIZE
and strictly lower than IP_HDR_SIZE+8
leads to a value for len
comprised between 0
and 7
. This ultimately results in a truncated division by 8
resulting in a value of 0
, forcing the hole metadata and fragment to point to the same location. The subsequent memcpy then overwrites the hole metadata with the fragment data. Through a second fragment, this attacker-controlled metadata can be exploited to perform a controlled write to an arbitrary offset.
This bug is only exploitable from the local network as it requires crafting a malformed packet which would most likely be dropped during routing. However, this it can be effectively leveraged to root linux based embedded devices locally.
static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
{
static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN);
static u16 first_hole, total_len;
struct hole *payload, *thisfrag, *h, *newh;
struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff;
uchar *indata = (uchar *)ip;
int offset8, start, len, done = 0;
u16 ip_off = ntohs(ip->ip_off);
/* payload starts after IP header, this fragment is in there */
payload = (struct hole *)(pkt_buff + IP_HDR_SIZE);
offset8 = (ip_off & IP_OFFS);
thisfrag = payload + offset8;
start = offset8 * 8;
len = ntohs(ip->ip_len) - IP_HDR_SIZE;
The last line of the previous excerpt from u-boot/net/net.c
shows how the attacker can control the value of len
to be strictly lower than 8
by issuing a packet with ip_len
between 21
and 27
(IP_HDR_SIZE
has a value of 20
).
Also note that offset8
here is 0
which leads to thisfrag = payload
.
} else if (h >= thisfrag) {
/* overlaps with initial part of the hole: move this hole */
newh = thisfrag + (len / 8);
*newh = *h;
h = newh;
if (h->next_hole)
payload[h->next_hole].prev_hole = (h - payload);
if (h->prev_hole)
payload[h->prev_hole].next_hole = (h - payload);
else
first_hole = (h - payload);
} else {
Later in the same function, execution reaches the above code path. Here, len / 8
evaluates to 0
leading to newh = thisfrag
. Also note that first_hole
here is 0
since h
and payload
point to the same location.
/* finally copy this fragment and possibly return whole packet */
memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len);
In the above excerpt the call to memcpy()
overwrites the hole metadata (since thisfrag
and h
both point to the same location) with arbitrary data from the fragmented IP packet data. With a len
value of 6
, last_byte
, next_hole
, and prev_hole
of the first_hole
all end- up attacker-controlled.
Finally the arbitrary write is triggered by sending a second fragment packet, whose offset and length only need to fit within the hole pointed to by the previously controlled metadata (next_hole
) set from the first packet.
This bug was disclosed to U-Boot support team and will be fixed in an upcoming patch. Update to the latest master branch version once the fix has been committed.
Project | U-Boot |
Project URL | https://github.com/u-boot/u-boot |
Versions affected | all versions up to commit TBD |
Systems affected | All systems defining CONFIG_IP_DEFRAG |
CVE identifier | CVE-2022-30552 |
Advisory URL | TBD |
Risk | High 7.1 (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:H) |
Authors | Nicolas Guigo, Nicolas Bidron |
U-boot is a popular boot loader for embedded systems with implementations for a large number of architectures and prominent in most linux based embedded systems.
u-boot/net/net.c
lines 915 and 1011.
The U-Boot implementation of RFC815 IP DATAGRAM REASSEMBLY ALGORITHMS is susceptible to a buffer overflow through a specially crafted fragmented IP Datagram with an invalid total length which causes a denial of service.
In compiled versions of U-Boot that define CONFIG_IP_DEFRAG, a value of ip->ip_len
(IP packet header’s total length) lower than IP_HDR_SIZE
leads to len
taking a negative value, which ultimately results in a buffer overflow during the subsequent call to memcpy()
that uses len
as its count
parameter.
This bug is only exploitable from the local network as it requires crafting a malformed packet with an ip_len
value lower than the minimum accepted total length (21 as defined in the IP specification document: RFC791) which would most likely be dropped during routing.
static struct ip_udp_hdr *__net_defragment(struct ip_udp_hdr *ip, int *lenp)
{
static uchar pkt_buff[IP_PKTSIZE] __aligned(PKTALIGN);
static u16 first_hole, total_len;
struct hole *payload, *thisfrag, *h, *newh;
struct ip_udp_hdr *localip = (struct ip_udp_hdr *)pkt_buff;
uchar *indata = (uchar *)ip;
int offset8, start, len, done = 0;
u16 ip_off = ntohs(ip->ip_off);
/* payload starts after IP header, this fragment is in there */
payload = (struct hole *)(pkt_buff + IP_HDR_SIZE);
offset8 = (ip_off & IP_OFFS);
thisfrag = payload + offset8;
start = offset8 * 8;
len = ntohs(ip->ip_len) - IP_HDR_SIZE;
The last line of the previous excerpt from u-boot/net/net.c
shows where the underflow to a negative len
value occurs if ip_len
is set to a value strictly lower than 20 (IP_HDR_SIZE
being 20). Also note that in the above excerpt the pkt_buff
buffer has a size of CONFIG_NET_MAXDEFRAG
which defaults to 16 KB but can range from 1KB to 64 KB depending on configurations.
/* finally copy this fragment and possibly return whole packet */
memcpy((uchar *)thisfrag, indata + IP_HDR_SIZE, len);
In the above excerpt the memcpy()
overflows the destination by attempting to make a copy of nearly 4 gigabytes in a buffer that’s designed to hold CONFIG_NET_MAXDEFRAG
bytes at most, which leads to a DoS.
This bug was disclosed to U-Boot support team and will be fixed in an upcoming patch. Update to the latest master branch version once the fix has been committed.
May 18th 2022: Initial e-mail from NCC to U-boot maintainers announcing two vulnerabilities were identified. U-Boot maintainers responded indicating that the disclosure process is to be handled publicly through U-Boot’s mailing list.
May 18th 2022: NCC posted a full writeup of the two vulnerabilities identified to U-Boot’s public mailing list.
May 25th 2022: a U-Boot maintainer indicated on the mailing list that they will implement a fix to the two findings.
May 26th 2022: a patch has been proposed by U-Boot maintainers to fix both CVEs through the mailing list.
May 31st 2022: U-boot maintainers and NCC Group agree to publishing the advisories in advance of patch deployment, given the public mailing-list-based discussion of the vulnerability and proposed fixes.
Jennifer Fernick, and Dave Goldsmith for their support through the disclosure process.
U-Boot’s maintainers.
Nicolas Guigo, and Nicolas Bidron