# Executing system commands from PHP with SUID executable.

If you want to execute system commands from something like PHP, you need a SUID executable which you can call from your PHP scripts. This is such a script. It could be extended to support parameters for the commands you want to execute, but that would be an enormous security risk, because then anybody can execute any command. If you need something as flexible as that, you need to think about adding some kind of security restrictions, like a list of allowed commands.

When writing this, it occurred to me how unnecessary this all is. I will explain below. First, I will describe the old way.

Here is the c source code, as written for our backup script, bsbackup.sh:

// Wrapper for the bsbackup.sh shell script, to be able to run it as root when
// started from a webserver, for example. Set the resulting executable to SUID
// root.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <error.h>

int main(int argc, char *argv[], char *envp[])
{
int set_uid_result;
int effective_user_id;
int execute_script_error;
char* script;

effective_user_id = geteuid();

// Set real and effective user ID
set_uid_result = setreuid(effective_user_id, effective_user_id);

if (set_uid_result != 0)
{
printf("Failed to set user id\n");
return 1;
}

script = "/usr/local/sbin/bsbackup.sh";

// This does not return on success.
execve(script, argv, envp);
execute_script_error = errno;

// Show a fancy error message.
error(execute_script_error, execute_script_error, script);

// Shouldn't be necceary, but you never know.
return 1;
}

To compile:

gcc -o bsbackup bsbackup.c

You can then run this inside PHP:

// The 2>&1 makes all error messages appear on stdout, for easy capturing.
passthru('/usr/local/sbin/bsbackup usb_backup 2>&1');

As I said, when writing this, it all became very clear to me that it is quite useless. One can also install sudo, run visudo and put this in (assuming your webserver runs as www-data, like on (Debian and Ubuntu):

www-data ALL = NOPASSWD: /usr/local/sbin/bsbackup.sh

Then in PHP, just run this:

passthru('sudo /usr/local/sbin/bsbackup.sh usb_backup 2>&1');

I haven’t tested whether specifying the parameters after the script in the passthru actually works, but I think so. If not, you can just write a wrapper script around the command you’re going to execute.

See what you like best 🙂