php, proc_open how pass multiple arguments - php

I am writing php code which will work under *nix systems. I need to call a python script with 5 arguments inside php code. Some of arguments
are user input so there could be any characters. I cannot figure out how pass arguments to a script. How would I separate arguments?. For example in this question you separate by end of line character, but in my arguments could be anything written by users. How to pass such arguments? any ideas?
EDIT:
I have idea of putting escape character in each quotes symbols ' or " before
passing arguments, on the other end I will get rid of escape character. Does json encoding do it?

The function you want is escapeshellcmd():
$arg1 = escapeshellarg($input1);
$arg2 = "foo";
$arg3 = escapeshellarg($input3);
$arg4 = "bar";
$arg5 = escapeshellarg("a string containing spaces and other *special* characters");
$proc = proc_open("python_prog $arg1 $arg2 $arg3 $arg4 $arg5", ...);
In the other thread, the program didn't take any arguments, the newlines were being used to separate items on standard input.

You're basically executing a command on the command prompt; you might want to familiarize yourself with a command prompt first.
Parameters are separated by spaces. So if your inputs have spaces in them, you have to put quotes around these inputs (I suggest single quotes; using a double quote will lead to environment variables being expanded, among other things).
So basically, you have to escape all the single quotes, line breaks, and carriage returns in your inputs, surround each of them with single quotes and append them to the command.
Warning: Security-wise, this whole thing is very problematic. If your escape mechanism is not bullet-proof, anyone would be able to execute a command on your server.
An alternate answer would be to write the inputs into a temporary file, and read from this file in your python script. If you have control over this script, I would strongly suggest you to do that.

Related

Using $_GET in system() function - security question

So let's say we have a following code:
<?php
$str = addslashes($_GET['str']);
$cmd = 'sometool "'.$str.'"';
system($cmd);
?>
Is it secure? Can I escape from double quotes somehow? The operating system in linux.
Purely theoretical consideration. I don't use it in my code ;)
It's not secure. You can still pass some arguments that will be malicious, i.e. execute other files in system.
$var = '$(sh file.sh)';
$str = addslashes($var);
$cmd = 'sometool "'.$str.'"';
system($cmd);
You should use escapeshellarg method for escaping shell arguments.
$str = escapeshellarg($_GET['str']);
$cmd = 'sometool ' . $str;
system($cmd);
Note that you have to use the argument $str as it is and that it will be a single argument. You must not surround it with quotes ' or double quotes "
Definitively not secure as is,
Like any user's input, you'll have to not only escape quotes, but also verify the conformity of the data being submitted and have to be strict on that.
For e.g.: if the "sometool" command is waiting for a path, you've to ensure that the user input is a valid path. If the path has to be in a restricted location, you've to check that restriction.
Assuming that your "str" has to be a valid string, you can begin with the filter input function.
https://www.php.net/manual/en/function.filter-input.php
Make sure to use the appropriate filters (for quotes, encoding, etc.) : https://www.php.net/manual/en/filter.filters.php.
It will not be possible to by-pass the escaping here.
This somehow ensure the conformity of the data, but still, you've to be sure that the command "sometool" itself will not be affected by any valid string characters or syntax, that's what makes it still unsecure to use.

Escaping spaces in variable while passing as a command line argument in php

I have a php file say abc.php, this is processing some command line arguments and at the end calling another php say def.php,
system("php /user/release/scheduler_test/def.php $name $final > ~/scheduler_test/logs/logs_$name.txt 2>&1 &")
The problem here is, the variable $final is having a huge string separated with spaces and since for php the space is the delimiter, it is not taking the entire $final as one argument.
I want to pass the value inside this $final variable as a single value. Can anyone tell me how? I hope I am clear.
That is why escapeshellarg is for. This does the escaping what you are exactly looking for.
A clearcut example from the PHP Docs..
<?php
system('ls '.escapeshellarg($dir));
?>
So escape your parameters/user-provided parameters using this function by wrapping this around it.

PHP SSH Lib, echoing to a file

