flawopen.com/Teardowns/cve-2021-3156-sudo-baron-samedit
CVE-2021-3156: Sudo Baron Samedit Heap Overflow
CVE-2021-3156 was a heap buffer overflow in sudo set_cmnd(): a trailing backslash could make its unescaper step past an argument terminator and copy beyond a buffer sized from the arguments. Qualys reported affected legacy versions 1.8.2–1.8.31p2 and stable versions 1.9.0–1.9.5p1 in the default configuration.[3]
A form-processing clerk treats a backslash as an instruction to skip the next character. If the backslash is the last character, the clerk copies the form’s end marker as if it were ordinary text, then keeps reading beyond the form and writing into a tray sized only for its actual text. The fix checks that a real character follows before skipping and checks that each write fits.[3]
Core Concepts & Subsystem Terms
Baron SameditCVE-2021-3156is a heap-based buffer overflow in sudo. Qualys reported that the affected parsing path had existed since July 2011.[3]Heap Overflow- Writing past the boundary of dynamically allocated heap memory, corrupting control structures.[3], [2]
Bounded parser- A parser that carries an explicit input length and checks every read and output write against that length and capacity.[4]
Framework mappings
- CWE reference for the weakness described in this guide: CWE-193
- CISA KEV — added 2022-04-06 — CVE-2021-3156
Root Cause Analysis
Reachability depended on different mode checks: parse_args() escaped metacharacters only in command-run plus shell mode, while set_cmnd() entered the unescape block for shell or login-shell mode without requiring command-run mode. This mismatch could let an argument reach the unescaper without the preceding escape transformation. In set_cmnd(), the unescape loop copied a backslash and then read the following byte. When an argument ended with a backslash, the loop advanced past the terminating NUL and continued copying beyond the source argument into user_args, whose size was calculated from the original arguments. The affected path required local access and the vulnerable default configuration; Qualys reported local privilege escalation to root on affected systems. The fix checks that a byte follows the backslash before skipping it and verifies remaining destination capacity before each write.[3], [4]
Step-by-Step Attack Flow
Mode-check mismatch
Qualys traced CVE-2021-3156 to a mode-check mismatch: parse_args() escaped metacharacters only for command-run plus shell mode, but set_cmnd() entered its unescape block for shell or login-shell mode without requiring command-run mode. This could expose the unescaper to an argument that had not passed through the matching escape stage. Local access and the vulnerable default configuration were required.[3], [4]
Destination sized from arguments
set_cmnd() allocated user_args from the lengths of the command-line arguments, including space for their terminators and separators.[3]
Terminator crossed
In the unescape loop, a trailing backslash passed the non-whitespace test because the following NUL byte is not whitespace. Advancing over the backslash left the source pointer at NUL; copying and advancing again moved it past the argument.[3]
Heap boundary exceeded
The loop could then read past the argument and write bytes not included in the destination-size calculation, causing a heap buffer overflow. Qualys verified local privilege escalation to root on affected default configurations.[3]
Upstream fix
The upstream patch avoids this unescape path outside command-run mode, refuses to skip a backslash before NUL, and checks remaining destination capacity before writes. Apply the security update supplied by the operating-system vendor.[4]
Source Code: Flaw vs. Secure Implementation
The hardened excerpt also adds MODE_RUN to align the unescape block with the mode that performs the preceding escape transformation. These focused excerpts show sudo set_cmnd() argument-unescaping logic and the corresponding upstream fix; they are not the complete function or an exploit. The vulnerable loop checks whether the next byte is whitespace but not whether it is NUL before advancing over a backslash. The fix rejects that end-of-argument case and checks remaining destination capacity before writes. Apply the security update provided by the operating-system vendor.[3], [4]
size_t size = 0;
for (char **av = NewArgv + 1; *av; av++)
size += strlen(*av) + 1;
char *user_args = malloc(size);
if (ISSET(sudo_mode, MODE_SHELL | MODE_LOGIN_SHELL)) {
for (char *to = user_args, **av = NewArgv + 1, *from; (from = *av); av++) {
while (*from) {
if (from[0] == '\\' && !isspace((unsigned char)from[1]))
from++;
*to++ = *from++;
}
*to++ = ' ';
}
*--to = '\0';
}
if (ISSET(sudo_mode, MODE_SHELL | MODE_LOGIN_SHELL) &&
ISSET(sudo_mode, MODE_RUN)) {
for (char *to = user_args, **av = NewArgv + 1, *from;
(from = *av); av++) {
while (*from) {
if (from[0] == '\\' && from[1] != '\0' &&
!isspace((unsigned char)from[1])) {
from++;
}
if (size - (size_t)(to - user_args) < 1)
debug_return_int(NOT_FOUND_ERROR);
*to++ = *from++;
}
if (size - (size_t)(to - user_args) < 1)
debug_return_int(NOT_FOUND_ERROR);
*to++ = ' ';
}
*--to = '\0';
}
Incident Timeline
Engineering & System Hardening Checklist
- ✓Upgrade through the security release supplied by your operating-system vendor; distributions may backport the fix without changing to upstream sudo 1.9.5p2.[3], [4]
- ✓Inventory sudo packages and verify each installed package against its distribution advisory, not only the upstream version string.[3]
- ✓When reviewing unescape loops, require a non-NUL lookahead before skipping an escape marker and prove the destination capacity covers every emitted byte and terminator.[4]
- ✓Add regression tests for a trailing backslash, an escaped non-space character, an escaped space, empty arguments, and output at the exact capacity boundary.[3], [4]
- ✓Treat local access as the precondition and prioritize patching privileged utilities; do not dismiss a root-impacting flaw because it is not remotely reachable by itself.[3]
Sources
- NVD — CVE-2021-3156
- MITRE CWE-193
- Qualys — CVE-2021-3156 heap-based buffer overflow in sudo
- sudo upstream fix commit 1f86385
- oss-security — CVE-2021-3156 sudo heap-based buffer overflow
- CISA advisory — sudo heap-based buffer overflow
- Heartbleed: one missing bounds check, half the internet
- When Rust is not memory safe: unsafe blocks and the smallvec overflow