Out-of-Bounds write in systemd-networkd dhcpv6 option handling
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
systemd (Ubuntu) |
Fix Released
|
Medium
|
Unassigned |
Bug Description
systemd-networkd contains a DHCPv6 client which is written from scratch and can be spawned automatically on managed interfaces when IPv6 router advertisement are received:
"Note that DHCPv6 will by default be triggered by Router Advertisement, if that is enabled, regardless of this parameter. By enabling DHCPv6 support explicitly, the DHCPv6 client will be started regardless of the presence of routers on the link, or what flags the routers pass"
(https:/
The function dhcp6_option_
// https:/
int dhcp6_option_
uint16_t len;
uint8_t *ia_hdr;
size_t iaid_offset, ia_buflen, ia_addrlen = 0;
int r;
switch (ia->type) {
case SD_DHCP6_
len = DHCP6_OPTION_
case SD_DHCP6_
len = DHCP6_OPTION_
default:
}
A: if (*buflen < len)
ia_hdr = *buf;
ia_buflen = *buflen;
*buf += sizeof(
B: *buflen -= sizeof(
C: memcpy(*buf, (char*) ia + iaid_offset, len);
*buf += len;
D: *buflen -= len;
E: LIST_FOREACH(
r = option_
if (r < 0)
}
r = option_
if (r < 0)
return 0;
}
The function receives a pointer to the option buffer buf, it's remaining size buflen and the IA to be added to the buffer. While the check at (A) tries to ensure that the buffer has enough space left to store the IA option, it does not take the additional 4 bytes from the DHCP6Option header into account (B). Due to this the memcpy at (C) can go out-of-bound and *buflen can underflow in (D) giving an attacker a very powerful and largely controlled OOB heap write starting at (E).
The overflow can be triggered relatively easy by advertising a DHCPv6 server with a server-id >= 493 characters long. This will trigger the following code once the client tries to create a REQUEST message:
//https:/
case DHCP6_STATE_
case DHCP6_STATE_RENEW:
if (client->state == DHCP6_STATE_
A: r = dhcp6_option_
if (r < 0)
if (FLAGS_
B: r = dhcp6_option_
}
optlen starts with the value 512 and gets decreased to 15 (512 - 493 - 4) after the call in A. As 15 > DHCP6_OPTION_
# journalctl -u systemd-networkd
Oct 03 14:27:50 ubuntu18 systemd-
Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-
Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-
Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-
Oct 03 14:27:50 ubuntu18 systemd[1]: systemd-
Oct 03 14:27:50 ubuntu18 systemd[1]: Stopped Network Service.
Oct 03 14:27:50 ubuntu18 systemd[1]: Starting Network Service...
Oct 03 14:27:50 ubuntu18 systemd-
Oct 03 14:27:50 ubuntu18 systemd-
Oct 03 14:27:50 ubuntu18 systemd-
Oct 03 14:27:50 ubuntu18 systemd[1]: Started Network Service.
Oct 03 14:27:50 ubuntu18 systemd-
Oct 03 14:27:50 ubuntu18 systemd-
Oct 03 14:27:53 ubuntu18 systemd-
Oct 03 14:27:53 ubuntu18 systemd[1]: systemd-
Oct 03 14:27:53 ubuntu18 systemd[1]: systemd-
An interesting aspect of this bug is that systemd-networkd will restart automatically after a crash, which means that even a somewhat unstable exploit can work reliably.
Testing was done on a Ubuntu 18.04 server installation running systemd 237-3ubuntu10.3
This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available (whichever is earlier), the bug
report will become visible to the public.
Please credit Felix Wilhelm from the Google Security Team in all releases, patches and advisories related to this issue.
Let me know if you have any questions.
Best,
Felix
Related branches
- Alfonso Sanchez-Beato: Approve
- James Jesudason (community): Approve
- System Enablement Bot: Approve (continuous-integration)
-
Diff: 13 lines (+1/-1)1 file modifiedsrc/systemd/src/libsystemd-network/dhcp6-option.c (+1/-1)
CVE References
Changed in systemd (Ubuntu): | |
status: | New → Confirmed |
information type: | Private Security → Public Security |
Changed in systemd (Ubuntu): | |
importance: | Undecided → Medium |
Changed in systemd (Ubuntu): | |
status: | Confirmed → Fix Released |
Hello Felix, thanks for contacting us. Excellent discovery.
Have you contacted anyone else about this issue yet?
Have you allocated a CVE number for this yet?
My first thought to address this is to amend A for sizeof( DHCP6Option) ; -- does this feel like a sufficient or correct approach to addressing this issue?
Thanks