27

I want to execute a Bash script present on the system from a PHP script. I have two scripts present on the system. One of them is a PHP script called client.php present at /var/www/html and the other is a Bash script called testscript present at /home/testuser.

My client.php script looks like

<?php
  $message=shell_exec("/home/testuser/testscript 2>&1");
  print_r($message);
?>  

My testscript looks like

#!/bin/bash
echo "Testscript run succesful"

When i do the following on terminal

php client.php

I get the following output on terminal

Testscript run successful

But when i open the page at

http://serverdomain/client.php

I get the following output

sh: /home/testuser/testscript: Permission denied 

I get this error even after I did chmod +x testscript.
How do I get it to work from the browser? Please help.

jww
  • 97,681
  • 90
  • 411
  • 885
nmadhok
  • 1,704
  • 3
  • 16
  • 20
  • 1
    How about `read` permission? `Execute` permission is useless without `read` permission. – Samveen Jun 17 '13 at 16:04
  • 9
    Apache is not going to have permissions to reach into your home directory. Nor should you ever really want it to. REmember that it's not enough to grant rights to the script. You have to grant access to the directories it's in as well. e.g. if I put a "free, take one" newspaper holder inside a bank vault, no one can take the papers, even though they're free. – Marc B Jun 17 '13 at 16:07
  • @MarcB I forgot about the path's permissions. And a good analogy. – Samveen Jun 17 '13 at 16:10
  • i did ls -l and got the following output: -rwxr-xr-x 1 apache apache 236 Jun 17 14:05 testscript. So how do i go about it? Where should i keep the script so that it can be executed by the php script from browser as well as the bash script can execute as root. – nmadhok Jun 17 '13 at 16:10
  • @user2132281: the script is not the problem. it's your home directory. apache cannot reach into it. don't give apache permission to do so either. move the script somewhere where apache CAN get at it. – Marc B Jun 17 '13 at 16:12
  • i want the testscript to have root privileges. so i cannot move it into the directory /var/www/html right? – nmadhok Jun 17 '13 at 16:15
  • See my answer. regardless of where it's at. root can do anything and everything even if root is not the owner. – Panama Jack Jun 17 '13 at 16:20
  • google for `visudo NOPASSWD` – bwoebi Jun 17 '13 at 19:44
  • 4
    You'll need to grand `sudo` permissions to your webserver on that script. Or wrap the script in a suid binary. But either way, be VERY VERY VERY VERY VERY careful with this. Invoke the script wrong, pass around some data wrong, and you've provided remote users with a root shell on your server, and boom goes your server. – Marc B Jun 17 '13 at 19:45
  • 1
    That's called as a **bad idea**. :) Simply don't do it. Real security risk. – clt60 Jun 17 '13 at 21:10
  • i want the php to execute the script and run it as root. How else can i do that? – nmadhok Jun 17 '13 at 21:48
  • Another method is running a daemon with the correct privileges, which isn't reachable from outside the server, and listens to very specific whitelisted local requests only. Still be very careful, but much better then giving the _webserver_ those privileges. – Wrikken Jun 17 '13 at 23:25
  • Instead of `#!/bin/bash` you should use Posix `#!/usr/bin/env bash`. It should locate Bash in other locations, like `/usr/local/bin` if present and on-path. – jww Nov 28 '18 at 03:37

4 Answers4

20

I would have a directory somewhere called scripts under the WWW folder so that it's not reachable from the web but is reachable by PHP.

e.g. /var/www/scripts/testscript

Make sure the user/group for your testscript is the same as your webfiles. For instance if your client.php is owned by apache:apache, change the bash script to the same user/group using chown. You can find out what your client.php and web files are owned by doing ls -al.

Then run

<?php
      $message=shell_exec("/var/www/scripts/testscript 2>&1");
      print_r($message);
    ?>  

EDIT:

If you really want to run a file as root from a webserver you can try this binary wrapper below. Check out this solution for the same thing you want to do.

Execute root commands via PHP

Community
  • 1
  • 1
