This past week, I decided I wanted to learn more about exploits and exploit analysis. As a result, I checked out CVE-2021-3156, a Sudo vulnerability from 2021 that has long since been patched. Reproducing the vulnerability presented a number of difficulties: setting up an environment with a pre-2021 version of Sudo, ensuring it hadn't been patched, and triggered the heap overflow. However, I was unable to achieve full code execution due to heap layout differences in Docker.
CVE-2021-3156 is a heap-based buffer overflow in sudo. The vulnerability allows you to write data beyond the bounds of an allocated buffer, corrupting adjacent memory on the heap. The exploit targets the service_user struct in glibc's Name Service Switch (NSS) system, which contains function pointers used for user and group lookups. By overflowing the buffer, you can overwrite these function pointers to redirect them to attacker-controlled code.
When sudo calls an NSS function (like getpwnam_r() to look up user information), it follows the corrupted function pointer and executes your code instead. Since sudo runs with root privileges, your code inherits those privileges, allowing you to spawn a root shell.
I needed to set up an environment with this vulnerability. The first approach was to run Ubuntu 18.04 in the UTM virtualization application. The older Ubuntu image presented some issues. Installation crashed repeatedly across multiple attempts, and even the live image of this version would not run. I abandoned this approach in favor of using a docker container.
A Docker container running Ubuntu 18.04 ran successfully on my machine. However when I attempted the exploit, I received a usage error indicating that Sudo had been updated. I checked the binary's timestamp and it had been compiled in 2023 which was two years after the patch.
Finally, I simply downloaded the old version of Sudo in the docker instance of Ubuntu 18.04, and I installed it. I ran it from it's location, and success! I can achieve a memory access error using this version of sudo.
From here I downloaded the exploit scripts, and attempted to exploit this flaw to gain root access. Because these scripts assume the default version of sudo and not the one I installed, they repeatedly would not work. I made a symlink to the newly installed sudo and that did allow the heap overflow attempt to gain root access. Unfortunately after trying multiple different chunk sizes, I could not gain root access through the exploit. I speculate that Docker may arrange heap memory substantially differently from a typical Ubuntu install, making this exploit more difficult to achieve.
So while I did not get to root shell, the exercise taught me a lot: how heap-based buffer overflows work, the difficulties of exploits on different architectures, using virtualization to match a target architecture, and why exploits are not universal across architectures.
Top comments (0)