One of my websites is hosted on a shared webserver that uses the software Softaculous to automatically install applications. When going through the system files there was one file in particular that caught my attention: softaculous/bin/soft

[user@webserver]$ ll /usr/local/directadmin/plugins/softaculous/bin/soft 
-r-sr-xr-x 1 root root 11570 19 sep  2020 /usr/local/directadmin/plugins/softaculous/bin/soft

It is a binary file with the setuid root bit set, this can be dangerous as it can create opportunities for privilege escalation. I was not able to find any documentation on what this binary does, and running it also did not help me much:

[user@webserver]$ /usr/local/directadmin/plugins/softaculous/bin/soft 
Softaculous Computer Binary : Invalid Command
[user@webserver]$ /usr/local/directadmin/plugins/softaculous/bin/soft --help
Softaculous Computer Binary : Invalid Command

After reverse engineering the binary using IDA Pro I found out that it executes with two arguments. The first argument (arg1) being one of the list:

  • download
  • sess
  • sess_admin

And the second argument (arg2) needs to be a string containing 32 chars (no special characters allowed). It then runs a specific php script based on the first argument. This php script is called with the execve() function which uses the following php path: /usr/local/bin/php The full command that is executed:

/usr/local/bin/php -c /usr/local/directadmin/plugins/softaculous/php.ini -d auto_prepend_file=none -d auto_append_file=none /usr/local/directadmin/plugins/softaculous/cron.php download=<arg1>

Since arg1 is properly checked on special characters nothing can be injected there. However, after checking the php path on the webserver I found out that this is a symbolic link chain that eventually lead to a php binary. These symbolic links are used in CloudLinux to let the user choose a specific php version for their hosting package, and one of them is actually writable:

[user@webserver]$ ll /usr/local/bin/php
lrwxrwxrwx 1 root root /usr/local/bin/php -> /usr/local/php55/bin/php55
[user@webserver]$ ll /usr/local/php55/bin/php55
lrwxrwxrwx 1 root root /usr/local/php55/bin/php55 -> /etc/cl.selector/php-cli
[user@webserver]$ ll /etc/cl.selector/php-cli
lrwxrwxrwx 1 user user /etc/cl.selector/php-cli -> /opt/alt/php72/usr/bin/php
[user@webserver]$ ll /opt/alt/php72/usr/bin/php
-rwxr-xr-x 1 root 493 /opt/alt/php72/usr/bin/php

This link can be changed so that the soft binary will run your own executable. Since the suid bit is set on the soft binary, it can lead to privilege escalation. Let’s try by removing the link and replacing it with a script that executes the id command:

[user@webserver]$ ./soft download AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
uid=1712(user) gid=1717(user) groups=1717(user)

That’s weird, the user doesn’t get root privileges while the script should’ve run as root because of the suid bit. I tried to replicate the environment and copied the soft binary to my own machine with the exact same permissions.

[max@PC]$ ./soft download AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
uid=0(root) gid=0(root) groups=0(root)

It worked on my machine! There must have been something on the server that blocked the soft binary from being executed as root.

After some research I found out that CloudLinux uses some kind of containers that prevent their users from executing suid binaries, it is called CageFS.

So this probably prevents the user from becoming root. However, not all users in DirectAdmin are in CageFS, for example the admin user (which should not have root access either). So I tested it on the admin-user as well, but it has a different php symlink which is not writable, so that one is secure!

[admin@webserver]$ ll /usr/local/bin/php 
lrwxrwxrwx 1 root root /usr/local/bin/php -> /usr/local/php55/bin/php55
[admin@webserver]$ ll /usr/local/php55/bin/php55
-rwxr-xr-x 1 root root /usr/local/php55/bin/php55

Conclusion

On webservers that run CloudLinux and Softaculous, users are able to change a php-symlink to make the soft binary run a different executable. Fortunately, CageFS prevents the users to execute suid binaries so it is not possible to actually escalate privileges. It still feels like this was not really intentional and systems with somewhat similar architectures that don’t have such a protection layer can still be exploited.

Discussion

  • Apparently the suid binary can’t be executed as root in CloudLinux by users with CageFS, so why is it even needed? (The only logical reason in this case would be that only the admin user needs to run the soft binary)
  • Can the path /usr/local/bin/php always be trusted? Like in this case, it was a symbolic link that could be changed. How do other webservers with different architectures look?