escapeshellcmd

(PHP 4, PHP 5, PHP 7, PHP 8)

escapeshellcmd β€” Π­ΠΊΡ€Π°Π½ΠΈΡ€ΡƒΠ΅Ρ‚ мСтасимволы ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строки

ОписаниС

function escapeshellcmd(string $command): string

Команда escapeshellcmd() экранируСт Π»ΡŽΠ±Ρ‹Π΅ символы Π² строкС, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Ρ‹ для ΠΎΠ±ΠΌΠ°Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ΠΎΠ±ΠΎΠ»ΠΎΡ‡ΠΊΠΈ ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹Ρ… ΠΊΠΎΠΌΠ°Π½Π΄. Π­Ρ‚Π° функция Π΄ΠΎΠ»ΠΆΠ½Π° Π±Ρ‹Ρ‚ΡŒ использована, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ Π»ΡŽΠ±Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅, Π²Π²ΠΎΠ΄ΠΈΠΌΡ‹Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΌ, Π±ΡƒΠ΄ΡƒΡ‚ экранированы ΠΏΠ΅Ρ€Π΅Π΄ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Π΅ΠΉ ΠΈΡ… функциям exec() ΠΈΠ»ΠΈ system() ΠΈΠ»ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Ρƒ "ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ апостроф".

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ символы Π±ΡƒΠ΄ΡƒΡ‚ экранированы ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠ³ΠΎ слСша: &#;`|*?~<>^()[]{}$\, \x0A ΠΈ \xFF. Π‘ΠΈΠΌΠ²ΠΎΠ»Ρ‹ ' ΠΈ " ΡΠΊΡ€Π°Π½ΠΈΡ€ΡƒΡŽΡ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² Ρ‚ΠΎΠΌ случаС, Ссли ΠΎΠ½ΠΈ Π²ΡΡ‚Ρ€Π΅Ρ‡Π°ΡŽΡ‚ΡΡ Π½Π΅ ΠΏΠΎΠΏΠ°Ρ€Π½ΠΎ. Π’ Windows всС этим символам, плюс ! ΠΈ % ΠΏΡ€Π΅Π΄ΡˆΠ΅ΡΡ‚Π²ΡƒΠ΅Ρ‚ символ ΠΊΠ°Ρ€Π΅Ρ‚ΠΊΠΈ (^).

Бписок ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ²

command

Команда, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ заэкранируСт функция.

Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΡ‹Π΅ значСния

Ѐункция Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΡΠΊΡ€Π°Π½ΠΈΡ€ΠΎΠ²Π°Π½Π½ΡƒΡŽ строку.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ #1 ΠŸΡ€ΠΈΠΌΠ΅Ρ€ использования Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ escapeshellcmd()

<?php

// НамСрСнноС Π΄ΠΎΠΏΡƒΡ‰Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠ³ΠΎ количСства Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ²
$command = './configure '.$_POST['configure_options'];

$escaped_command = escapeshellcmd($command);

system($escaped_command);

?>

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ

Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅

Π€ΡƒΠ½ΠΊΡ†ΠΈΡŽ escapeshellcmd() слСдуСт ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π°Π΄ всСй ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строкой, Π½ΠΎ ΠΎΠ½Π° всё Π΅Ρ‰Ρ‘ позволяСт Π°Ρ‚Π°ΠΊΡƒΡŽΡ‰Π΅ΠΌΡƒ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠ΅ количСство Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ². Для экранирования ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° вмСсто Π½Π΅Ρ‘ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ escapeshellarg().

Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅

Ѐункция escapeshellcmd() Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΡΠΊΡ€Π°Π½ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π΅Π»Ρ‹, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡ‚Π°Ρ‚ΡŒ ΠΏΡ€ΠΈΡ‡ΠΈΠ½ΠΎΠΉ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ Π² ОБ Windows с путями Π½Π°ΠΏΠΎΠ΄ΠΎΠ±ΠΈΠ΅: C:\Program Files\ProgramName\program.exe. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ ΠΌΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡ€ΡƒΡŽΡ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ Ρ„Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΎΠΌ ΠΊΠΎΠ΄Π°:

<?php

$cmd
= preg_replace('`(?<!^) `', '^ ', escapeshellcmd($cmd));