I'm trying to create a PHP script that creates a file on a remote linux server through ssh, and echos the file contents into it.
However, I cannot figure out how to correctly and safely encode/escape the file contents, so that the contents don't get interpreted as commands.
I'm using phpseclib from here.
I've tried something like
echo $ssh->exec('sudo echo "' . escapeshellarg($newConfig) . '" > /etc/nginx/nginx.conf') . "\n";
but without success.
Thanks,
Steve
What about escapeshellcmd? Quoting the description of that vs escapeshellarg:
escapeshellarg() adds single quotes around a string and quotes/escapes
any existing single quotes allowing you to pass a string directly to a
shell function and having it be treated as a single safe argument.
...and...
escapeshellcmd() escapes any characters in a string that might be used
to trick a shell command into executing arbitrary commands. This
function should be used to make sure that any data coming from user
input is escaped before this data is passed to the exec() or system()
functions, or to the backtick operator.
Following characters are preceded by a backslash: #&;`|*?~<>^()[]{}$\,
\x0A and \xFF. ' and " are escaped only if they are not paired. In
Windows, all these characters plus % are replaced by a space instead.
I was going about this all wrong, I should have used Net_SFTP instead of NET_SSH for this sort of thing.

PHP exec() with double quote argument

I've been having trouble running a command using PHP's exec() function on Windows. Per a comment on PHP's site on exec():
In Windows, exec() issues an internal call to "cmd /c your_command".
My command looks like:
"path\to\program.exe" -flag1 attribute1 -flag2 attribute2 -flag3 "attribute3 attribute4"
Under regular execution of this command in my local command prompt, without the /c flag, the command runs fine. However, with the introduction of the /c flag, command prompt tells me that "The system cannot find the path specified."
I think the command prompt is interpreting the double-quoted argument as a path to another file, but that's the furthest I've gotten with this problem.
Does anybody have any ideas on how to get past this? Thanks!
I also encountered this issue, and the cause of it is, indeed, the internal use of "cmd /c" as described in your own answer.
I have done some investigation, and have found that this was resolved in PHP 5.3, which is why some commenters were unable to reproduce it.
It was fixed in the following commit:
https://github.com/php/php-src/commit/19322fc782af429938da8d3b421c0807cf1b848a#diff-15f2d3ef68f383a87cce668765721041R221
For anyone who still needs to support PHP 5.2, it is fairly easy to replicate the fix in your own code. Instead of:
$cmd = "...any shell command, maybe with multiple quotes...";
exec($cmd);
use
function safe_exec($cmd, &$output = null, &$result_code = null) {
if (strtoupper(substr(php_uname('s'), 0, 3)) == "WIN"
&& version_compare(PHP_VERSION, "5.3", "<"))
{
$cmd = '"' . $cmd . '"';
}
return exec($cmd, $output, $result_code);
}
$cmd = "...any shell command, maybe with multiple quotes...";
safe_exec($cmd);
This replicates the behaviour of PHP 5.3 and above, in the same way as in the above-linked commit.
Note that this applies to all shell commands, not just exec() as used in my example.
I've figured out the answer by myself...
After perusing cmd.exe's /? and trying to decipher that, I've noticed one important section:
If all of the following conditions are met, then quote characters on the command line are preserved:
No /S switch (Strip quotes)
Exactly two quote characters
No special characters between the two quote characters, where special is one of: & < >( ) # ^ |
There are one or more whitespace characters between the the two quote characters
The string between the two quote characters is the name of an executable file.
Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character. To negate this behaviour use a double set of quotes "" at the start and end of the command line.
It seems as though if there more than one pair of quotes at any time, quotation marks will be stripped from the second pair of quotes and on.
A relevant question: How do I deal with quote characters when using cmd.exe but not completely relevant, since PHP will not allow you to modify its exec() command by putting an /S flag on its call (which would definitely be a lot easier).
I've managed to work around this problem by directly changing directories with chdir() to the folder where the .exe is located, then chdir()'ing back to my original working directory. It's an extremely hacky solution, given the case that I'm lucky that I only have one set of arguments using double quotes. If anybody were to have multiple arguments using double quotes, I wouldn't know how to solve that...
Just a guess (I am not familiar with PHP on windows): maybe escape the quotes as " becoming ""?
"path\to\program.exe" -flag1 attribute1 -flag2 attribute2 -flag3 ""attribute3 attribute4""
Whatever the solution is, make sure that when there's some form of user-input that gets passed to this command as arguments that you use escapeshellarg and/or escapeshellcmd.
I hope it will help
escapeshellarg() — Escape a string to be used as a shell argument
escapeshellcmd() — Escape shell metacharacters

