Message ID | 20240403122917.29741-1-hsimeliere.opensource@witekio.com |
---|---|
State | Accepted, archived |
Commit | d1f74ec0419dd13a23549cfdc228b91602bfb065 |
Delegated to: | Steve Sakoman |
Headers | show |
Series | [dunfell] shadow: fix CVE-2023-4641 | expand |
On Wed, Apr 3, 2024 at 2:29 AM Hugo Simeliere via lists.openembedded.org <hsimeliere.opensource=witekio.com@lists.openembedded.org> wrote: > > From: Hugo SIMELIERE <hsimeliere.opensource@witekio.com> > > Upstream-Status: Backport [https://github.com/shadow-maint/shadow/commit/65c88a43a23c2391dcc90c0abda3e839e9c57904] > > Signed-off-by: Hugo SIMELIERE <hsimeliere.opensource@witekio.com> > --- > .../shadow/files/CVE-2023-4641.patch | 145 ++++++++++++++++++ > meta/recipes-extended/shadow/shadow.inc | 1 + > 2 files changed, 146 insertions(+) > create mode 100644 meta/recipes-extended/shadow/files/CVE-2023-4641.patch > > diff --git a/meta/recipes-extended/shadow/files/CVE-2023-4641.patch b/meta/recipes-extended/shadow/files/CVE-2023-4641.patch > new file mode 100644 > index 0000000000..709813ab31 > --- /dev/null > +++ b/meta/recipes-extended/shadow/files/CVE-2023-4641.patch > @@ -0,0 +1,145 @@ > +From 51731b01fd9a608397da22b7b9164e4996f3d4c6 Mon Sep 17 00:00:00 2001 > +From: Alejandro Colomar <alx@kernel.org> > +Date: Sat, 10 Jun 2023 16:20:05 +0200 > +Subject: [PATCH] gpasswd(1): Fix password leak > + > +CVE: CVE-2023-4641 > +Upstream-Status: Backport [https://github.com/shadow-maint/shadow/commit/65c88a43a23c2391dcc90c0abda3e839e9c57904] Missing Signed-off-by in the patch file, please submit a V2 Thanks, Steve > + > +How to trigger this password leak? > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + > +When gpasswd(1) asks for the new password, it asks twice (as is usual > +for confirming the new password). Each of those 2 password prompts > +uses agetpass() to get the password. If the second agetpass() fails, > +the first password, which has been copied into the 'static' buffer > +'pass' via STRFCPY(), wasn't being zeroed. > + > +agetpass() is defined in <./libmisc/agetpass.c> (around line 91), and > +can fail for any of the following reasons: > + > +- malloc(3) or readpassphrase(3) failure. > + > + These are going to be difficult to trigger. Maybe getting the system > + to the limits of memory utilization at that exact point, so that the > + next malloc(3) gets ENOMEM, and possibly even the OOM is triggered. > + About readpassphrase(3), ENFILE and EINTR seem the only plausible > + ones, and EINTR probably requires privilege or being the same user; > + but I wouldn't discard ENFILE so easily, if a process starts opening > + files. > + > +- The password is longer than PASS_MAX. > + > + The is plausible with physical access. However, at that point, a > + keylogger will be a much simpler attack. > + > +And, the attacker must be able to know when the second password is being > +introduced, which is not going to be easy. > + > +How to read the password after the leak? > +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > + > +Provoking the leak yourself at the right point by entering a very long > +password is easy, and inspecting the process stack at that point should > +be doable. Try to find some consistent patterns. > + > +Then, search for those patterns in free memory, right after the victim > +leaks their password. > + > +Once you get the leak, a program should read all the free memory > +searching for patterns that gpasswd(1) leaves nearby the leaked > +password. > + > +On 6/10/23 03:14, Seth Arnold wrote: > +> An attacker process wouldn't be able to use malloc(3) for this task. > +> There's a handful of tools available for userspace to allocate memory: > +> > +> - brk / sbrk > +> - mmap MAP_ANONYMOUS > +> - mmap /dev/zero > +> - mmap some other file > +> - shm_open > +> - shmget > +> > +> Most of these return only pages of zeros to a process. Using mmap of an > +> existing file, you can get some of the contents of the file demand-loaded > +> into the memory space on the first use. > +> > +> The MAP_UNINITIALIZED flag only works if the kernel was compiled with > +> CONFIG_MMAP_ALLOW_UNINITIALIZED. This is rare. > +> > +> malloc(3) doesn't zero memory, to our collective frustration, but all the > +> garbage in the allocations is from previous allocations in the current > +> process. It isn't leftover from other processes. > +> > +> The avenues available for reading the memory: > +> - /dev/mem and /dev/kmem (requires root, not available with Secure Boot) > +> - /proc/pid/mem (requires ptrace privileges, mediated by YAMA) > +> - ptrace (requires ptrace privileges, mediated by YAMA) > +> - causing memory to be swapped to disk, and then inspecting the swap > +> > +> These all require a certain amount of privileges. > + > +How to fix it? > +~~~~~~~~~~~~~~ > + > +memzero(), which internally calls explicit_bzero(3), or whatever > +alternative the system provides with a slightly different name, will > +make sure that the buffer is zeroed in memory, and optimizations are not > +allowed to impede this zeroing. > + > +This is not really 100% effective, since compilers may place copies of > +the string somewhere hidden in the stack. Those copies won't get zeroed > +by explicit_bzero(3). However, that's arguably a compiler bug, since > +compilers should make everything possible to avoid optimizing strings > +that are later passed to explicit_bzero(3). But we all know that > +sometimes it's impossible to have perfect knowledge in the compiler, so > +this is plausible. Nevertheless, there's nothing we can do against such > +issues, except minimizing the time such passwords are stored in plain > +text. > + > +Security concerns > +~~~~~~~~~~~~~~~~~ > + > +We believe this isn't easy to exploit. Nevertheless, and since the fix > +is trivial, this fix should probably be applied soon, and backported to > +all supported distributions, to prevent someone else having more > +imagination than us to find a way. > + > +Affected versions > +~~~~~~~~~~~~~~~~~ > + > +All. Bug introduced in shadow 19990709. That's the second commit in > +the git history. > + > +Fixes: 45c6603cc86c ("[svn-upgrade] Integrating new upstream version, shadow (19990709)") > +Reported-by: Alejandro Colomar <alx@kernel.org> > +Cc: Serge Hallyn <serge@hallyn.com> > +Cc: Iker Pedrosa <ipedrosa@redhat.com> > +Cc: Seth Arnold <seth.arnold@canonical.com> > +Cc: Christian Brauner <christian@brauner.io> > +Cc: Balint Reczey <rbalint@debian.org> > +Cc: Sam James <sam@gentoo.org> > +Cc: David Runge <dvzrv@archlinux.org> > +Cc: Andreas Jaeger <aj@suse.de> > +Cc: <~hallyn/shadow@lists.sr.ht> > +Signed-off-by: Alejandro Colomar <alx@kernel.org> > +--- > + src/gpasswd.c | 1 + > + 1 file changed, 1 insertion(+) > + > +diff --git a/src/gpasswd.c b/src/gpasswd.c > +index 4d75af96..a698b32a 100644 > +--- a/src/gpasswd.c > ++++ b/src/gpasswd.c > +@@ -918,6 +918,7 @@ static void change_passwd (struct group *gr) > + strzero (cp); > + cp = getpass (_("Re-enter new password: ")); > + if (NULL == cp) { > ++ memzero (pass, sizeof pass); > + exit (1); > + } > + > +-- > +2.42.0 > + > diff --git a/meta/recipes-extended/shadow/shadow.inc b/meta/recipes-extended/shadow/shadow.inc > index 2ecab5073d..c16292c38a 100644 > --- a/meta/recipes-extended/shadow/shadow.inc > +++ b/meta/recipes-extended/shadow/shadow.inc > @@ -16,6 +16,7 @@ SRC_URI = "https://github.com/shadow-maint/shadow/releases/download/${PV}/${BP}. > file://shadow-relaxed-usernames.patch \ > file://CVE-2023-29383.patch \ > file://0001-Overhaul-valid_field.patch \ > + file://CVE-2023-4641.patch \ > " > > SRC_URI_append_class-target = " \ > -- > 2.25.1 > > > -=-=-=-=-=-=-=-=-=-=-=- > Links: You receive all messages sent to this group. > View/Reply Online (#197916): https://lists.openembedded.org/g/openembedded-core/message/197916 > Mute This Topic: https://lists.openembedded.org/mt/105307284/3620601 > Group Owner: openembedded-core+owner@lists.openembedded.org > Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com] > -=-=-=-=-=-=-=-=-=-=-=- >
diff --git a/meta/recipes-extended/shadow/files/CVE-2023-4641.patch b/meta/recipes-extended/shadow/files/CVE-2023-4641.patch new file mode 100644 index 0000000000..709813ab31 --- /dev/null +++ b/meta/recipes-extended/shadow/files/CVE-2023-4641.patch @@ -0,0 +1,145 @@ +From 51731b01fd9a608397da22b7b9164e4996f3d4c6 Mon Sep 17 00:00:00 2001 +From: Alejandro Colomar <alx@kernel.org> +Date: Sat, 10 Jun 2023 16:20:05 +0200 +Subject: [PATCH] gpasswd(1): Fix password leak + +CVE: CVE-2023-4641 +Upstream-Status: Backport [https://github.com/shadow-maint/shadow/commit/65c88a43a23c2391dcc90c0abda3e839e9c57904] + +How to trigger this password leak? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When gpasswd(1) asks for the new password, it asks twice (as is usual +for confirming the new password). Each of those 2 password prompts +uses agetpass() to get the password. If the second agetpass() fails, +the first password, which has been copied into the 'static' buffer +'pass' via STRFCPY(), wasn't being zeroed. + +agetpass() is defined in <./libmisc/agetpass.c> (around line 91), and +can fail for any of the following reasons: + +- malloc(3) or readpassphrase(3) failure. + + These are going to be difficult to trigger. Maybe getting the system + to the limits of memory utilization at that exact point, so that the + next malloc(3) gets ENOMEM, and possibly even the OOM is triggered. + About readpassphrase(3), ENFILE and EINTR seem the only plausible + ones, and EINTR probably requires privilege or being the same user; + but I wouldn't discard ENFILE so easily, if a process starts opening + files. + +- The password is longer than PASS_MAX. + + The is plausible with physical access. However, at that point, a + keylogger will be a much simpler attack. + +And, the attacker must be able to know when the second password is being +introduced, which is not going to be easy. + +How to read the password after the leak? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Provoking the leak yourself at the right point by entering a very long +password is easy, and inspecting the process stack at that point should +be doable. Try to find some consistent patterns. + +Then, search for those patterns in free memory, right after the victim +leaks their password. + +Once you get the leak, a program should read all the free memory +searching for patterns that gpasswd(1) leaves nearby the leaked +password. + +On 6/10/23 03:14, Seth Arnold wrote: +> An attacker process wouldn't be able to use malloc(3) for this task. +> There's a handful of tools available for userspace to allocate memory: +> +> - brk / sbrk +> - mmap MAP_ANONYMOUS +> - mmap /dev/zero +> - mmap some other file +> - shm_open +> - shmget +> +> Most of these return only pages of zeros to a process. Using mmap of an +> existing file, you can get some of the contents of the file demand-loaded +> into the memory space on the first use. +> +> The MAP_UNINITIALIZED flag only works if the kernel was compiled with +> CONFIG_MMAP_ALLOW_UNINITIALIZED. This is rare. +> +> malloc(3) doesn't zero memory, to our collective frustration, but all the +> garbage in the allocations is from previous allocations in the current +> process. It isn't leftover from other processes. +> +> The avenues available for reading the memory: +> - /dev/mem and /dev/kmem (requires root, not available with Secure Boot) +> - /proc/pid/mem (requires ptrace privileges, mediated by YAMA) +> - ptrace (requires ptrace privileges, mediated by YAMA) +> - causing memory to be swapped to disk, and then inspecting the swap +> +> These all require a certain amount of privileges. + +How to fix it? +~~~~~~~~~~~~~~ + +memzero(), which internally calls explicit_bzero(3), or whatever +alternative the system provides with a slightly different name, will +make sure that the buffer is zeroed in memory, and optimizations are not +allowed to impede this zeroing. + +This is not really 100% effective, since compilers may place copies of +the string somewhere hidden in the stack. Those copies won't get zeroed +by explicit_bzero(3). However, that's arguably a compiler bug, since +compilers should make everything possible to avoid optimizing strings +that are later passed to explicit_bzero(3). But we all know that +sometimes it's impossible to have perfect knowledge in the compiler, so +this is plausible. Nevertheless, there's nothing we can do against such +issues, except minimizing the time such passwords are stored in plain +text. + +Security concerns +~~~~~~~~~~~~~~~~~ + +We believe this isn't easy to exploit. Nevertheless, and since the fix +is trivial, this fix should probably be applied soon, and backported to +all supported distributions, to prevent someone else having more +imagination than us to find a way. + +Affected versions +~~~~~~~~~~~~~~~~~ + +All. Bug introduced in shadow 19990709. That's the second commit in +the git history. + +Fixes: 45c6603cc86c ("[svn-upgrade] Integrating new upstream version, shadow (19990709)") +Reported-by: Alejandro Colomar <alx@kernel.org> +Cc: Serge Hallyn <serge@hallyn.com> +Cc: Iker Pedrosa <ipedrosa@redhat.com> +Cc: Seth Arnold <seth.arnold@canonical.com> +Cc: Christian Brauner <christian@brauner.io> +Cc: Balint Reczey <rbalint@debian.org> +Cc: Sam James <sam@gentoo.org> +Cc: David Runge <dvzrv@archlinux.org> +Cc: Andreas Jaeger <aj@suse.de> +Cc: <~hallyn/shadow@lists.sr.ht> +Signed-off-by: Alejandro Colomar <alx@kernel.org> +--- + src/gpasswd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/gpasswd.c b/src/gpasswd.c +index 4d75af96..a698b32a 100644 +--- a/src/gpasswd.c ++++ b/src/gpasswd.c +@@ -918,6 +918,7 @@ static void change_passwd (struct group *gr) + strzero (cp); + cp = getpass (_("Re-enter new password: ")); + if (NULL == cp) { ++ memzero (pass, sizeof pass); + exit (1); + } + +-- +2.42.0 + diff --git a/meta/recipes-extended/shadow/shadow.inc b/meta/recipes-extended/shadow/shadow.inc index 2ecab5073d..c16292c38a 100644 --- a/meta/recipes-extended/shadow/shadow.inc +++ b/meta/recipes-extended/shadow/shadow.inc @@ -16,6 +16,7 @@ SRC_URI = "https://github.com/shadow-maint/shadow/releases/download/${PV}/${BP}. file://shadow-relaxed-usernames.patch \ file://CVE-2023-29383.patch \ file://0001-Overhaul-valid_field.patch \ + file://CVE-2023-4641.patch \ " SRC_URI_append_class-target = " \