PHP executable will run in command line but not browser - php

So using 'php index.php' gives me the output I want in the command line. But will not give the output on the webpage.
So first of all I have this python file which basically does everything I want:
import subprocess
subprocess.call("sudo nmap -sP 192.168.1.0/24 > /home/pi/whohome.txt", shell=True)
searchfile = open("/home/pi/whohome.txt", "r")
for line in searchfile:
if "android-5ab6eb374b5fd6" in line: print "Jeremy is home (phone)"
if "Jeremys-MBP" in line: print "Jeremy is home (computer)"
if "LMCMs-iPhone" in line: print "Liam is home (phone)"
if "Liam" in line: print "Liam is home (computer)"
if "android-4a186cbbeb2c5229" in line: print "Lara is home (phone)"
if "LaraD" in line: print "Lara is home (computer)"
if "KristiansiPhone" in line: print "Martin is home (phone)"
if "Martins-MBP" in line: print "Martin is home (computer)"
searchfile.close()
Secondly I just have a sh executable that will put the output of this python command into another text file:
python /home/pi/myRoomMates.py > /var/www/html/website.txt
I then have the php file on an apache web server running on the raspberry pi, it reads:
<?php
shell_exec('/home/pi/whoishome.sh');
echo file_get_contents ("/var/www/html/website.txt");
?>
So if I'm not wrong, each time the page is refreshed it should execute that, wait for the exec to finish, then display the txt file contents? I have tried both shell_exec and just exec, they both do the same..

There are many rights, you have to ensure:
the apache user has to be in the sudoers group
the apache user must write to /home/pi/whohome.txt
the apache user must write to /var/www/html/website.txt
/home/pi/whoishome.shmust be executable for the apache user
For point 1 to 3, it is normally not a good idea, to give the apache users these rights.
You can make it easier if you start your python script as CGI:
import subprocess
ADDRESS = "192.168.1.0/24"
USERS = {
"android-5ab6eb374b5fd6": ("Jeremy", "phone"),
"Jeremys-MBP": ("Jeremy", "computer"),
"LMCMs-iPhone": ("Liam", "phone"),
"Liam": ("Liam", "computer"),
"android-4a186cbbeb2c5229": ("Lara", "phone"),
"LaraD": ("Lara", "computer"),
"KristiansiPhone": ("Martin", "phone"),
"Martins-MBP": ("Martin", "computer"),
}
nmap = subprocess.Popen(["sudo", "nmap", "-sP", ADDRESS], stdout=subprocess.PIPE)
for line in nmap.stdout:
for user, name in USERS.items():
if user in line:
print "%s is home(%s)" % name
nmap.wait()
The only point 1 and 4 must be fullfilled.

