Sudo is a setuid program that allows a user to execute commands as another user (impersonation feature). Because of this, finding a vulnerability in sudo is an important privilege escalation.
When running sudo with the -s or -i flag, turns on functionality that rewrites argv by concatenating all command-line arguments and by escaping all meta-characters with backslashes. Parsing is really hard!
When this transferring and escaping happens, is where the parsing issues occur. If a command line input ends with a single backslash, then issues happen with the copying functionality. It causes the while loop to over deviate because of an unexpected increase in the 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.
Here is the problem though: it is not possible to call this function with a single backslash, by default. By calling sudoedit, the escape code can be skipped, causing the vulnerable path to hit. An example crashing exploit looks like the sudoedit -s '\' `perl -e 'print "A" x 65536'`. This hits iterates once too many, then copies extra data from the heap.
To an attacker, this buffer overflow is awesome because
- The size of the "user_args" buffer that we overflow, which is just the size of our concatenated command line arguments.
- Independently control the size and contents of the overflow itself.
Our last command-line argument is conveniently followed by our first
environment variables.
- Even write null bytes to the buffer that we overflow (every command-line argument or environment variable that ends with a single backslash writes a null byte to user_args.
The original idea for exploitation was to corrupt locale strings in order to turn this into a format string exploit. This is a common exploitation for sudo and other binaries with locales used. However, this technique did not produce any good results.
After setting up a fuzzer to find different exploitation primitives, a few ways were found that caused crashes with the proper heap grooming.
The first interesting crash was a direct overwrite of a function pointer within process_hooks_getenv . The main thing to note was that the function pointer could be partially overwritten in order to bypass ASLR.
The second exploitation method was found by a crash in nss_load_library. Using this crash, it was possible to overwrite the file path of the library being loaded to load an arbitrary library.
The final way they found to exploit this was different than all of the others. From the fuzzing, they noticed that a bunch of directories were created with root privileges by overwriting the location of the def_timestampdir. Using this, we can overwrite this directory then we can race against Sudo's ts_mkdirs(), create a symlink to an arbitrary file and (or create) this arbitrary file as root, and write a struct timestamp_entry to it.
Using this timestamp writing functionality, we can open /etc/passwd and write a user into this. Now, we can use sudo and become root.