CVE-2021-3156: Heap-Based Buffer Overflow in Sudo (Baron Samedit)

ID QUALYSBLOG:A341C9278C6DD389E0F263AE83CB5579
Type qualysblog
Reporter Animesh Jain
Modified 2021-01-26T18:09:09


Update Feb 3, 2021: It has been reported that macOS, AIX, and
Solaris are also vulnerable to CVE-2021-3156, and that others may also
still be vulnerable. Qualys has not independently verified the exploit.

Original Post: The Qualys Research Team has discovered a heap overflow vulnerability in sudo, a near-ubiquitous utility available on major Unix-like operating systems. Any unprivileged user can gain root privileges on a vulnerable host using a default sudo configuration by exploiting this vulnerability.

Sudo is a powerful utility that’s included in most if not all Unix- and Linux-based OSes. It allows users to run programs with the security privileges of another user. The vulnerability itself has been hiding in plain sight for nearly 10 years. It was introduced in July 2011 (commit 8255ed69) and affects all legacy versions from 1.8.2 to 1.8.31p2 and all stable versions from 1.9.0 to 1.9.5p1 in their default configuration.

Successful exploitation of this vulnerability allows any unprivileged user to gain root privileges on the vulnerable host. Qualys security researchers have been able to independently verify the vulnerability and develop multiple variants of exploit and obtain full root privileges on Ubuntu 20.04 (Sudo 1.8.31), Debian 10 (Sudo 1.8.27), and Fedora 33 (Sudo 1.9.2). Other operating systems and distributions are also likely to be exploitable.

As soon as the Qualys research team confirmed the vulnerability, Qualys engaged in responsible vulnerability disclosure and coordinated with sudo’s author and open source distributions to announce the vulnerability.

Disclosure Timeline

  • 2021-01-13: Advisory sent to Todd.Miller@sudo
  • 2021-01-19: Advisory and patches sent to distros@openwall
  • 2021-01-26: Coordinated Release Date (6:00 PM UTC)

Proof of Concept Video

Technical Details