I suspect your problem is the sudo part of the nmap command line. If you replace the subprocess.call with subprocess.check_call, I think you will find that command raises a CalledProcessError.
Presumably, your user account is in the /etc/sudoers file, but the Web server is not.
Since the first thing the shell's output redirect operator (>) does is truncate the output file, that failed attempt to run nmap results in a zero-byte whohome.txt. The rest of the Python script then does the same to website.txt, and you end up with nothing to display on your Web site.
Solutions
No sudo required.
On my Linux desktop, I do not need to run nmap as root to do a local ping scan. If that's true on your system, then you should be able to just drop the sudo part of your nmap command, and be done with it.
There is a difference, though. nmap will perform a more thorough testing of each target when the -pS ping sweep is run by root. From an old nmap man page (emphasis added):
-sP (Skip port scan) .
[...]
The -sP option sends an ICMP echo request, TCP SYN to port 443, TCP ACK to port 80, and an ICMP timestamp request by default. When executed by an unprivileged user, only SYN packets are sent (using a connect call) to ports 80 and 443 on the target. When a privileged user tries to scan targets on a local ethernet network, ARP requests are used unless --send-ip was specified. [...]
Enable sudo for your Web server.
If you need this extra information (and it sounds like you do), you'd need to run nmap (or the Python script that calls it) with super-user privileges. I've never tried to force a Web server to do this, but I assume you would at least have to add your Web server's user to /etc/sudoers. Something like:
apache localhost=/usr/bin/nmap -sP
or:
httpd ALL=/usr/local/bin/nmap
...and so on, depending on the user name, where your nmap is located, how strictly you want to limit the arguments to nmap, etc.
Create an SUID executable to run nmap for you.
Alternatively (and I hate myself for recommending this --- there must be a better way) is to write a tiny SUID (Set User ID) program that executes only the nmap command you want. Here's a C program that will do it:
#include <stdio.h>
#include <unistd.h>
int main(void);
int main(void) {
int retval = 0;
char* const error_string = "ERROR: Failed to execute \"/usr/bin/map\"";
char* const nmap_args[] = {
"/usr/bin/nmap",
"-sP",
"192.168.1.0/24",
NULL
};
retval = execv("/usr/bin/nmap", nmap_args);
/* execv returns _only_ if it fails, so if we've reached this
* point, print an error and exit.
*/
perror(error_string);
return retval;
}
Save the above as something like nmap_lan.c, and compile with:
$ gcc -Wall -o nmap_lan nmap_lan.c
Then, move it to wherever you keep your Web site's scripts, and as root, change its ownership and permissions:
# chown root:root nmap_lan # Or whatever group name you use.
# chmod 4555 nmap_lan
The leading 4 sets the SUID bit. A color ls of the directory will probably show that file highlighted. The permissions should look like this:
# ls -l nmap_lan
-r-sr-xr-x. 1 root root 6682 May 23 03:04 nmap_lan
Any user who runs nmap_lan will be temporarily promoted to whoever owns the nmap_lan file (in this case, root) until the program exits. That's extraordinarily generous, which is why I hard-coded everything in that program... To change anything it does --- even just the IP range to scan --- you'll have to edit nmap_lan.c file, re-compile, and re-install.
I've tested nmap_lan on my command line, and it produces privileged-user nmap output when run by an unprivileged user who normally gets only limited output.
Comments on the Python script
In general, Python is vastly better at parsing shell arguments than the shell is (the default value for shell is False for a reason), so have your Python script do as much of the job as possible, including parsing the shell command, redirecting input, and redirecting output.
A major advantage of doing the work in Python is that failure to open, read, write, or close any of your files will result in an immediate crash and a stack trace --- instead of the silent failure you've been dealing with.
I'd rewrite that call command to use a list of explicitly separated arguments. You can handle the output redirection by passing an opened file stream to the stdout parameter. You can eliminate your last bit of shell redirection by having Python open your output file and write to it explicitly.
nmap_file='/home/pi/whohome.txt'
with open(nmap_file, 'wt', encoding='ascii') as fout:
subprocess.call(
['/usr/bin/nmap', '-sP', '192.168.1.0/24'], # Or just ['nmap_lan']
stdout=fout,
universal_newlines=True,
)
output_file='/var/www/html/website.txt'
with open(nmap_file, 'rt', encoding='ascii') as fin:
with open(output_file, 'wt', encoding='ascii') as fout:
for line in fin:
...
print('Output here', file=fout) # Add `file=...` to each print.
Also, unless you need that whohome.txt file for something else, you can eliminate it entirely by using check_output to store the output from the nmap command as a string, and then splitting it into separate lines. (The universal_newlines parameter also handles converting the bytes object into a str, at least in Python 3.)
lines = subprocess.check_output(
['/usr/bin/nmap', '-sP', '192.168.1.0/24'], # Or just ['nmap_lan']
universal_newlines=True
).split('\n')
output_file='/var/www/html/website.txt'
with open(output_file, 'wt', encoding='ascii') as fout:
for line in lines:
...
print('Output here', file=fout) # Add `file=...` to each print.
Note that I used with blocks to get the file closing for free.
(Finally, that series of if commands is crying out to be rewritten as a for machine in machines_dict: loop, with the strings you're searching for as the keys in that dictionary, and the output you want to print as the values.)

Related

No output on web page PHP while executing shell command

I am trying to execute Linux shell command from php but there is no output on web page. If I am trying to execute the php page from linux cosole its working fine.
PHP Code:
<?php
$result = shell_exec('asterisk -rx "core show channels concise"');
$ccount =shell_exec('asterisk -rx "core show channels count"');
echo $result;
echo $ccount;
?>
Above code is not giving any output on web page. But on linux console its woking. e.g.
[abc#host sysadminman]# php myfile.php
Asterisk control socket permissions can also be changed easily in /etc/asterisk.conf:
[files]
astctlpermissions = 0660
astctlowner = root
astctlgroup = apache
astctl = asterisk.ctl
First of all your question is incomplete as you not showing what is expected output. But aside from this you are doing a few common mistakes there.
First you are testing your script as root (# php ...) but your httpd is NOT serving your scripts as root. So your tests are useless. You should switch to right user (most likely www-data and then check to run your script from shell. Most likely it will fail for one of two common reasons - insufficient permissions to run asterisk program or incomplete $PATH not pointing to the place where asterisk is.
I agree to Marcin.
I would suggest you write script to execute those commands and put result to some storage (such as text or database). Use cron to run it in root. Then you read the data from storage on web page.
If you want real time response, you have to run cron all the time though it consume server resource. That is trade-off you have to consider. Its depends on what you wanna achieve from the web site.
Use sudo to run thoes commands as root or Asterisk user. You can configure sudo to allow execution without password to only specific commands.
check disable_functions in php.ini. Mb shell_exec just off for web server

Different answer from same script depending on caller (php exec() vs. console)

I run Bash-Scripts from PHP (5.4) with root permissions through a binary wrapper (see [Execute root commands via PHP), which works perfectly fine except for the following example. Additionally, I am using zfs-on-linux on CentOS7.
I prepared 2 simple example Bash-Scripts:
test_zfsadd:
#!/bin/bash
#ARGS=1
err="$(zfs create $1 2>&1 > /dev/null)"
if [ $? -ne 0 ]
then
echo $err
exit 1
fi
echo "OK"
exit 0
test_zfspart:
#!/bin/bash
#ARGS=1
msg="$(zfs get mounted $1 -H | awk '{print $3}')"
echo $msg
exit 0
When I call the according binaries from PHP with e. g.
<?php
$partition = 'raid1/testpart';
$ret = shell_exec("/path/test_zfsadd_bin $partition");
echo "<p>Return value: $ret</p>\n";
$ret = shell_exec("/path/test_zfspart_bin $partition");
echo "<p>Is mounted: $ret</p>\n";
the output is:
Return value: OK
Is mounted: yes
This looks good, but when I call 'test_zfspart_bin raid1/testpart' directly from console, I get the correct result which is
no
(means that the partition is NOT mounted, checked in /proc/mounts). So I get 2 different answers from the same script depending somehow on the context. I first thought it has something to do with the SUID-Bit, but calling the script in console with an unprivileged user works fine. If I try (as root)
zfs mount raid1/testpart
in console I get
filesystem 'raid1/testpart' is already mounted
cannot mount 'raid1/testpart': mountpoint or dataset is busy
which is weird. I also can't destroy the 'partition' from console, this works only from PHP. On the other hand, if I create a partition as root directly from bash and try to delete it via PHP, it doesn't work either. Looks like the partitions are somehow separated from each other by context. Everything gets synchronized again if I do
systemctl restart httpd
I think apache or PHP is keeping the zfs system busy to some extend, but I have absolutely no clue why and how. Any explanation or some workaround is much appreciated.
I figured it out myself in the meantime.. The problem was not the apache process itself, it was how it is started by systemd. There is an option called 'PrivateTmp', which is set to 'true' in the httpd service file by default (at least in CentOS7). The man page says
PrivateTmp=
Takes a boolean argument. If true sets up a new file system
namespace for the executed processes and mounts a private /tmp
directory inside it, that is not shared by processes outside of
the namespace. This is useful to secure access to temporary files
of the process, but makes sharing between processes via /tmp
impossible. Defaults to false.
This explains it all I think. The newly created zfs partition is mounted in this 'virtual' file system and is therefore invisible to the rest of the system, what is not desired in this case. The apache process is not able to mount or unmount file systems outside its namespace. After disabling the option everything worked as expected.

How to run executable from PHP website as a specific Windows user?

By default PHP runs under IUSR account. When executed directly:
$lastline = exec('D:\\MyProgram.exe', $output, $return_var);
It runs, but the program is unable to do it's tasks due to insufficient privileges. My question is: How do I run an executable under a specific Windows account from a PHP website?
I've tried:
When executed through Sysinternals PsExec:
$lastline = exec('D:\\PsExec.exe -u UserName -p Password -accepteula "D:\\MyProgram.exe"', $output, $return_var);
MyProgram.exe does not even execute at all. PHP throws empty output and return_var is -1073741502. I assume this is some sort of unhandled exception.
When executed through lsrunas:
$lastline = exec('D:\\lsrunas.exe /user:UserName /password:Password /domain:DOMAIN /command:"D:\\MyProgram.exe" /runpath:D:\\', $output, $return_var);
MyProgram.exe does not execute either. PHP throws empty output and return_var is 0.
Microsoft's built in runas command doesn't work because it doesn't accept password as a parameter.
Tried using different PHP functions like shell_exec, system and passthru
Tried creating a new Application Pool under IIS, to run the website under LOCAL SERVICE, SYSTEM, or a specific user.
EDIT: This is where I went wrong. It should work, but fails when Load User Profile setting is not enabled (step 3 in my answer).
Does anyone have working suggestions?
Note: All commands are working and tested through command line directly.
I kept digging and found out that the only thing that works is a dedicated application pool.
Create a new Application Pool under IIS
Set username and password in Advanced Settings > Identity > Custom account
Set Advanced Settings > Load User Profile to true (this one is important)
Choose this pool under Basic Settings of a site,
-or- for a better security:
. 5.Move all command-relatied code to one section within your website, convert it to application and apply that Application Pool to it. Then you can restrict any public access to that part and safely call that functionality from the other parts of your site.
Important note (!):
If you're running PHP via FastCGI, you must set fastcgi.impersonate = 0 in php.ini file.
Test batch file:
To test who is running the process you can save the following code to a *.bat file and call it from PHP.
#echo off
SET now=%date% %time%
SET out=[%now%] %userdomain%\%username%
echo %out%
echo %out% > D:\hello.txt
::msg * "%out%"
if %username%=="SpecificUser" (
exit /B 100
) else (
exit /B 200
)
Replace SpecificUser with your desired user name. Sometimes you'll see no output. The exit codes will help you then.
In case you can't see any output or exit code at all, this script will output the username to a text file. Replace D:\hello.txt with your desired path.

bash script execution from php and instantaneous output back to webpage

I have a collection of bash and Perl scripts to
develop a directory structure desired for deployment on linux box
(optionally) export code from svn
build a package from this source
This is working well from terminal. Now my client requests a web interface to this process.
e.g "Create New Package" button on certain page will invoke above steps one by one and return the output to user as script echos, not when the whole scripts executes.
Is it possible to send instantaneous output from bash script to webpage or php script which has invoked it through program execution functions (system, exec, passthru ... or any thing else that suite this process flow) ?
What is elegant why to do this ?
What security precautions I should take while doing such thing (if possible)?
Edit
After some search I have found part of the solution but still not working :
$cmd = 'cat ./password.txt|sudo -S ./setup.sh ';
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will read from
);
flush();
$process = proc_open($cmd, $descriptorspec, $pipes, './', array());
echo "<pre>";
if (is_resource($process)) {
while ($s = fgets($pipes[1])) {
print "Message:".$s;
flush();
}
while ($s = fgets($pipes[2])) {
print "Error:".$s;
flush();
}
}
echo "</pre>";
output: (webpage)
Error:WARNING: Improper use of the sudo command could lead to data loss
Error:or the deletion of important system files. Please double-check your
Error:typing when using sudo. Type "man sudo" for more information.
Error:
Error:To proceed, enter your password, or type Ctrl-C to abort.
Error:
Error:Password:
Error:Sorry, try again.
Error:Password:
Error:Sorry, try again.
Error:Password:
Error:Sorry, try again.
Error:sudo: 3 incorrect password attempts**
First issue I am having now is to pass sudo passoword
help please !
I would use a kind of master / slave design. The slave would be your perl / bash script, just doing a job (packaging, compiling, exporting code or so), and feed a log entry.
The master would be your php process. So the principle is the following: the master and the slave share a communication channel, and communicate asynchronously from that channel.
You could imagine a database like:
create table tasks ( id INT primary key, status INT, argument VARCHAR(100));
You php page should switch user choice, and filter input:
switch ($_GET['action']) {
case 'export':
$argument = sanitize($_GET['arg']);
add_task('export', $argument);
break;
case '...':
// ...
}
and the add_task function could be something like:
function add_task($action, $arg)
{
return $db->add('tasks', array($action, NEW_TASK, $arg);
}
The slaves could be run via a cron job, and query the database, feeding the progression of the task.
The pro are:
independant systems, ease the evolution.
if a client gets disconnected, the job is never lost
easier to secure
The cons are:
a little more complicated at the beginning.
less reactive, because of the polling time of the slaves running (for instance if they run every 5 minutes)
less output than the direct output of the command
Notice that you can then implement xml-rpc like triggers to run the slaves, rather than using a message passing system.
The simplest approach is to use shell_exec for your purpose. It executes a command via shell and returns the complete output as a string, hence you can display it on your website.
If this doesn't suit your purpose, because maybe you want some responses while waiting for the command to finish, check out the other program execution functions available in php (hint: there are a few good comments on the manual pages).
Keep in mind, when evoking commandline scripts this way, generated output will have the file owner, group and permissions of your webserver (p.e. wwwrun or whatever). If parts of your deployment need a separate owner, group and/or file permissions, you have to manually set them either in your scripts or after invoking shell_exec (chmod, chown and chgrp can deal with this in php).
About security:
Alot of web-based applications put that kind of function into a separate installation directory, and kindly ask you to remove this directory after installing. I even remember some of them shouting at admins quite persistent unless it is removed. This is an easy way preventing this script being invoked by wrong hands at a wrong time. If your application might need it after installing, then you have to put it into an area where only authorized users have access to (p.e. admin area or something similar).
You can use the Comet web application model to do real time updating of the results page.
For the sudo problem, as a quick and dirty solution I'd use restricted set of commands that the web server can execute without password. Example (add in /etc/sudoers):
apache ALL=(ALL) NOPASSWD: /sbin/service myproject *
which would allow apache to run /sbin/service myproject stop, /sbin/service myproject start and so on.
Take a look at Jenkins, it does the web part nicely, you'll only have to add your scripts in the build.
A better solution would be, as suggested by Aif, to separate the logic. One daemon process waiting for tasks and a web application visualizing the results.
Always use escapeshellarg and escapeshellcmd when executing system commands via PHP for security. It would also be suggested to have the user within a chroot'd directory as limited as possible.
You seem to have solved the issue of getting stdout and stdin to be outputted to your webpage by using proc_open. Now the problem looks to be executing the script via sudo.
From a security perspective, having a PHP app run a script as root (via sudo) makes me cringe. Having the root password in password.txt is a pretty huge security hole.
What I would do (if possible) is to make whatever changes necessary so that setup.sh can be run by the unix user that is running Apache. (I'm assuming you're running PHP with Apache.) If setup.sh is going to be executed by the webserver, it should be able to run it without resorting to sudo.
If you really need to execute the script as a different user, you may check into suPHP, which is designed to run PHP scripts as a non-standard user.
Provide automated sudo rights to a specific user using the NOPASSWD option of /etc/sudoers then run the command with the prefix sudo -u THE_SUDO_USER to have the command execute as that user. This prevents the security hole of giving the entire apache user sudo rights, but also allows sudo to be executed on the script from within apache.

Executing a command line using php shell_exec() for initiating a camera on the server

I am using opencv for initiating the camera on my arch linux. Its getting initiated and works well when I actually do it from the command line on the server itself.
I want to initialize it using php. I tried doing it using shell_exec from PHP.
My PHP file looks like:
<?php
$output=shell_exec('LD_LIBRARY_PATH=usr/local/lib ./a.out 0 2>&1 1>/dev/null');
echo $output;
?>
It gives this output:
ERROR: capture is NULL
I am running this through my windows web browser as a client and the opencv and the related files are on the server that is my arch linux.
I want to start the camera and capture images when I run this php file from the windows web browser, but when executed it throws the error as mentioned.
While this may work when you are SSHed into your server. The webserver user is most likely different than the user you login as. Popular user ids/groups that webservers run as on Linux machines are http, www-data, nobody, and others.
From this point you have two options.
You can make sure the script you are trying to run from PHP (and all of it's children, if any) is able to be run by the webserver user.
You can modify your /etc/sudoers file which gives the webserver user access to elevate permissions for that script only. (NOTE: This potentially opens up security holes so be careful).
To find out what user your webserver runs as execute this: ps aux
Take a look at the output and the first column in the output lists the user that that process is running at. Here's an excerpt of my webserver (nginx) on one of my boxes:
www-data 26852 0.0 0.0 29768 3840 ? S Jun04 0:50 nginx: worker process
You can see that nginx runs with the user www-data here. You can also execute the command with grep to help you find the process quicker. Grep will only show you those lines which match what you send to it. Here's an example: ps aux | grep nginx
Ok now that you know what user the webserver is running as, let's try giving that user access to the script. Let's say your script is foo and is located in /usr/local/bin. You would do the following commands:
chown www-data /usr/local/bin/foo
After changing ownership on the file try to rerun your command again from your PHP page and see if it works.
For completeness I also said you could give your webserver user sudo privileges to that file. To do that you would need to append the following line to the bottom of your /etc/sudoers file:
www-data ALL= NOPASSWD: /usr/local/bin/foo
Then your shell_exec command could switch to this:
shell_exec('sudo /usr/local/bin/foo');
Please keep in mind that doing this would allow your webserver user to run the script as root which is incredibly dangerous in a variety of situations. However, the camera may require elevated permissions to work. I'm not sure what the permissions requirements are on the camera setup you are trying to invoke.
Good luck. Hope this helps!

Categories