Π‘ΠΌΠΎΡ‚Ρ€ΠΈΡ‚Π΅ Ρ‚Π°ΠΊΠΆΠ΅

  • escapeshellarg() - Π­ΠΊΡ€Π°Π½ΠΈΡ€ΡƒΠ΅Ρ‚ строку для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π² качСствС Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π° ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строки
  • exec() - ВыполняСт внСшнюю ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ
  • popen() - ΠžΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚ Ρ„Π°ΠΉΠ»ΠΎΠ²Ρ‹ΠΉ ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ процСсса
  • system() - ВыполняСт внСшнюю ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ ΠΈ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅Ρ‚ Π²Ρ‹Π²ΠΎΠ΄
  • ΠžΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ исполнСния
οΌ‹Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ

ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΡ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ 7 notes

up
9
nicholas at nicholaswilson dot me dot uk ΒΆ
15 years ago
There is a quirk to be aware of regarding use of echo. If you have a command which you want to execute which takes input from STDIN, you would normally do:

<?php $output = shell_exec("echo $input | /the/command"); ?>

Unfortunately, this is a *bad idea* and will make your script unportable, providing a very hard-to-trace bug on some systems. Depending on how the server is set up, /bin/sh will either call /bin/bash or /bin/dash, and these have very different versions of echo. Never use echo; use printf instead which is consistent. How do you escape for printf? Do this:

<?php
$input = 'string to be passed *exactly* to the command';
//Escape only what is needed to get by PHP's parser; we want
//the string data PHP is holding in its buffer to be passed
//exactly to stdin buffer of the command.
$cmd = str_replace(array('\\', '%'), array('\\\\', '%%'), $input);
$cmd = escapeshellarg($cmd);

$output = shell_exec("printf $cmd | /path/to/command");
?>

For the paranoid, this torture test verifies that both shell escaping and printf's own escaping are handled correctly. Use with confidence!

<?php

$test = 'stuff bash interprets, space: # & ; ` | * ? ~ < > ^ ( ) [ ] { } $ \ \x0A \xFF. \' " %'.PHP_EOL.
        'stuff bash interprets, no space: #&;`|*?~<>^()[]{}$\\x0A\xFF.\'\"%'.PHP_EOL.
        'stuff bash interprets, with leading backslash: \# \& \; \` \| \* \? \~ \< \> \^ \( \) \[ \] \{ \} \$ \\\ \\\x0A \\\xFF. \\\' \" \%'.PHP_EOL.
        'printf codes: % \ (used to form %.0#-*+d, or \\ \a \b \f \n \r \t \v \" \? \062 \0062 \x032 \u0032 and \U00000032)';

echo "These are the strings we are testing with:".PHP_EOL.$test.PHP_EOL;
$cmd = $test;
$cmd = str_replace(array('\\', '%'), array('\\\\', '%%'), $test);
$cmd = escapeshellarg($cmd);

echo PHP_EOL."This is the output using the escaping mechanism given:".PHP_EOL;
echo `printf $cmd | cat`.PHP_EOL;

echo PHP_EOL."They should match exactly".PHP_EOL;
?>
up
1
trisk at earthling dot net ΒΆ
21 years ago
This function does not work as shown in the php.net examples.

If you put your encoded filename into double-quotes as they suggest, then it will break on certain characters in filenames, such as ampersand.

For example if you have a filename called "foo & bar.jpg" and you use this function on it, your resulting filename when double-quoted will produce this and not be found:

"foo \& bar.jpg"

If you need to have a single argument where spaces are included then do not use this function with added double-quotes, use escapeshellarg() which encloses the whole string in single quotes.

I do not understand which purpose this particular function is intended for.  I can't see any use for it, unless you pass it through another function and convert spaces " " to "\ ", which would allow you to use the string directly on the command line.
up
1
docey ΒΆ
21 years ago
the main reason for quoting a command is that it not multiple  command can be joined. i don't know for sure if this is the right syntax but remeber that this can do some nice security breaks. here's one way of how to know exactly what your trying to break into for.

normal any user on linux can view almost any directory so:
ls / -als will print a complete list of any file in the linux filesystem including its size, security and hidden files as well.

now the output would only become known to php and never will the user be able to view this data unless the php script would actual start to print it out. like passtru does!! but a good php coder knows never to use passtru unless not otherwise possible. 

