php命令执行函数

php原生的命令执行函数如下,一共7个

  • system
  • passthru

(语法与system类似)

  • exec(需要输出函数才能执行,******print**** exec($_POST["cmd"]);?> **)
  • shell_exec

(与exec一样,语法如下echo shell_exec(“tac flag.php”);

  • popen

(格式语法如下popen(command,mode)

command 必需。规定要执行的命令。
mode 必需。规定连接模式。
可能的值:
+ r: 只读。
+ w: 只写 (打开并清空已有文件或创建一个新文件)

  • proc_open(与popen语法类似)
  • pcntl_exec(只能适用于Linux的函数,语法为****** print**** shell_exec($_POST["cmd"]); ?>**)
  • dl
  • xx(反引号里面的内容也会被当作os命令执行)

php命令执行漏洞

本质:用户输入无过滤或者无效过滤,拼接到了系统中

面试难题:如何防御php的命令注入漏洞

好,那么我们在这之前先去了解一下php命令执行漏洞特点

  • 没有给命令传入参数的方法
  • 字符串拼接的危害

命令执行过滤函数

escapeshellcmd

escapeshellarg

回到问题上

那么,首先我们要明确一点:所有的安全措施都不能影响网站自身的任何正常业务

以这段代码为例,这是用于处理图片大小的代码

1
2
3
4
5
6
<?php
$x=$_GET[('x')];
$y=$_GET[('y')];
$filname=$_GET[('filename')];
exec(command:"convert -crop {$x}*{$y} {$filname}");
?>

如何修,假如上waf进行过滤函数,那么必然会影响到业务的正常,那么最简单的方式就是转义

1
2
3
4
5
6
<?php
$x=intval($_GET[('x')]);
$y=intval($_GET[('y')]);
$filname=$_GET[('filename')];
exec(command:"convert -crop {$x}*{$y} {$filname}");
?>

那么又以这段代码为例,这是一段用于处理邮件的代码

1
2
3
4
5
6
<?php
$to_email=$_POST[('email')];
$title=$_POST[('title')];
$content=$_POST[('content')];
exec(command:"mail --title $title --content $content --to-email $to_email");
?>

邮件里面内容有很多,如果用waf过滤不合理,转义肯定会影响到业务,但是php有自带的原生函数用于处理命令执行函数

escapeshellcmd和escapeshellarg的区别

php中只能使用escapeshellarg和escapeshellcmd进行命令参数的过滤

区分escapeshellarg和escapeshellcmd的角色差异

escapeshellcmd

只要输入的内容涉及到shell命令的关键字就会用”"进行转义

官方注释为

escapeshellcmd:

(PHP 4, PHP 5, PHP 7)

shell 元字符转义

string escapeshellcmd ( string $command )

escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义;_**反斜线(\)会在以下字符之前插入: &#;`|*?~<>^()[]{}$, \x0A 和 \xFF;’ 和 “ 仅在不配对儿的时候被转义;在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替**_

概述:

1.确保用户只执行一个命令

2.用户可以指定不限数量的参数

3.用户不能执行不同的命令

escapeshellarg

以tail -n 5 etc/passwd这条指令为例子,这条命令的作用是在Linux中以从结尾处打开etc/passwd并显示尾部5行,但是假设在用户输入时,开发者对输入处做了预处理而且使用了escapeshellarg函数,那么用户实际输入的值为tail -n ‘’ etc/passwd

官方注释:

escapeshellarg:

(PHP 4 >= 4.0.3, PHP 5, PHP 7)

把字符串转码为可以在 shell 命令里使用的参数

string escapeshellarg ( string $arg )

escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含 exec(), system() 执行运算符

概述:

1.确保用户只传递一个参数给命令

2.用户不能指定更多的参数一个

3.用户不能执行不同的命令

(3月28日更新,这俩函数并非是不可绕过的,如果php的版本非常的古早还是能绕过的,https://www.anquanke.com/post/id/107336#h3-15

如果在实战中开发者将escapeshellarg和escapeshellcmd调用错误,例如escapeshellarg处用escapeshellcmd做过滤,这两个函数作用颠倒,就有可能挖到意外的洞,而且这两个函数并不是万能的,还是可以绕过的,例子:cve 2016 10033

https://www.leavesongs.com/PENETRATION/PHPMailer-CVE-2016-10033.html

这个cve就是典型的函数调用错误导致的cve漏洞,其本质是phpmailer这个函数错误引用sedmail原生函数导致的

那么肯定就会有人说了,那么escapeshellarg就是绝对安全的吗,我的回答是并非

可以参考一个漏洞,那就是gitlist0.6.0远程命令执行漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
public function searchTree($query, $branch)
{
if (empty($query)) {
return null;
}

$query = escapeshellarg($query);#正如你所见,用户的输入内容仅仅只是被做了简单的处理

try {
$results = $this->getClient()->run($this, "grep -i --line-number {$query} $branch");
} catch (\RuntimeException $e) {#然后就拼接到了命令中
return false;
}

其中,$query是搜索的关键字,$branch是搜索的分支,知道grep函数的应该就能看懂,从分支里面搜索关键字。

为什么会出现这个漏洞,首先这里经过escapeshellarg处理后就会变成能正常执行的shell参数,如果用户输入的$query = –open-files-in-pager=id;是作为-e的参数去执行,那就是正常的,也就是说这只是一个字符串,一个附带的参数值,就像这样

1
git grep -i --line-number -e '--open-files-in-pager=id;' master

但–open-files-in-pager=id;如果直接作为grep的参数,那就可以执行命令

1
git grep -i --line-number  --open-files-in-pager=id;  master

这里就是escapeshellarg函数特性’’导致的,根据这个函数特性还可以衍生出sql注入等诸多漏洞,因为其涉及到了系统,这里涉及到又一个只是点

你无法正常打开文件开头为–的文件,会报错,但是cat ./–x却可以,cat – “–x”也可以,/bin/cat ./–x也可以