通过shell_exec将二进制数据发送到php-cgi

问题描述 投票:1回答:3

我有一个脚本,向post发送/usr/bin/php-cgi请求。处理纯文本时脚本工作正常,但当数据为二进制时失败:

$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');

我收到以下错误:

sh:-c:第1行:在寻找匹配的“”时出现意外的EOF sh:-c:第5行:语法错误:意外的文件结束

我想这是因为我试图发送的数据是二进制的,必须以某种方式编码/转义。

就像我说的,如果数据是纯文本,上面的代码是有效的:

$post_data = "data=sample data to php-cgi";
$size = strlen($post_data);

我也尝试使用base64_encode()对数据进行编码,但后来又面临另一个问题;必须从接收脚本中解码数据。我想也许我可以编码base64中的数据然后添加一些内容或mime类型标题来强制php-cgi二进制文件进行对话?

另一个问题是我喜欢将数据作为附件发送,因此我认为我们必须将CONTENT_TYPE设置为multipart/form-data; boundary=<random_boundary>并将CONTENT_DISPOSITION设置为form-data,但我不确定如何从命令行设置这些标头。

php post cgi sh shell-exec
3个回答
2
投票

您正尝试通过shell_exe上传二进制文件以发布内容。 shell_exe不接受二进制编码。如果您将图像数据更改为base64,那么您的问题将得到解决。但是你会遇到另一个问题,即如何识别提交的文本/字符串,即文本或图像。目前,我发现无法确定提交的值是图像还是文本。

既然,你想发布图像和数据,我建议你使用CURL并提供通过CURL提交图像和数据的方法,我也使用它:

$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;

1
投票

您还可以将所有发布数据存储在临时文件中,然后将该文件存入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);

1
投票

最后,我得到了这个工作,解决方案是发送一个base64编码的请求,其中还包含一个常量命名字段,如ORIGINAL_QUERY_URI到一种gateway文件,反过来将解码请求并将其弹回到它的原始目的地。

基本上,服务器执行此操作:

  1. 编码base64中的任何接收文件数据
  2. 添加名为ORIGINAL_REQEUST_URI的表单数据字段,并将原始URL作为值
  3. 基于上面组装编码为multipart/form-data的有效http请求体
  4. 使用shell_exec将此数据发送到将解码内容的gateway文件

这是我用来将所有内容发送到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');

然后在gate.php文件中我解码了数据并包含了theORIGINAL_REQUEST_URI字段指向的文件。

// 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']);  
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.