I have a script that sends a post
request to /usr/bin/php-cgi
. The script is working fine when dealing with plain text, but fails when the data is binary:
$data = file_get_contents('example.jpg');
$size = filesize('example.jpg') + 5;
$post_data = 'file='.$data;
$response = shell_exec('echo "'.$post_data.'" |
REDIRECT_STATUS=CGI
REQUEST_METHOD=POST
SCRIPT_FILENAME=/example/script.php
SCRIPT_NAME=/script.php
PATH_INFO=/
SERVER_NAME=localhost
SERVER_PROTOCOL=HTTP/1.1
REQUEST_URI=/example/index.html
HTTP_HOST=example.com
CONTENT_TYPE=application/x-www-form-urlencoded
CONTENT_LENGTH='.$size.' php-cgi');
I get the following error:
sh: -c: line 1: unexpected EOF while looking for matching `"'
sh: -c: line 5: syntax error: unexpected end of file
I guess this is because the data I'm trying to send is binary and must be encoded/escaped somehow.
Like I said the above code works if the data is plain text:
$post_data = "data=sample data to php-cgi";
$size = strlen($post_data);
I also tried to encode the data using base64_encode()
but then I face another problem; the data must be decoded from within the receiving script. I was thinking that perhaps I could encode the data in base64
and then add some content or mime type header to force the php-cgi
binary to make the conversation?
One other problem is that I like to send the data as an attachment and therefore I think we must set CONTENT_TYPE
to multipart/form-data; boundary=<random_boundary>
and CONTENT_DISPOSITION
to form-data
, but I'm not sure how to set these headers from the commandline.
Finally I got this working, the solution was to send a base64
encoded request which also contained a constant named field like ORIGINAL_QUERY_URI
to a sort of gateway
file that in turn would decode the request and bounce it to it's original destination.
Basically, the server does this:
base64
ORIGINAL_REQEUST_URI
with the original url as valuemultipart/form-data
based on aboveshell_exec
to a gateway
file that will decode the contentHere is the command I used to send everything to php-cgi
:
shell_exec('echo "' . $body . '" |
HOST=localhost
REDIRECT_STATUS=200
REQUEST_METHOD=POST
SCRIPT_FILENAME=/<path>/gate.php
SCRIPT_NAME=/gate.php
PATH_INFO=/
SERVER_NAME=localhost
SERVER_PROTOCOL=HTTP/1.1
REQUEST_URI=/example.php
HTTP_HOST=localhost
CONTENT_TYPE="multipart/form-data; boundary=' . $boundary . '"
CONTENT_LENGTH=' . $size . ' php-cgi');
Then inside the gate.php
file I decoded the data and included the file pointed to by theORIGINAL_REQUEST_URI
field.
// File: gate.php
if (isset($_FILES)) {
foreach ($_FILES as $key => $value) {
// decode the `base64` encoded file data
$content = file_get_contents($_FILES[$key]['tmp_name']);
$content = base64_decode($content);
file_put_contents($_FILES[$key]['tmp_name'], $content);
}
}
// bounce to original destination
include_once($_POST['ORIGINAL_REQUEST_URI']);
You are trying to upload binary files through shell_exe to post the contents. shell_exe doesn't accept the binary encoding. If you change the image data to base64 then you problem would be solved. But you will get into another problem i.e. how to identify the submitted text/string i.e. text or image. Presently, I find no solution to identify the submitted value is image or text.
Since, you want to post the image and data, I would suggest you to use CURL and providing the way to submit the image and data through CURL which is used by me also:
$local_directory=dirname(__FILE__).'/local_files/';
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_VERBOSE, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible;)");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_URL, 'http://localhost/curl_image/uploader.php' );
//most importent curl assues @filed as file field
$post_array = array(
"my_file"=>"@".$local_directory.'filename.jpg',
"upload"=>"Upload"
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_array);
$response = curl_exec($ch);
echo $response;
You can also store all post data in a temporary file and then cat that file into php-cgi:
char file[20] = "/tmp/php"; //temp post data location
char name[10];
webserver_alpha_random(name, 10); //create random name
strcat(file, name);
int f = open(file, O_TRUNC | O_WRONLY | O_CREAT);
write(f, conn->content, conn->content_len); //post data from mongoose
close(f);
/* cat temp post data into php-cgi */
/* this function also takes care of all environment variables */
/* but the idea is understandable */
output = webserver_shell("cat %s | php-cgi %s", conn, request, file, request);
unlink(file);