but what would happen if you can direct the output from ls also from that same commandline to a file in the webroot most webserver still default their base-webroot to /var/www/ so storing it there in text file to download it later and you can simply take coffee while checking wich files can be read by php security mode and then simply use the cp command to copy those to the webroot and download them to your own hard-disk. without a list of the files you can only guess where to copy from! and thats harder then guessing the root password. 

so if the first command was quoted it is not possible to attach another command because of a syntax error. think of all the thinks you can do once you got a complete list of every file on the filesystem. including mounted once via NFS and others. security starts at keeping the door hidden.

also another nice command for hanging the webserver can be "php <?php while(true){ exec('ls / -als'); }; ?>" this keeps creating a file list on the entire filesystem wich not only keeps the hard-disk(s) bussy but also memory and cpu   wich must store the returned list. so keeping in mind not all command accepted from users can be used blind. 

actualy never accept any command from external sources only proven built-in predefined commands should be executed.
up
0
carlos at wfmh dot org dot pl dot REMOVE dot COM ΒΆ
15 years ago
Mind it does not escape ! (exclamation mark). So if you want to i.e. printf() commands for later use in shell (i.e. by pasting to the console) you need to escape all exclamation marks or shell will try to process ! as history reference. This approach shall suffice:

<?php $scaped = str_replace('!', '\!', escapeshellarg( $str ) ); ?>
up
-2
ceejay at trashfactory dot de ΒΆ
20 years ago
Well guys, i find it very hard that escapeshellarg and escapeshellcmd are forcely run when passing a command to exec, system or popen, when safe_mode is turned on.

Right now, i did not find any working solution to pass commands like this:
cmd -arg1 -arg2 "<BLA varname=\"varvalue\" varname1=\"varvalue1\" />"

it is just the case, that the parameter for arg2 which is a string that looks like an HTML-Tag with various attributes set, all attributes of the string in arg2 gets splitted by the whitespaces within. this wont happen with safe_mode turned off, so it must be one of the escapefunctions, that breaks functionality. 

In order to circumvent this, i have made a temporary solution, which dynamically creates a skriptfile (by fopen), which just contains the whole command with arguments, and then execute that skriptfile. i dont like that solution, but in the other hand, safe_mode cannot be easily turned off on that server.
up
-3
abennett at clarku dot edu ΒΆ
19 years ago
I've got a php script that needs to pass a username and password via exec to a perl script.  The problem is valid password characters were getting escaped...

Here's a little perl function I wrote to fix it.

sub unescape_string {
      my $string = shift;
      # all these interpolated regex's are slow, so if there's no
      # backslash in the string don't bother with it
      # index() is faster then a regex
      if ( ! index($string,'\\\\') ) {
         return $string;
      }
      my @characters = ('#', '&', ';', '`', '|', '*', '?', '~', '<', '>', '^', '(', ')',
                        '[', ']', '{', '}', '$', '\\', ',', ' ', '\x0A', '\xFF' );
      my $character;
      foreach $character (@characters) {
         $character = quotemeta($character);
         my $pattern = "\\\\(" . $character . ")";
         $string =~ s/$pattern/$1/g;
      }
      return $string;
}

Hope this is useful.
up
-3
Leon ΒΆ
19 years ago
This function is great -- except when you need to legitimately use an escaped character as part of your command.  The code below leaves the parts of the command that are enclosed within single quotes alone, but escapes the rest eg:

"echo Never use the '<blink>' tag ; cat /etc/passwd"
becomes:
"echo Never use the '<blink>' tag \; cat /etc/passwd"
and not:
"echo Never use the '\<blink\>' tag \; cat /etc/passwd"

i.e, we really want the ';' escaped, but not the HTML tag.  I really needed the code below in order to run the external ImageMagick's 'convert' command properly and safely...

<?php

// Escape whole string
$cmdQ = escapeshellcmd($cmd);

 // Build array of quoted parts, and the same escaped
preg_match_all('/\'[^\']+\'/', $cmd, $matches);
$matches = current($matches);
$quoted = array();
foreach( $matches as $match )
    $quoted[escapeshellcmd($match)] = $match;

// Replace sections that were single quoted with original content
foreach( $quoted as $search => $replace )
    $cmdQ = str_replace( $search, $replace, $cmdQ );

return $cmdQ;

?>