So I have hosted a webpage on my apache server and I'm trying to run some python and bash scripts when the user presses a button via PHP and AJAX.
Now my php file executes at python script (located in /var/www/html) which in turn executes a bash file (located in root/files).
On doing this manually in terminal, everything works perfectly fine.
But when I try to this via the webpage, the bash script isn't executed.
(I can't place the bash script in /var/www/html because it has the command to clone a git repository to the server and it gives private key shouldn't be public error when placed there)
I already tried suggestions in this answer by adding www-data to sudoers but it is still not working as expected.
Any help on this would be appreciated.
Thanks
PHP file :
if(isset($_POST['timestamp']))
{
$uid = $_POST['timestamp'];
echo "Please wait while the app is being generated".$uid;
exec("python /var/www/html/appgenserver.py $uid");
appgenserver.py
#! /usr/bin/env python
import os
import json,sys
from firebase import firebase
import requests
import subprocess
arg = sys.argv[1]
# Path to be created
path = "/root/files/"+str(arg)
print path
if not os.path.exists(path):
os.makedirs(path) #Gets executed
subprocess.call(['/root/Final/clone.sh', path) #Not getting executed
Most likeley because a bash script in its self won't be executable, it's just a plain textfile.
Your bash (and perhaps even appgenserver.py?) might be located under /root and apache probably runs as a non-priviliged user such as www-data, that user won't be able to access either your python script and in turn not the bash that the python would run.
Consider instead calling bash with the script as a parameter.
#! /usr/bin/env python
import os
import json,sys
from firebase import firebase
import requests
import subprocess
arg = sys.argv[1]
path = "/root/files/"+str(arg)
print path
if not os.path.exists(path):
os.makedirs(path)
subprocess.call(['/bin/bash', '/root/Final/clone.sh', path)
Now, this is NOT the most pretty of solutions.
But what you got before was probably a generic "Permission denied" error in the background (check your /var/log/apache/error.log).
What this does is start /bin/bash as a subprocess with the first parameter being the script you want to execute.
But you have zero error handling here and you can't interract with the process very much.
Consider doing something like this instead:
import subprocess
handle = subprocess.Popen(['/bin/bash', '/root/Final/clone.sh', 'parameter'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
while handle.poll() is None:
print(handle.stdout.read()) # There's some optimizations here to be done as well so it's not a blocking call etc.
handle.stdout.close()
handle.stdin.close()
And one last tip is not to place stuff in /root/ at all if you're integrating it into a web front-end/backend type of thing.
You're asking for trouble : )
Another way is to make use of sudo
If you modify your exec() in PHP to run exec("sudo ...") and enable your web-user to run the scripts without a password prompt it could work.
Bare in mind, it's not recommended to give www-data sudo access, rather do something like this:
# useradd -m -G www-data -s /bin/bash wwwexec
# echo "myuser ALL=NOPASSWD: /usr/bin/python" >> /etc/sudoers
and change your PHP script to have the following:
exec("sudo -u wwwexec python /var/www/html/appgenserver.py $uid");
That way at least your entire web service isn't given root access via the default username.
The way to do it
Would be to place your appgenserver.py under /var/www/cgi-bin/ instead, and create a CGI hook for .py in your apache configuration and hand over the user to the URL prividing you access to the CGI script.
That way everything should be according to best practices even tho, in theory, you could get your original solution to work.
For instance, this guide should get you started.
Related
I have a php file which is called by a website:
example: serial_tx.php?v=W100
Within the php I write a log file where I can see which string v I received (W100 in this case).
The webserver is hosted on a Raspberry Pi and should send this data to the uart.
The files locations:
/SCRIPTS/serial_tx.php
/SCRIPTS/c/jmsend/serial_tx // the executable, compiled from a C script
If I am in the root of the webserver and, from the console of my Pi, I run
sudo /var/www/html/SCRIPTS/c/jmsend/serial_tx W100
I get the command sent correctly.
With the php file I tried with system, shell_exec and exec without success.
shell_exec("sudo /var/www/html/SCRIPTS/c/jmsend/serial_tx ".$ric);
$ric is the received command.
I tried with different path settings too (starting from Pi root or webserver root).
All the files have a 777 as permissions.
Something like this in /etc/sudoers should work to allow your web server user to run that particular command without issue:
Cmnd_Alias SERIAL = /var/www/html/SCRIPTS/c/jmsend/serial_tx *
www-data ALL=(ALL) NOPASSWD: SERIAL
Note that you must escape user input before using it:
$ric = escapeshellarg($_GET["v"]);
shell_exec("sudo /var/www/html/SCRIPTS/c/jmsend/serial_tx $ric");
You should also be aware of the differences between exec() and shell_exec(), specifically that you can't check for a failure using shell_exec().
$ric = escapeshellarg($_GET["v"]);
exec("sudo /var/www/html/SCRIPTS/c/jmsend/serial_tx $ric", $output, $return);
if ($return !== 0) {
// let the user know something didn't work
}
This assumes, of course, that your executable is written to return appropriate error codes.
I have a Python script, which works fine when I run it in Terminal. Here it is:
import bs4, requests, json, sys
from sys import argv
def getFsmonData(link):
res = requests.get(link)
res.raise_for_status()
soup = bs4.BeautifulSoup(res.text)
# Get table data
tds = soup.select('td')
new_td_list = []
for i in range(len(tds)):
inner_td_list = []
for y in range(len(tds)):
inner_td_list.append(tds[y].getText())
new_td_list.append(inner_td_list)
print(inner_td_list)
td_list_json = json.dumps(inner_td_list)
tdf = open("/path/to/file/data/td_data.txt", "w")
tdf.write(td_list_json)
tdf.close()
getFsmonData(sys.argv[1])
But now I am trying to run it from PHP and I get the following error:
Traceback (most recent call last): File
"/path/to/file/example.py", line 1, in import bs4, requests,
json, sys ModuleNotFoundError: No module named 'bs4'
I guess PHP thinks I do not have this module installed, but I am not sure of course. Here is my PHP code:
<?php
$link = $_POST['dn-link'];
if (isset($_POST['submit'])) {
system('/usr/bin/python3 /path/to/file/example.py ' . escapeshellarg($link) . ' 2>&1', $retval);
}
?>
Who can help solving this issue?
Most likely, when your PHP script executes your Python script, its executing it as a different user (possibly super user). What you can do is check if the packages are installed under the super user by doing
sudo pip list
or if you're using python3
sudo pip3 list
see if the packages you we're wanting to use are listed from this command.
If they aren't listed, you can easily install them via the command:
sudo pip install <pkg name>
or
sudo pip3 install <pkg name>
What you're doing here is installing the packages under the super user rather than you the local user.
I think your misunderstanding the permission issue. It's not that you need to add it PHP. It's that your OS keeps track of permissions, and the permission you have assigned to your PHP interpreter and your Python interpreter are misaligned. There are two permissions that usually people mix up here - USER which is login level access and SYSTEM which is system wide access. If your on your own dev machine then add both to SYSTEM.
We need to know your OS to be able to fully answer, i'm going to guess Mac OS in which case would be this (I think, can't test it right now).
sudo nano "pathOfYourInterpreter"
The other thing could be happening is that the libraries you're tryng to import are somewhere where the current sys path you have setup can't reach them. For example if you have your sys variable for Python set to the directory where the interpretor is but the scripts you're importing are in a different folder. In that case you have several options for accessing those scripts.
Set the current working directory to the folder where they're located before importing with:
os.chdir("locationOfScript/")
import script.py
Add a sys Path at runtime:
sys.path.append("locationOfScript/)
There are more methods of attack, but I think this should get you started.
I have a bash script that takes a parameter is called in PHP by shell_exec(script.sh parameter). Basically, my goal is to call a script that is owned by another user that is not apache.
The script.sh script is a file that contains the following (right now there are some error handling commands):
#/bin/bash
whoami>>whoami
echo $1 >> parameter
while read f; do
env>>envoutput
sudo -i -u archivescriptowner /path/to/archivescript.sh -command archive >> output
done < $1
In my /etc/sudoers file , I have the following:
apache ALL=(archivescriptowner) NOPASSWD: /bin/bash -c /path/to/archivescript.sh *
When I run this script as by running su -s /bin/bash apache and pass a parameter, it works.
When I run it via my button in php, archivescript.sh does not execute
The whoami file has apache written to it
The parameter file has the right file written to it
env shows the following
Term=xterm
LD_LIBRARY_PATH=/path/to/library
PATH=/sbin/:usr/sbin:/bin:/usr/bin
PWD=/var/www/html
LANG=C
SHLVL=4
=/bin/env
PWD is outputting right, that is where my script is right now, it will be moved in the future.
The output file when it is ran by the button click is blank.
I am at a loss as to why this is not working. Any insight would be helpful. Please let me know if I need to give any additional information.
I recently published a project that allows PHP to obtain and interact with a real Bash shell. Get it here: https://github.com/merlinthemagic/MTS
After downloading you would simply use the following code:
$shell = \MTS\Factories::getDevices()->getLocalHost()->getShell('bash', true);
$return1 = $shell->exeCmd('/path/to/archivescript.sh');
echo $return1; //return from your script
I actually try to laucnh a gnome-term with a php script, seems i have some problems with the users www-data;
my script make only a ls -l command in a directory (is just for a test) and i run it with a php page in my local-web site.
here the gnome-terminal command in my bash script (he run perfectly when i double-click on him) :
gnome-terminal --working-directory=/opt/cuckoo -x bash -c "ls -l"
and here is the call on the php-page :
system("/my/path/to/the/script/script.sh");
i have some echo in my script and i see them in the php page after i try to run the script with the php.page.
i think www-data don't have the right to do so i give the ownership of the script with the chown command, and at last a try the sudo visudo command and make the script execute like the user www-data is root (with NO PASSWD arg)
But i can't open the terminal and make a ls at last, i try with exec too, and show the result with $ouput butthe result is the same as well.
At last my question is : Php can really run a terminal or maybe a fool myself^^? Thanks for taking time to rescure me ;)
PHP can run everything, but depends who spawns it. Forget just running X apps from a web server - you'll need more than just executing them (permissions, DISPLAY and Xauth settings). Read more about the X clients and architecture.
Probably the right place to ask this is at SuperUser, since the problem is not in the coding itself.
I have a script in /var/www/myscript.sh which creates folders and runs the command svn update for my projects. I need to execute this script by calling it in a PHP file in the browser (i.e. Localhost/test.php). I tried using functions shell_exec() and exec() but those did not work. I ran my shell script in terminal with su www-data && ./myscript.sh and it worked. What else am I missing?
<?php
$output = shell_exec("./myscript.sh");
?>
Update 5/4/2011:
I added www-data ALL=(ALL) NOPASSWD:ALL to /etc/sudoers and it works, but this is very insecure. Is there another way to do this?
Several possibilities:
You have safe mode enabled. That way, only exec() is working, and then only on executables in safe_mode_exec_dir
exec and shell_exec are disabled in php.ini
The path to the executable is wrong. If the script is in the same directory as the php file, try exec(dirname(__FILE__) . '/myscript.sh');
You might have disabled the exec privileges, most of the LAMP packages have those disabled. Check your php.ini for this line:
disable_functions = exec
And remove the exec, shell_exec entries if there are there.
Good Luck!
Residuum did provide a correct answer to how you should get shell exec to find your script, but in regards to security, there are a couple of points.
I would imagine you don't want your shell script to be in your web root, as it would be visible to anyone with web access to your server.
I would recommend moving the shell script to outside of the webroot
<?php
$tempFolder = '/tmp';
$webRootFolder = '/var/www';
$scriptName = 'myscript.sh';
$moveCommand = "mv $webRootFolder/$scriptName $tempFolder/$scriptName";
$output = shell_exec($moveCommand);
?>
In regards to the:
i added www-data ALL=(ALL) NOPASSWD:ALL to /etc/sudoers works
You can modify this to only cover the specific commands in your script which require sudo. Otherwise, if none of the commands in your sh script require sudo to execute, you don't need to do this at all anyway.
Try running the script as the apache user (use the su command to switch to the apache user) and if you are not prompted for sudo or given permission denied, etc, it'll be fine.
ie:
sudo su apache (or www-data)
cd /var/www
sh ./myscript
Also... what brought me here was that I wanted to run a multi line shell script using commands that are dynamically generated. I wanted all of my commands to run in the same shell, which won't happen using multiple calls to shell_exec(). The answer to that one is to do it like Jenkins - create your dynamically generated multi line of commands, put it in a variable, save it to a file in a temp folder, execute that file (using shell_exec in() php as Jenkins is Java), then do whatever you want with the output, and delete the temp file
... voila
If you are having a small script that you need to run (I simply needed to copy a file), I found it much easier to call the commands on the PHP script by calling
exec("sudo cp /tmp/testfile1 /var/www/html/testfile2");
and enabling such transaction by editing (or rather adding) a permitting line to the sudoers by first calling sudo visudo and adding the following line to the very end of it
www-data ALL=(ALL) NOPASSWD:/bin/cp /tmp/testfile1 /var/www/html/testfile2
All I wanted to do was to copy a file and I have been having problems with doing so because of the root password problem, and as you mentioned I did NOT want to expose the system to have no password for all root transactions.