If Sudo is executed to run a command in "shell" mode (shell -c command):

  • either through the -s option, which sets Sudo's MODE_SHELL flag; OR
  • through the -i option, which sets Sudo's MODE_SHELL and MODE_LOGIN_SHELL flags; then, at the beginning of Sudo's main(), parse_args() rewrites argv (lines 609-617), by concatenating all command-line arguments (lines 587-595) and by escaping all meta-characters with backslashes (lines 590-591):

    571 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { 572 char av, cmnd = NULL; 573 int ac = 1; ... 581 cmnd = dst = reallocarray(NULL, cmnd_size, 2); ... 587 for (av = argv; av != NULL; av++) { 588 for (src = av; src != '\0'; src++) { 589 / quote potential meta characters / 590 if (!isalnum((unsigned char)src) && src != '_' && src != '-' && src != '$') 591 dst++ = '\'; 592 dst++ = src; 593 } 594 dst++ = ' '; 595 } ... 600 ac += 2; / -c cmnd / ... 603 av = reallocarray(NULL, ac + 1, sizeof(char )); ... 609 av[0] = (char ); / plugin may override shell / 610 if (cmnd != NULL) { 611 av[1] = "-c"; 612 av[2] = cmnd; 613 } 614 av[ac] = NULL; 615
    616 argv = av; 617 argc = ac; 618 }

Later, in sudoers_policy_main(), set_cmnd() concatenates the command-line arguments into a heap-based buffer "user_args" (lines 864-871) and unescapes the meta-characters (lines 866-867), "for sudoers matching and logging purposes":

819     if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { 
852             for (size = 0, av = NewArgv + 1; *av; av++) 
853                 size += strlen(*av) + 1; 
854             if (size == 0 || (user_args = malloc(size)) == NULL) { 
857             } 
858             if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { 
864                 for (to = user_args, av = NewArgv + 1; (from = *av); av++) { 
865                     while (*from) { 
866                         if (from[0] == '\\' && !isspace((unsigned char)from[1])) 
867                             from++; 
868                         *to++ = *from++; 
869                     } 
870                     *to++ = ' '; 
871                 } 
884             } 
886     } 

Unfortunately, if a command-line argument ends with a single backslash character, then:

  • at line 866, "from[0]" is the backslash character, and "from[1]" is the argument's null terminator (i.e., not a space character);
  • at line 867, "from" is incremented and points to the null terminator;
  • at line 868, the null terminator is copied to the "user_args" buffer, and "from" is incremented again and points to the first character after the null terminator (i.e., out of the argument's bounds);
  • the "while" loop at lines 865-869 reads and copies out-of-bounds characters to the "user_args" buffer.

In other words, set_cmnd() is vulnerable to a heap-based buffer overflow, because the out-of-bounds characters that are copied to the "user_args" buffer were not included in its size (calculated at lines852-853).

In theory, however, no command-line argument can end with a single backslash character: if MODE_SHELL or MODE_LOGIN_SHELL is set (line 858, a necessary condition for reaching the vulnerable code), then MODE_SHELL is set (line 571) and parse_args() already escaped all meta-characters, including backslashes (i.e., it escaped every single backslash with a second backslash).

In practice, however, the vulnerable code in set_cmnd() and the escape code in parse_args() are surrounded by slightly different conditions:

819     if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { 
858             if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { 


571     if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { 

Our question is: can we set MODE_SHELL and either MODE_EDIT or MODE_CHECK (to reach the vulnerable code) but not the default MODE_RUN (to avoid the escape code)?

The answer, it seems, is no: if we set MODE_EDIT (-e option, line 361) or MODE_CHECK (-l option, lines 423 and 519), then parse_args() removes MODE_SHELL from the "valid_flags" (lines 363 and 424) and exits with an error if we specify an invalid flag such as MODE_SHELL (lines 532-533):

358                 case 'e': 
361                     mode = MODE_EDIT; 
362                     sudo_settings[ARG_SUDOEDIT].value = "true"; 
363                     valid_flags = MODE_NONINTERACTIVE; 
364                     break; 
416                 case 'l': 
423                     mode = MODE_LIST; 
424                     valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST; 
425                     break; 
518     if (argc > 0 && mode == MODE_LIST) 
519         mode = MODE_CHECK; 
532     if ((flags & valid_flags) != flags) 
533         usage(1); 

But we found a loophole: if we execute Sudo as "sudoedit" instead of "sudo", then parse_args() automatically sets MODE_EDIT (line 270) but does not reset "valid_flags", and the "valid_flags" include MODE_SHELL by default (lines 127 and 249):

249     int valid_flags = DEFAULT_VALID_FLAGS; 
267     proglen = strlen(progname); 
268     if (proglen > 4 && strcmp(progname + proglen - 4, "edit") == 0) { 
269         progname = "sudoedit"; 
270         mode = MODE_EDIT; 
271         sudo_settings[ARG_SUDOEDIT].value = "true"; 
272     } 

Consequently, if we execute "sudoedit -s", then we set both MODE_EDIT and MODE_SHELL (but not MODE_RUN), we avoid the escape code, reach the vulnerable code, and overflow the heap-based buffer "user_args" through a command-line argument that ends with a single backslash character:

sudoedit -s '\' `perl -e 'print "A" x 65536'` 
malloc(): corrupted top size 
Aborted (core dumped) 

From an attacker's point of view, this buffer overflow is ideal due to following reasons:

1) The attacker controls the size of the "user_args" buffer that can be overflowed (the size of our concatenated command-line arguments, at lines 852-854);

2) The attacker independently controls the size and contents of the overflow itself (our last command-line argument is conveniently followed by our first environment variables, which are not included in the size calculation at lines 852-853);

3) The attacker can even write null bytes to the buffer that was overflowed (every command-line argument or environment variable that ends with a single backslash writes a null byte to "user_args", at lines 866-868).

For example, on an amd64 Linux, the following command allocates a 24-byte "user_args" buffer (a 32-byte heap chunk) and overwrites the next chunk's size field with "A=a\0B=b\0" (0x00623d4200613d41), its fd field with "C=c\0D=d\0" (0x00643d4400633d43), and its bk field with "E=e\0F=f\0" (0x00663d4600653d45):

env -i 'AA=a\' 'B=b\' 'C=c\' 'D=d\' 'E=e\' 'F=f' sudoedit -s '1234567890123456789012\' 

  |        |        |12345678|90123456|789012.A|A=a.B=b.|C=c.D=d.|E=e.F=f.| 

              size  <---- user_args buffer ---->  size      fd       bk


Given the breadth of the attack surface for this vulnerability, Qualys recommends users apply patches for this vulnerability immediately.

Qualys customers can search the vulnerability knowledgebase for CVE-2021-3156 to identify all the QIDs and assets vulnerable for this vulnerability.

If you are not a customer, start your free Qualys VMDR trial to get full access to the QIDs (detections) for CVE-2021-3156, so you can identify your vulnerable assets.

Qualys Coverage

Qualys is releasing the QIDs in the table below as they become available starting with vulnsigs version VULNSIGS-2.5.90-4 and in Linux Cloud Agent manifest version lx_manifest-

QID* | Title | O/S | Advisory | **Version
352207 | Amazon Linux Security Advisory for sudo: ALAS2-2021-1590 (Baron Samedit)) | Amazon Linux | ALAS2-2021-1590 | VULNSIGS-2.5.94-2 /
352208 | Amazon Linux Security Advisory for sudo: ALAS-2021-1478 (Baron Samedit) | Amazon Linux | ALAS-2021-1478 | VULNSIGS-2.5.94-2 /
174570 | SUSE Enterprise Linux Security Update for sudo (SUSE-SU-2021:0226-1) (Baron Samedit) | SUSE Enterprise Linux | SUSE-SU-2021:0226-1 | VULNSIGS-2.5.94-2 /
174571 | SUSE Enterprise Linux Security Update for sudo (SUSE-SU-2021:0227-1) (Baron Samedit) | SUSE Enterprise Linux | SUSE-SU-2021:0227-1 | VULNSIGS-2.5.94-2 /
174572 | SUSE Enterprise Linux Security Update for sudo (SUSE-SU-2021:0225-1) (Baron Samedit) | SUSE Enterprise Linux | SUSE-SU-2021:0225-1 | VULNSIGS-2.5.94-2 /
178379 | Debian Security Update for sudo (DSA 4839-1) (Baron Samedit) | Debian | DSA 4839-1 | VULNSIGS-2.5.94-2 /
198231 | Ubuntu Security Notification for Sudo Vulnerabilities : USN-4705-1(Baron Samedit) | Ubuntu | USN-4705-1 | VULNSIGS-2.5.94-2 /
374915 | Gentoo Linux Sudo Multiple Vulnerabilities (GLSA 202101-33) (Baron Samedit) | Gentoo | GLSA 202101-33 | VULNSIGS-2.5.94-2 /
158992 | Oracle Enterprise Linux Security Update for sudo (ELSA-2021-0221)(Baron Samedit) | Oracle Enterprise Linux | ELSA-2021-0221 | VULNSIGS-2.5.94-4 /
158993 | Oracle Enterprise Linux Security Update for sudo (ELSA-2021-0218)(Baron Samedit) | Oracle Enterprise Linux | ELSA-2021-0218 | VULNSIGS-2.5.94-4 /
158994 | Oracle Enterprise Linux Security Update for sudo (ELSA-2021-9019)(Baron Samedit) | Oracle Enterprise Linux | ELSA-2021-9019 | VULNSIGS-2.5.94-4 /
178383 | Debian Security Update for sudo (DLA 2534-1) (Baron Samedit) | Debian | DLA 2534-1 | VULNSIGS-2.5.94-4 /
239026 | Red Hat Update for sudo (RHSA-2021:0218) (Baron Samedit) | Red Hat | RHSA-2021:0218 | VULNSIGS-2.5.94-4 /
239027 | Red Hat Update for sudo (RHSA-2021:0219) (Baron Samedit) | Red Hat | RHSA-2021:0219 | VULNSIGS-2.5.94-4 /
239028 | Red Hat Update for sudo (RHSA-2021:0220) (Baron Samedit) | Red Hat | RHSA-2021:0220 | VULNSIGS-2.5.94-4 /
239029 | Red Hat Update for sudo (RHSA-2021:0221) (Baron Samedit) | Red Hat | RHSA-2021:0221 | VULNSIGS-2.5.94-4 /
239030 | Red Hat Update for sudo (RHSA-2021:0222) (Baron Samedit) | Red Hat | RHSA-2021:0222 | VULNSIGS-2.5.94-4 /
239031 | Red Hat Update for sudo (RHSA-2021:0223) (Baron Samedit) | Red Hat | RHSA-2021:0223 | VULNSIGS-2.5.94-4 /
239032 | Red Hat Update for sudo (RHSA-2021:0227) (Baron Samedit) | Red Hat | RHSA-2021:0227 | VULNSIGS-2.5.94-4 /
257056 | CentOS Security Update for sudo Security Update (CESA-2021:0221) (Baron Samedit) | CentOS | CESA-2021:0221 | VULNSIGS-2.5.94-4 /
280866 | Fedora Security Update for sudo (FEDORA-2021-8840cbdccd) (Baron Samedit) | Fedora | FEDORA-2021-8840cbdccd | VULNSIGS-2.5.94-4 /
280868 | Fedora Security Update for sudo (FEDORA-2021-2cb63d912a) (Baron Samedit) | Fedora | FEDORA-2021-2cb63d912a | VULNSIGS-2.5.94-4 /
352217 | Amazon Linux Security Advisory for sudo: AL2012-2021-335 (Baron Samedit) | Amazon Linux | AL2012-2021-335 | VULNSIGS-2.5.95-2 /
374891 | Sudo Heap-based Buffer Overflow Vulnerability (Baron Samedit) | Local | Sudo Security Alerts | VULNSIGS-2.5.90-4 /

  • Version is the signature version followed by the Linux manifest version.


With VMDR Dashboard, you can track this vulnerability, their impacted hosts, their status and overall management in real time. With trending enabled for dashboard widgets, you can keep track of these vulnerabilities trends in your environment using the “Baron Samedit | Heap-based buffer overflow Sudo” Dashboard.

View and download the Baron Samedit dashboard.

Baron Samedit dashboard

Vendor References

Frequently Asked Questions (FAQs)

What versions are vulnerable?

The following versions of sudo are vulnerable:

  • All legacy versions from 1.8.2 to 1.8.31p2
  • All stable versions from 1.9.0 to 1.9.5p1
How can I test if I have vulnerable version?

To test if a system is vulnerable or not, login to the system as a non-root user.

Run command "sudoedit -s /"

If the system is vulnerable, it will respond with an error that starts with "sudoedit:"

If the system is patched, it will respond with an error that starts with "usage:"

Are versions before 1.8.2 vulnerable?

No. See explanation above.

I'm running a vulnerable version of sudo from the list mentioned above. Why does the test show my host is not vulnerable?

Operating system vendors don't always update the software's version number after introducing a patch to the software, particularly when they backport the patch. For example, Ubuntu released a patch (USN-4705-1) for Ubuntu 20.04, but the patched version is still 1.8.31 after the patch is installed.

Why am I seeing multiple QIDs for the same CVE-2021-3156 on the same host?

Qualys released a generic QID at the time of release which detected the vulnerability based on the output of the command sudoedit. Later on, as OS vendors released patches, we released QIDs based on package versions as well. So, it is possible for a vulnerable asset to have the vulnerability reported for QID 374891, and OS specific checks that were released later on.

What are the possible reasons for QID 374891 not triggering against vulnerable systems?

QID 374891 was released with vulnsigs version VULNSIGS-2.5.90-4 and in Linux Cloud Agent manifest version lx_manifest- For the vulnerabilities to be reported these signature and manifest versions need to be available on the platform (the deployment schedule varies), and agents have completed a scan with the latest versions of the signatures.

Also, Qualys released more QIDs based on the security advisories and patches released by various Linux distributions. So certain Linux distributions had coverage before the rest. For e.g., Amazon Linux security advisory was released first, and CentOS advisories, and patches were released couple of days later. Support for CentOS linux was added to later versions vulnsigs and manifests. See table above for details.

For CVE-2021-3156, what is the difference in the detection logic between QID 374891 and the rest of the QIDs?

QID 374891 attempts to confirm the vulnerability based on output of command sudoedit. The rest of the QIDs confirm the vulnerability based on version comparison based on the versions disclosed by the OS vendor in their respective security advisories.

Is a local user required to exploit the vulnerability?

Yes. However, this user does not need to be a privileged user or be a part of sudoers list. For example, even account ‘nobody’ can exploit the issue.

Why name the vulnerability “Baron Samedit”?

It’s a play on Baron Samedi and sudoedit.

Will Qualys Research Team publish exploit code for this vulnerability?