PHP is a sufficiently rich programming environment that it is not common that I truly need to execute external programs on the server on which it executes. However, every once in a while, this situation does come along, and for these, it is important to understand the options that PHP provides, what their differences are, and their relative strengths and weaknesses.
There are four primary choices for executing external programs in PHP:
- The
systemfunction. - The
execfunction. - The
shell_execfunction or its syntactic analogue, the backtick operator, ( ` ). - the
passthrufunction.
proc_, such as proc_open and proc_close, but these are quite advanced, and beyond the scope of this article.
We will first discuss the usage of these three functions in their most basic forms.
The system() Function
The system function in PHP takes a string argument with the command to execute as well as any arguments you wish passed to that command. This function executes the specified command, and dumps any resulting text to the output stream (either the HTTP output in a web server situation, or the console if you are running PHP as a command line tool). The return of this function is the last line of output from the program, if it emits text output.
For example:
// Windows Users replace "ls ..." with "dir ..."
$lastLine = system('ls /Users/marcwan');
echo "<br/>LastLine: $lastLine<br/>\\n";
The output of this command on my system is:
Desktop Documents Download Library Movies Music Pictures
Public Sites bin blah.php books make1.png make1.tiff media src
yici1.png yici1.tiff
LastLine: yici1.tiff
The return value of the function will be FALSE if the function completely fails to execute.
In addition to the return value of the system function, however, there is also the question of the return status of the program being executed (just as functions in PHP all have return values (NULL if you don’t explicitly choose one), all commands executed in most operating systems also have return values, typically a 32bit integer value). For those programs you are executing whose return status you wish to know, you can pass a second argument to the system function, which tells PHP where to put this value.
For example:
// define this variable here so that it can actually be used in the system fn
$returnValue = -1;
system('ls /Users/marcwan', $returnValue);
echo "Return Value: $returnValue<br/>\\n";
The output of this program will be:
Desktop Documents Download Library Movies Music Pictures
Public Sites bin blah.php books make1.png make1.tiff media src
yici1.png yici1.tiff
Return Value: 0
For most modern operating systems, a return value of 0 is considered a success, and other values indicate failure. Some programs will have a range of values to indicate the exact way in which they failed while others will just return 1 or -1 to indicate that they were unsuccessful.
The exec() Function
The system function is quite useful and powerful, but one of the biggest problems with it is that all resulting text from the program goes directly to the output stream. There will be situations where you might like to format the resulting text and display it in some different way, or not display it at all.
For this, the exec function in PHP is perfectly adapted. Instead of automatically dumping all text generated by the program being executed to the output stream, it gives you the opportunity to put this text in an array returned in the second parameter to the function:
// you define this variable here so that it exists for the call to exec
$output = null;
// Windows users: 'dir c:\\' or something similar
exec('ls /Users/marcwan', $output);
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";
Once again, on my machine, the following output is printed:
array (
0 => 'Desktop',
1 => 'Documents',
2 => 'Download',
3 => 'Library',
4 => 'Movies',
5 => 'Music',
6 => 'Pictures',
7 => 'Public',
8 => 'Sites',
9 => 'bin',
10 => 'blah.php',
11 => 'books',
12 => 'make1.png',
13 => 'make1.tiff',
14 => 'media',
15 => 'src',
16 => 'yici1.png',
17 => 'yici1.tiff',
)
Of course, instead of just directly emitting this output, we could instead choose to process it and only emit those files with a ’.png’ extension, for example:
foreach ($output as $fileName)
{
if (strtolower(substr($fileName, -4)) == '.png')
echo "<br/>$fileName\\n";
}
And once again, you can get the return code from the exec function as follows:
$output = null;
$returnValue = -1;
exec('ls /Users/marcwan', $output, $returnValue);
echo "Return Value: $returnValue<br/>\\n";
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";
The shell_exec() Function
Most of the programs we have been executing thus far have been, more or less, real programs1. However, the environment in which Windows and Unix users operate is actually much richer than this. Windows users have the option of using the Windows Command Prompt program, cmd.exe (See Figure 1). This program is known as a command shell.
Figure 1— The Windows Command Prompt
Similarly, Unix (and Mac OS X) users can run a command shell such as sh, bash, or csh (see Figure 2). These are typically quite advanced interactive systems that allow elabourate scripts such as for x in *.png; do echo $x; done to be executed.
Figure 2— bash on Mac OS X
On both Windows and Unix, the commands for the various command shells can be put into files and executed by these programs. On Windows, convention has it that these script file have the extension .cmd while on Unix it is not uncommon to see .sh.
For those sitautions where we want to run these shell scripts from within PHP, we can use the shell_exec function, as follows:
// lists all .bat files anywhere in the system
$output = shell_exec('FOR /R C:\\ IN (*.bat) DO echo %x'); // Windows
// lists all .png files in /Users/marcan only
$output = shell_exec('for x in /Users/marcwan/*.png; do echo $x; done'); // Unix
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";
On my system this produces:
'/Users/marcwan/make1.png
/Users/marcwan/yici1.png
'
PHP also has a syntactic operator called the backtick operator which does the exact same thing as the shell_exec function. Instead of calling the shell_exec function, you simply wrap the command in the backtick character, `, which is very different from the single quote character '.
// lists all .bat files anywhere in the system
$output = `FOR /R C:\\ IN (*.bat) DO echo %x`; // Windows
// lists all .png files in /Users/marcan only
$output = `for x in /Users/marcwan/*.png; do echo $x; done`; // Unix
echo "<pre>" . var_export($output, TRUE) . "</pre>\\n";
It is worth noting that I avoid this operator at all times as it is not easy to spot in code, and I like potential security trouble spots to be as visible as humanly possible.
The passthru() Function
One fascinating function that PHP provides similar to those we have seen so far is the passthru function. This function, like the others, executes the program you tell it to. However, it then proceeds to immediately send the raw output from this program to the output stream with which PHP is currently working (i.e. either HTTP in a web server scenario, or the shell in a command line version of PHP).
When would this be useful? When you are working with image files and want to execute a program to show or otherwise manipulate these files. A most simple example would be as follows:
/**
* Windows:
*/
header('Content-Type: image/bmp');
passthru('type c:\\Windows\\zapotec.bmp');
/**
* Unix
*/
header('Content-Type: image/jpg');
passthru('cat /Users/marcwan/yici1.png');
The result will be an image in your browser. This is extremely powerful if you want to use some programs to resize or otherwise manipulate image files and then just dump the output to the client. There are a number of interesting utilities that come with various JPEG source code distributions that do exactly this and are powerful tools.
Are We Done Yet?
Armed with this basic knowledge, we have a one major thing left to deal with before we go off into the real world and begin using these functions: Security.
Allowing users to specify any portion of the string you pass to the exec, system, shell_exec, or passthru functions is a huge security risk, and you need to plan carefully in advance before allowing any of this. |
For example, the following code is simply a recipe for disaster:
$output = shell_exec('cat /webserver/info/user_files/' . $_POST['user_info_file']);
echo $output
If a malicious user were to set the value of the user_info_file post variable to bob; cat /etc/passwd. The output of both files would now be dumped, which is definitely not in your best interests.
There are two key ways which we will get around this:
- We will design our web applications carefully for security. If we can design the web application in such a way that only we choose the file names and programs that execution functions work with, we have much less to worry about in terms of security. We can also do our best to isolate any files that we work with and try to limit damage whenever something unexpected occurs.
- For those cases where we decide that user input is absolutely unavoidable, we will filter the user input extremely heavily and make sure it is as safe as possible before passing it to any of these functions. I always do the following:
- Absolutely forbid the use of any slash characters, whether it be forward ( / ) or backward ( \ ). I also forbid any user strings from beginning with
X:, where X could be a valid drive letter on the system (Windows only, of course). - Use the
escapeshellargfunction to further make the parameters safer. I.e.$output = shell_exec("cat /web/users/info/" . escapeshellarg($processedUserInfoFile));
- Absolutely forbid the use of any slash characters, whether it be forward ( / ) or backward ( \ ). I also forbid any user strings from beginning with
A way in which we are helped by the operating system is that most web servers operate as a user with restricted permission, and thus can only do things that user is permitted to do.
One last security note we should mention here is that if your php.ini file has safe_mode set to On, then the shell_exec function and analagous backtick operator is not available.
Conclusion
We have covered a lot of ground in this article, but we should now have a solid understanding of the various execution functions available to us in PHP, as well as how they differ. While we should be extremely concerned about the security of running programs external to our web server, we can do so in a reasonably safe and controlled manner, which can only be a good thing for our web applications and their customers.
—
1 It is worth noting that dir, type, and many other commands on Windows are not programs, but actualy commands built into the cmd.exe program. Fortunately the system execution functions are smart enough to know to execute cmd.exe for you if you ask to execute one of them.
just ran into a problem using exec. It seems that when executing a command with exec only the main "group" will be used. User rights defined in additional groups assigned to the user are ignored.
E.g.:
Call "id" from shell you get
uid=1002(gate) gid=1002(gate) groups=20(dialout),1002(gate)
Call it from PHP with exec you get
uid=1002(gate) gid=1002(gate) groups=1002(gate)
So additional Group dialout is not assigned in the php environment.
Cheers
Faser
1. add the apache user to sudoers file:
www ALL = (ALL) PASSWD: ALL
2. give apache a password:
passwd www
3. use this function to execute command as the user
function execCmd($cmd, $user = 'root') {
$fhandle = popen("$this->SUDOCMD -v \n\n", "w");
fputs($fhandle, "mypassword");
fclose($fhandle);
$cmd = ereg_replace("\n\r","",$cmd);
$result = shell_exec("$this->SUDOCMD -u $user $cmd");
if (!is_null($result)) {
return ereg_replace("[\n \r]", "", $result);
} else {
return false;
}
}
This applications runs:
exec("/Applications/HexEditor.app/Contents/MacOS/HexEditor");
This one doesn't:
exec("/Applications/TextEdit.app/Contents/MacOS/TextEdit");
(I get a return value of 5 for this)
I have done a 'chown webuser /Applications/TextEdit.app/Contents/MacOS/TextEdit' to get the owner correct. The file permissions for the two executables are identical.
I have upped memory_limit in php.ini as well.
Any ideas?
I am using PHP on a web server. It is my intention to allow user input to be processed, sent to a program, and processed by that program accordingly.
I am writing a web application which turns user input into an image using an image generator... but the generator is a separate, complex program, well beyond generic GDImage libraries.
I have no other way of embedding this programs functions into my web application except through command line prompts.
<html>
<body>
<?php
header('Content-Type: image/jpg');
passthru('cat newsealb.jpg');
?>
</body>
</html>
This doesn't work. The browser error I get is:
The image “http://wwwtest.emailxl.com/~rain/passthru_image.php” cannot be displayed, because it contains errors.
I've tried it with various gifs, jpegs, and pngs
(with appropriate content-type tags). Always the
same error. All permissions and locations are
correct. I've also tried it on two different apache
servers, running PHP5 or above.
This is a problem in your code.
you want to do the following:
--- BEGIN FILE blah.php----
<?php
header('Content-Type: image/jpeg');
passthru('cat newsealb.jpg');
?>
--- END FILE blah.php------
The problem is that you added regular HTML display headers, namely <html> and <body>. If you don't add those, however, you have the correct output that a web browser will understand.
marc.
i have a problem with this script:
[start]
function playpause(){
<?php
/*exec("dir C:\\Windows\\system32\\cmd.exe /c C:\\winamp_ctrl\\playpause.bat")*/ //Old one, didnt work
$cmd = "c:/windows/system32/cmd.exe"; // path to cmd.exe
shell_exec($cmd . "C:/winamp_ctrl/ctrl.exe playpause");//Tested from another post, but it didnt work either :(
?>
}
<input type="button" value="Play/Pause" onclick="playpause()" />
[end]
anyone knows what im doing wrong?? Its supposed to control winamp, and its confirmed working with that command, but php wont execute it :/ Please help!
//Tobias "ToJa" Jansson
Ps.
This isnt the whole code, its the part that i have trouble with, and sorry if it's to long.
<?php
exec('tesseract des.tif output');
echo "finish";
?>
I call an OCR engine named tesseract from php to translate des.tif into output.txt plain text.It plays exactly in my Apache localhost. But in webserver, nothing happened.I don't know why? I used byethost-a free host server to test my work.
Plz help me!

