Bug 2236298 - dhcpcd-base: DoS: zero-length packet cause eventual lease expiration
Summary: dhcpcd-base: DoS: zero-length packet cause eventual lease expiration
Keywords:
Status: NEW
Alias: None
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
urgent
urgent
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL: https://bugs.debian.org/cgi-bin/bugre...
Whiteboard:
Depends On: 2236299 2236300 2262996
Blocks:
TreeView+ depends on / blocked
 
Reported: 2023-08-30 20:53 UTC by Chess Hazlett
Modified: 2024-02-07 11:48 UTC (History)
2 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Github NetworkConfiguration dhcpcd issues 179 0 None closed dhcpcd crashes every 5 days 2023-08-31 10:19:53 UTC
Github NetworkConfiguration dhcpcd issues 283 0 None open dhcpcd core dumps every 5 days 2024-02-07 11:41:53 UTC
Github NetworkConfiguration dhcpcd pull 295 0 None open Move dhcp(v4) packet size check earlier 2024-02-07 11:41:53 UTC

Description Chess Hazlett 2023-08-30 20:53:38 UTC
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1050805 
https://github.com/NetworkConfiguration/dhcpcd/issues/179
No CVE assigned yet.

Package: dhcpcd-base
Version: 9.4.1-22
Severity: critical
Tags: security
Justification: breaks unrelated software
X-Debbugs-Cc: Debian Security Team <team.org>

When the dhcpcd DHCPv4 client receives a zero-length UDP packet on port
68, the "network proxy" dhcpcd process exits with status 0.  dhcpcd then
stops all network activity:  It does not renew leases and eventually expires
the current lease (unless it has infinite duration) and removes the IP
address, leaving the system without networking.

This bug can be triggered remotely over the internet from any UDP port
and is critical on an internet-facing system that needs DHCP to get
an IP address, such as a gateway, a dedicated server or a VM.

This affects version 9.4.1-22 (stable) and 1:9.4.1-24~deb12u2
(stable proposed update) but not 1:10.0.2-4 (testing/unstable) as
upstream fixed it in 10.0.2:

Upstream Bug report: https://github.com/NetworkConfiguration/dhcpcd/issues/179
Upstream Fix: https://github.com/NetworkConfiguration/dhcpcd/commit/8b29c0ddf026c1c5647c3b8c6cfe21699c4056ae

This patch does not apply cleanly to 9.4.1 because the privsep
structure changed in 10.0.2.  It's likely that only the src/privsep.c
hunks about len == 0 and eloop_exit() needs to be backported, the other
changes are just here to avoid compiler warnings about unused
parameters.


