Unix `at` command via PHP - php

Here's my code to create a new at job...
system('echo \'php -f /path/to/my/php/file.php\' | at 1700');
I thought this would be simple and would just work but alas, nothing happens!
When I run echo \'php -f /path/to/my/php/file.php\' | at 1700 via ssh everything works as expected.
Is this a permissions problem? I.e PHP isn't allowed to create new at jobs?

Just FYI you have to make sure that you are allowed to execute system commands from your PHP scripts.
A lot of servers have this feature disabled.
However, if you want to turn this back on I believe you can do so in the php.ini file, you would have to turn safe_mode to off.
When allowing user-supplied data to be passed to this function, use escapeshellarg() or escapeshellcmd() to ensure that users cannot trick the system into executing arbitrary commands.
If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
Note: When safe mode is enabled, you can only execute files within the safe_mode_exec_dir. For practical reasons, it is currently not allowed to have .. components in the path to the executable.

Related

Run script with subprocess.run() in Python, blocking all file read/write attempts

I have a web server running Apache 2 on Raspbian Stretch. It is going to be a programming contest website, where users can send code via a HTML form, that sends their source code to PHP via a POST request. PHP then runs (using exec()) a Python script with arguments such as the submitted code path. The script then executes the code (using subprocess.run()) with a custom input and compares it to an expected output. All of that works just fine.
However, I want to make sure no one is going to send malicious code to overwrite files such as index.php, or read the expected outputs, for example. I'd like to know if there is any way to prevent an application that is being executed by subprocess.run() from reading, creating and writing to files other than stdin, stdout and stderr.
I have tried using Docker but didn't have success, as when I build and run the Dockerfile using PHP's exec() it reaches step 2/4 and just stops. My Dockerfile should copy the script, the code and the expected outputs to an image, cd to the new location and execute the code, but that is not very relevant since I want to avoid Docker as it isn't working properly.
I am considering using a chroot jail, but I am still looking for other less-complicated ways of doing that.
This is the PHP code I'm using. It calls the Python 3 code verifier (variables are retrieved from a HTML form and from a SQL query, those are not relevant):
$cmd = "python3 verify.py $uploadedFile $questionID $uploadedFileExtension $questionTimeLimit 2>&1";
And this is the Python 3 code that executes the submitted code:
def runCmd(args, vStdin, timelimit = 10):
p = subprocess.run(args, stdout = subprocess.PIPE, stderr = subprocess.PIPE, input = vStdin, encoding = 'utf-8', timeout=timelimit)
vStdout = p.stdout
vStderr = p.stderr
if vStdout.endswith('\n'):
vStdout = vStdout[:-1]
if vStderr.endswith('\n'):
vStderr = vStderr[:-1]
return vStdout, vStderr
...
# Assuming it is a .py file
# Its path is given by PHP's exec.
runCmd(['python3', sys.argv[1], 'simulatedinput.in', int(sys.argv[4]))
The combination of both programs works just fine. It runs the code with a simulated input, compares the stdout with the expected output and returns a status string to the PHP code. However, if the code sent has a malicious bit of code, such as
open('/var/www/html/contest/index.php', 'w').write('oops!')
the index.php file will be overwritten.
All I need is a way of executing the user-sent code in a way that its attempts to read or write to files (other than stdin, stdout and stderr) are denied.
Any thoughts?
doing this securely, to put it simply, is difficult. it's relatively easy to escape even a chroot jail if you're not really careful about how you set it up. basically the Unix security model isn't built to make this sort of thing easy and it's assumed that things are mostly cooperative
docker would probably be my suggestion, but there are other lighter weight solutions like chroot (but they'd probably still have the ability to do naughty things with the web server's network connection) or maybe something like firejail
with docker you'd probably want to create a single minimal docker image/container containing Python and whatever libraries are appropriate. you'd then use volumes to make the user supplied code appear inside the VM at runtime. you don't want to be creating containers all the time, that would entail lots of cleanup work
see https://security.stackexchange.com/q/107850/36536 for some more info on using docker as a sandbox, basically there are still lots ways out of it unless you're careful

why is my "at job" not executing my php script when created through a php webpage?

$output = shell_exec('echo "php '.$realFile.'" | at '.$targTime.' '.$targDate.' 2>&1');
print $output;
Can someone please help me figure out why the above line isn't doing what it's supposed to be doing? The idea is for it to create an 'at' job that will execute a php script. If I switch to the user apache(which will ideally control the at function when the php file is complete) I can run
echo "php $realFile.php" | at 00:00 05/30/17
and it'll do EXACTLY what I want. The problem is in the above snippet from my php file it will not create the at job correctly. when I do a at -c job# on both of them the job made from my file is about a 3rd the length missing the User info and everything. It basically starts at PATH= and goes down. Doesn't include HOSTNAME=, SHELL=, SSH_CLIENT=, SSH_TTY=, USER=. I assume it needs most of this info to run correctly. The end output (below)is always the same though it just doesn't have any of the top part for some reason. Let me know if you need more info. I didn't want to paste all of my code here as it contains job specific information.
${SHELL:-/bin/sh} << 'marcinDELIMITER0e4bb3e8'
php "$realFile".php
marcinDELIMITER0e4bb3e8
It doesn't seem to be a permission issue because I can su to apache and run the exact command needed. The folder the files are located in are also owned by apache. I've also resulted to giving each file I try to run 777 or 755 permissions through chmod so I don't think that's the issue.
I figured out a coupe ways around it a while back. The way I'm using right now is an ssh2 connect to my own server as root and creating it that way. No compromise as you have to enter the password manually each time. Really bad work around. The main issue is that apache doesn't have the correct permissions to do everything needed for the AT job so someone figuring that out would be awesome. Another option I found on a random webpage would be to use sudo through the php script, but basically the same minus having to reconnect to your own server. Any other options would be appreciated.
Reading the manual and logs would be a good place to start. In particular:
The value of the SHELL environment variable at the time of at invocation will determine which shell is used to execute the at job commands. If SHELL is unset when at is invoked, the user’s login shell will be used; otherwise, if SHELL is set when at is invoked, it must contain the path of a shell interpreter executable that will be used to run the commands at the specified time.
Other things to check are that the user is included in at.allow, SELinux is disabled and the webserver is not running chrrot.

Why won't shell_exec execute files but does execute simple commands?

Is there any reason why I can not complied files in PHP's shell_exec/exec/system function?
Example of something that does work in command line and PHP's shell_exec function:
<?php
$data = shell_exec("ls");
echo $data;
?>
Example of something that does not work in PHP's shell_exec function but will work in command line (I can confirm that):
<?php
$data = shell_exec("./c-compiled-file argv1 argv2 argv3");
echo $data;
?>
Is there anything I can do on my server so this will work? I've looked everywhere and no solutions I found fixed the problem. The compiled file is in the same directory as the PHP script as well, it just won't execute it. Also if you're asking, yes I have tried this with SSH2 and it still will not execute.
Also PHP is not in safe mode and NO functions are disabled.
Some common glitches when executing external commands from PHP that work fine from shell:
Command uses relative paths but PHP is launched from an arbitrary location:
Use getcwd() / chdir() to get/set working directory
PHP and shell run with different user credentials. This is often the case when PHP runs through a web server.
PHP and shell run different commands. Many people call stuff like exec("foo $bar") and doesn't even check what "foo $bar" contains.
No error checking is done. The bare minimum is to capture and print standard output, standard error, status code and, of course, all PHP error messages including warnings and notices.
You can redirect stderr to sdtout
You can use a PHP function that allows to capture more information, such as exec()
The web server is disallowed to execute the command at operating system level.
Lookout for SELinux or similar tools.
Just a guess, but the binary you're trying to execute might not have the proper permissions. Prepeding it with ./ in the command line forces it to execute, but PHP probably strips that for security purposes. Try this:
chmod +x c-compiled-file
You want to use system in the second case, and not shell_exec.
system executes an external program and displays the output.
shell_exec executes a command via shell and returns the complete output as a string.
and for good measure:
exec simply executes an external program.
Furthermore you want to make sure your external program is executable and (though you have stated it, I'll restate this) has execute permissions for the user which is running the web server. You also want to make sure the directory your external program is running in has the ability to write to its directory or /tmp or whatever output directory you have set.
Finally you should always use absolute paths for executing things like this in cron or php or whatever... so don't use ./c-compiled-file argv1 argv2 argv3, but instead use /home/username/c-compiled-file argv1 argv2 argv3 or whatever the full path is.

curl not executing properly when invoked by php

So I want to execute a bash command from PHP on my web server. I can do this using shell_exec. However, one of the commands I want to execute is curl. I use it to send a .wav file to another server and record its response. But when invoked from PHP, curl doesn't work.
I reduced the error to the following small example. I have a script named php_script.php which contains:
<?php
$ver=shell_exec("curl -F file=#uploads/2013-7-24-17-31-43-29097-flash.wav http://otherserver");
echo $ver
The curious thing is that when I run this php script from command line using php php_script.php, the result I get is
Status: 500 Internal Server Error
Content-type: text/html
However, if I run curl -F file=#uploads/2013-7-24-17-31-43-29097-flash.wav http://otherserver directly, I get the response I was expecting:
verdict = authentic
(Edit:) I should probably mention that if I put some bash code inside the shell_exec argument which does not contain curl, the bash command executes fine. For example, changing the line to $ver = shell_exec("echo hello > world"); puts the word "hello" into the file "world" (provided it exists and is writable). (End edit.)
Something is blocking the execution of curl when it is invoked from PHP. I thought this might be PHP's running in safe mode, but I found no indication of this in php.ini. (Is there a way to test this to make 100% sure?) What's blocking curl and, more importantly, how can I bypass or disable this block?
(And yes, I realize PHP has a curl library. However, I prefer to use commands I can run from the command line as well, for debugging purposes.)
cheers,
Alan
The reason is the administrative privileges when you run the command directly you are running it as root and thus the command gets executed. But, when you run the command through PHP it runs as an user. By, default user has not the privileges to run the shell_exec commands.
You have to change the settings of shell_exec through CPanel/Apache config file. But, it is not recommended to provide the shell_exec access to the user as it help hackers to attack on server and thus, proper care should be taken.
It would be more appropriate to use the curl library provided in PHP.

calling Windows program from PHP file (through command-prompt)

I have tried calling a windows program several ways and I have gotten the same result each time.
The program opens up on my machine (without a GUI) but never closes each means that the browser is forever loading.
Though when executing the query string manually through the command line prompt the program closes. Not only that, but the program doesn't actually execute
(it is just launched i.e. there aren't any results).
I just want to know the proper way of starting a program with switches through PHP.
Here is the query string that works (closes the program after executing):
"C:\Program Files (x86)\Softinterface, Inc\Convert PowerPoint\ConvertPPT.exe" /S
"C:\Users\Farzad\Desktop\upload\test.ppt" /T "C:\Users\Farzad\Desktop\upload\test.png" /C 18
If the program never closes, then PHP can't return a value from exec(). The program must close. Chances are there is a problem accessing your files on your desktop in this manner. It will be executed with whatever permissions the webserver has defined.
http://php.net/manual/en/function.exec.php
You might consider the advanced functionality of proc_open(). It will give you access to all the necessary pipes, but I don't think that will help you in this situation.
If the target directory on your Windows machine is C:\Program Files (x86)\Softinterface, Inc\Convert PowerPoint\ConvertPPT.exe, you need to double-quote the directories that have space character within them.
To translate it into php terms, it should be like this:
$directory = 'C:\"Program Files (x86)"\"Softinterface, Inc"\"Convert PowerPoint"\ConvertPPT.exe';
$command = $directory . ' enter your arguments here';
exec($command, $output, $return_var);
// if $return_var == 0, you hit the jackpot.
The physical directory where your Windows desktop is stored belongs to your user profile folder. That means that other users (including the one Apache runs as, which is typical "Local System") won't have the appropriate permissions to read and write files on it. While you can adjust your Apache set-up to make it run with your own user, Farzad, it's more common to put web applications in an entirely different directory tree. It may happen that ConvertPPT.exe just stalls because it's trying to write a file at a location where it's not allowed. I suggest you create a top folder directory and make sure it's world-writeable (once finished, you can tighten these permissions if you like).
Once you discard (or confirm) that the issue is caused by lack of appropriate credentials, make sure you are escaping your command and arguments properly. See this link:
http://es2.php.net/manual/en/function.exec.php#101579
One more thing you can try is to close PHP sessions before issuing the call to exec():
http://es2.php.net/manual/en/function.exec.php#99781
You have probably run into this bug: http://bugs.php.net/bug.php?id=44994
which has been bothering me for ages, even today, on PHP 5.3.5.
It seems like there is some kind of deadlock between the error output of the program and the apache error log file handle into which the program is redirected to write its stderr output, making the program be stuck for ever until the apache processes are killed.
Also, when using passthru, or system, or the backtick operator, there's an intermediary "cmd.exe" process that is used to run the program in an invisible console, and I have seen this cmd process getting stuck without even running the program.
I don't really have a solution as of now, and it seems the bug, even though reproduced by many people, hasn't been resolved.

Categories