PHP exec() and spaces in paths

I'm executing the following in a PHP application:
$source = '/home/user/file.ext';
$output_dir = $this->setOutputString();
chdir('/home/ben/xc/phplib/bgwatcher-2011a/a01/');
exec('php bin/createjob.php $source $output_dir', $output);
return $output[0];
The problem is this: I have control over $source, but not $output_dir, which is a legacy Windows filesystem, and there are spaces in the path. An example $output_dir is:
/home/vol1/district id/store id/this_is_the_file.html
When inserting the output string into the exec() function, I have tried both:
addslashes($output_dir) and '"' . $output_dir . '"' to escape the entire output string. In the first case, the path gets concatenated to:
/home/vol1/districtthis_is_the_file.html
... where everything between the first space and the filename gets dropped. In the second case, exec() appears to throw a shoe and doesn't execute properly - unfortunately, the error message is getting lost in the machinery - I can provide it if it's absolutely necessary, but I'm also under time constraints to find a solution.
What's the solution, here? Do I sprintf() the entire string for exec()? I'm very confused as to why addslashes isn't working correctly to escape the spaces, and I assume it has something to do with sanitization with exec(), but I can't find any documentation to back it up.
Update: I've tried escapeshellarg() and preg_replace() without success. Thinking about this further, do I need to double-escape the path? Or escape the path and the command? If the path is being unescaped once by exec(), and once by PHP before it executes the command, does it stand to reason that I need to account for both escapes? Or is that not how it works?
I don't believe addslashes() does anything with spaces. escapeshellarg() might be what you want instead. Docs on escapeshellarg
From the PHP doc (here),
Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).
This won't do anything to the spaces. What you will need to do is use str_replace() to add slashes, like this:
$new_string = str_replace(" ", "\\ ", $old_string);
According to the PHP docs,
Returns a string with backslashes before characters that need to be quoted in database queries etc. These characters are single quote ('), double quote ("), backslash () and NUL (the NULL byte).
Looks like you'll have to preg_replace the spaces yourself.
Edit:
Even though this is the topic of another discussion, if performance is an issue, then after looking into it a little more, it seems that str_replace is actually quite a bit faster than preg_replace:
The test labeled "str_replace()" was
the faster by 0.9053 seconds (it took
10.3% the time.)
The first test took 1.0093 seconds. (preg_replace)
The second test took 0.104 seconds. (str_replace)
Benchmark found here.
I've used exec() with paths with spaces before, on both Windows and Linux hosts, and in both cases quoting the path worked perfectly for me.
That said, if you have no control over the safety of a shell argument, always run it through escapeshellarg() first!
You can very well use shell quotes, since that is what all exec commands run through:
exec("php bin/createjob.php '$source' '$output_dir'", $output);
It btw works not just for arguments, but also for the command itself:
exec('"/bin/echo" "one parameter"');
Use escapeshellcmd() anyway.
this works for me when using exec() with soffice(LibreOffice):
$file_name = "Some, file name.xlsx";
exec('/usr/bin/soffice --headless --convert-to pdf '."'".$file_name."'".' 2>&1', $output, $r);
You can use double quotes and escape character together to work out this.
$fileName = "filename with spaces.pdf";
exec("php bin/createjob.php >\"".$fileName."\" 2> error.log" , $output, $return);

Categories