-- System Information:
Debian Release: 12.1
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.1.0-11-amd64 (SMP w/8 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages dhcpcd-base depends on:
ii  adduser   3.134
ii  libc6     2.36-9+deb12u1
ii  libudev1  252.12-1~deb12u1

Versions of packages dhcpcd-base recommends:
pn  wpasupplicant  <none>

Versions of packages dhcpcd-base suggests:
ii  openresolv [resolvconf]  3.12.0-3

-- Configuration Files:
/etc/dhcpcd.conf changed [not included]

-- no debconf information

Comment 1 Chess Hazlett 2023-08-30 20:53:52 UTC
Created dhcpcd tracking bugs for this issue:

Affects: epel-all [bug 2236299]
Affects: fedora-all [bug 2236300]

Comment 2 Martin Osvald 🛹 2023-08-31 06:09:10 UTC
(In reply to Chess Hazlett from comment #1)
> Created dhcpcd tracking bugs for this issue:
> 
> Affects: epel-all [bug 2236299]
> Affects: fedora-all [bug 2236300]

This affects "only" F37 and EPEL7 - they are based on dhcpcd-9.5.2.

F38, F39, F40, and EPEL8 & EPEL9 are based on dhcpcd-10.0.2 which is fixed (manually checked the code to be sure).

The released versions for individual Fedoras and EPELs can be found here:

https://src.fedoraproject.org/rpms/dhcpcd

Comment 3 Martin Osvald 🛹 2023-08-31 07:06:52 UTC
This needs to be doublechecked but due to this commit included in dhcpcd-9.5.2:

https://github.com/NetworkConfiguration/dhcpcd/commit/828e858d3d7bbdb82bec7cd56616795426861783

F37 and EPEL7:

don't seem to be affected.

See output from gdb DHCPCD_PRIVSEP is turned on:

~~~
(gdb) b ps_init
Breakpoint 2 at 0x5570f7122e00: file /usr/src/debug/dhcpcd-9.5.2-2.fc37.x86_64/src/privsep.c, line 95.
(gdb) c
Continuing.

Breakpoint 2, ps_init (ctx=0x7fff478a23a0) at /usr/src/debug/dhcpcd-9.5.2-2.fc37.x86_64/src/privsep.c:95
95      {
(gdb) n
99              errno = 0;
(gdb) 
100             if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) {
(gdb) 
111             if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
(gdb) p *ctx->ps_user
$2 = {pw_name = 0x5570f7feecd0 "dhcpcd", pw_passwd = 0x5570f7feecd7 "x", pw_uid = 993, pw_gid = 992, pw_gecos = 0x5570f7feece1 "Minimalistic DHCP client", pw_dir = 0x5570f7feecfa "/var/lib/dhcpcd", 
  pw_shell = 0x5570f7feed0a "/usr/sbin/nologin"}
(gdb) n
119             ctx->options |= DHCPCD_PRIVSEP;
(gdb) 
120             return 0;
(gdb)
~~~

and look at the current code from F37 branch - line #920:

dhcpcd-9.5.2/src/privsep.c:
~~~
 899 ssize_t
 900 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
 901 {
 902         struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
 903         uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
 904         uint8_t databuf[64 * 1024];
 905         struct iovec iov[] = {
 906             { .iov_base = databuf, .iov_len = sizeof(databuf) }
 907         };
 908         struct msghdr msg = {
 909                 .msg_name = &ss, .msg_namelen = sizeof(ss),
 910                 .msg_control = controlbuf, .msg_controllen = sizeof(controlbuf),
 911                 .msg_iov = iov, .msg_iovlen = 1,
 912         };
 913                 
 914         ssize_t len = recvmsg(rfd, &msg, 0);
 915         
 916         if (len == -1)
 917                 logerr("%s: recvmsg", __func__);
 918         if (len == -1 || len == 0) {
 919                 if (ctx->options & DHCPCD_FORKED &&
 920                     !(ctx->options & DHCPCD_PRIVSEPROOT))
 921                         eloop_exit(ctx->eloop,
 922                             len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 923                 return len;
 924         }
 925 
 926         iov[0].iov_len = (size_t)len;
 927         len = ps_sendcmdmsg(wfd, cmd, &msg);
 928         if (len == -1) {
 929                 logerr("ps_sendcmdmsg"); 
 930                 if (ctx->options & DHCPCD_FORKED &&
 931                     !(ctx->options & DHCPCD_PRIVSEPROOT))
 932                         eloop_exit(ctx->eloop, EXIT_FAILURE);
 933         }       
 934         return len;
 935 }
~~~

From above the behavior is that if DHCPCD_PRIVSEPROOT is not set in ctx->options it will exit, but as it is set, it won't.

Comment 4 Martin Osvald 🛹 2023-08-31 07:25:52 UTC
Additional bits and pieces for DHCPCD_PRIVSEPROOT and DHCPCD_PRIVSEP relation:

~~~~
 653 static int
 654 ps_root_startcb(void *arg)
 655 {
 656         struct dhcpcd_ctx *ctx = arg;
 657 
 658         if (ctx->options & DHCPCD_MANAGER)
 659                 setproctitle("[privileged proxy]");
 660         else
 661                 setproctitle("[privileged proxy] %s%s%s",
 662                     ctx->ifv[0],
 663                     ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
 664                     ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
 665         ctx->ps_root_pid = getpid();
 666         ctx->options |= DHCPCD_PRIVSEPROOT; <<<---
...
 809 pid_t
 810 ps_root_start(struct dhcpcd_ctx *ctx)
 811 {
 812         int logfd[2], datafd[2];
 813         pid_t pid;
 814 
 815         if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, logfd) == -1)
 816                 return -1;
 817 #ifdef PRIVSEP_RIGHTS
 818         if (ps_rights_limit_fdpair(logfd) == -1)
 819                 return -1;
 820 #endif
 821 
 822         if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, datafd) == -1)
 823                 return -1;
 824         if (ps_setbuf_fdpair(datafd) == -1)
 825                 return -1;
 826 #ifdef PRIVSEP_RIGHTS
 827         if (ps_rights_limit_fdpair(datafd) == -1)
 828                 return -1;
 829 #endif
 830 
 831         pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
 832             ps_root_recvmsg, NULL, ctx,
 833             ps_root_startcb, ps_root_signalcb, 0);  <<<---
 ~~~

src/privsep.h:
~~~
 95 /* Handy macro to work out if in the privsep engine or not. */
 96 #define IN_PRIVSEP(ctx) \
 97         ((ctx)->options & DHCPCD_PRIVSEP)
~~~

src/dhcpcd.c:
~~~
 464 int
 465 ps_start(struct dhcpcd_ctx *ctx)
 466 {
 467         pid_t pid;
 468 
 469         TAILQ_INIT(&ctx->ps_processes);
 470 
 471         switch (pid = ps_root_start(ctx)) { <<<---

1808 int
1809 main(int argc, char **argv, char **envp)
1810 {
...
2403 #ifdef PRIVSEP
2404         if (IN_PRIVSEP(&ctx) && ps_start(&ctx) == -1) {  <<<---
2405                 logerr("ps_start");
2406                 goto exit_failure;
2407         }
2408         if (ctx.options & DHCPCD_FORKED)
2409                 goto run_loop;
2410 #endif
~~~

Comment 5 Petr Menšík 2023-09-14 13:27:04 UTC
Strange is no fix were imported into branch dhcpcd-9, even though a release from it were recently made.

Original Debian report had version 9.4.1, which has quite similar code in relevant places. I cannot find how they differ in relevant places to not be vulnerable. Unless I am missing something important.

Comment 6 Petr Menšík 2024-02-07 11:41:54 UTC
It seems bug #2262996 reported issue related to this one. There is still unfixed code path causing dhcpcd to crash.
Reported on upstream issue: https://github.com/NetworkConfiguration/dhcpcd/issues/283


Note You need to log in before you can comment on or make changes to this bug.