Panama Jack
  • 24,158
  • 10
  • 63
  • 95
  • Since the webserver doesn't (or shouldn't) run as root, it won't be able to do that. – Barmar Jun 17 '13 at 16:34
  • I updated my answer with information to a solution that should work for you. Save me time for writing it out. :) – Panama Jack Jun 17 '13 at 16:44
  • 1
    i want my testscript to be located at /home/testuser only. And i want to execute that using php. How can i do it without having to shift my testscript or without having to compromise security. I just want it for this script – nmadhok Jun 17 '13 at 17:22
  • Does not work for me, i get no output from my script if i execute it as `www-data`, but it works if execute it with `root` – Black Jan 29 '16 at 15:07
5

Without really knowing the complexity of the setup, I like the sudo route. First, you must configure sudo to permit your webserver to sudo run the given command as root. Then, you need to have the script that the webserver shell_exec's(testscript) run the command with sudo.

For A Debian box with Apache and sudo:

  1. Configure sudo:

    • As root, run the following to edit a new/dedicated configuration file for sudo:

      visudo -f /etc/sudoers.d/Webserver
      

      (or whatever you want to call your file in /etc/sudoers.d/)

    • Add the following to the file:

      www-data ALL = (root) NOPASSWD: <executable_file_path>
      

      where <executable_file_path> is the command that you need to be able to run as root with the full path in its name(say /bin/chown for the chown executable). If the executable will be run with the same arguments every time, you can add its arguments right after the executable file's name to further restrict its use.

      For example, say we always want to copy the same file in the /root/ directory, we would write the following:

      www-data ALL = (root) NOPASSWD: /bin/cp /root/test1 /root/test2
      
  2. Modify the script(testscript):

    Edit your script such that sudo appears before the command that requires root privileges(say sudo /bin/chown ... or sudo /bin/cp /root/test1 /root/test2). Make sure that the arguments specified in the sudo configuration file exactly match the arguments used with the executable in this file. So, for our example above, we would have the following in the script:

    sudo /bin/cp /root/test1 /root/test2
    

If you are still getting permission denied, the script file and it's parent directories' permissions may not allow the webserver to execute the script itself. Thus, you need to move the script to a more appropriate directory and/or change the script and parent directory's permissions to allow execution by www-data(user or group), which is beyond the scope of this tutorial.

Keep in mind:

When configuring sudo, the objective is to permit the command in it's most restricted form. For example, instead of permitting the general use of the cp command, you only allow the cp command if the arguments are, say, /root/test1 /root/test2. This means that cp's arguments(and cp's functionality cannot be altered).

Craig H
  • 158
  • 8
  • Just set all up as above and am getting "sudo: no tty present and no askpass program specified" in apache's error.log on Centos 6. – webcoder.co.uk Oct 11 '20 at 11:14
  • I've come across other similar solutions that state one needs to separate sudoers allowed to run commands or scripts paths with commas i.e. NOPASSWD: /bin/cp, /root/test1, /root/test2 ? – webcoder.co.uk Oct 13 '20 at 14:01
3

I was struggling with this exact issue for three days. I had set permissions on the script to 755. I had been calling my script as follows.

<?php
   $outcome = shell_exec('/tmp/clearUp.sh');
   echo $outcome;
?>

My script was as follows.

#!bin/bash
find . -maxdepth 1 -name "search*.csv" -mmin +0 -exec rm {} \;

I was getting no output or feedback. The change I made to get the script to run was to add a cd to tmp inside the script:

#!bin/bash
cd /tmp;
find . -maxdepth 1 -name "search*.csv" -mmin +0 -exec rm {} \;

This was more by luck than judgement but it is now working perfectly. I hope this helps.

MarmiK
  • 5,639
  • 6
  • 40
  • 49
2

It's a simple problem. When you are running from terminal, you are running the php file from terminal as a privileged user. When you go to the php from your web browser, the php script is being run as the web server user which does not have permissions to execute files in your home directory. In Ubuntu, the www-data user is the apache web server user. If you're on ubuntu you would have to do the following: chown yourusername:www-data /home/testuser/testscript chmod g+x /home/testuser/testscript

what the above does is transfers user ownership of the file to you, and gives the webserver group ownership of it. the next command gives the group executable permission to the file. Now the next time you go ahead and do it from the browser, it should work.

priyolahiri
  • 668
  • 1
  • 5
  • 10
  • i'm on RHEL. web server user would be apache for me. Also will the script be still executable by the root? You said the user ownership would transfer to me. – nmadhok Jun 17 '13 at 19:28
  • Root is all encompassing. It would still be accessible by root. – priyolahiri Jun 20 '13 at 16:51