Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2021-3156-Heap-Based-Buffer-Overflow-in-Sudo-Baron-Samedit- — Technical analysis and exploit development for CVE-2021-3156, a heap-based buffer overflow in Sudo, including three working exploits for local privilege escalation on major Linux distributions. | Kitploit
Tools/GitHubGitHub/sornphut/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit-
Privilege EscalationVulnerability AnalysisExploitationPenetration TestingPapers & ResearchLearning & EducationBinary Exploitation
GitHubsornphut/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit-

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

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

Technical analysis and exploit development for CVE-2021-3156, a heap-based buffer overflow in Sudo, including three working exploits for local privilege escalation on major Linux distributions.

View Repository
131 year agoNot yet reviewed

Qualys Security Advisory

Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156)

======================================================================== Contents

Summary Analysis Exploitation Acknowledgments Timeline

======================================================================== Summary

We discovered a heap-based buffer overflow in Sudo (https://www.sudo.ws/). This vulnerability:

  • is exploitable by any local user (normal users and system users, sudoers and non-sudoers), without authentication (i.e., the attacker does not need to know the user's password);

  • 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.

We developed three different exploits for this vulnerability, and obtained 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 probably also exploitable.

======================================================================== Analysis

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 )user_details.shell; / 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 lines 852-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)) {

versus:


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

Our question, then, 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):


127 #define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL) ... 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 }

Download Tool