安全专家讲述PHP函数安全特性
(一)PHP的强制转换
PHP中的强制转换要在转换的变量前加上用括号括起来的目标类型,还可以使用函数settype(),代码0x1.php如下
$a=$_GET[a];//?a = "1abc";
$int = (int) $a;
$float = (float) $a;
$string = (string) $a;
$array = settype($int,'array');
?>
还可以使用intval() floatval() strval() 函数进行强制转换,代码0x2.php如下
$a=$_GET[a];//?a = "1.23abc"
$int = intval($a);
$flo= floatval($a);
$string = strval($a)
?>
当我们需要对变量进行强制类型转换时,通过以上方法,如若使用不当均会导致安全漏洞。
(二)模拟测试
我们对漏洞代码进行模拟测试,intval强制转换示例代码0x3.php:
$a="123a and 1=1";
intval($a);
echo $a;
echo "";
$a_safe="123a";
$a_safe=intval($a_safe);
echo $a_safe;
echo "";
?>
运行结果:
123a and 1=1
123
$a并未被转换未整型,而是原样输出来了。相应的我们$a_safe才真正被强制转换了。
int强制转换示例代码0x4.php:
$b="123b and 1=2";
(int)$b;
echo $b;
echo "";
$b_safe="123b";
$b_safe=(int)$b_safe;
echo $b_safe;
echo "";
?>
运行结果:
123b and 1=2
123
$b并未被转换为整型,还是原样输出来了。相应$b_safe被转换。
把上述安全隐患带入PHP的流程控制
If 示例代码0x5.php:
$c="123.3c";
if(floatval($c)) {
echo $c."";
}
$c_safe="123.3c";
if($c_safe=floatval($c_safe)) {
echo $c_safe."";
}
?>
运行结果:
123.3c
123.3
我们可以看到第一个if判断条件并未将$c转换未浮点型数值,相应$c_safe被转换了。
switch 示例代码0x6.php:
$d = "123.3d";
switch(intval($d)){
case "123.3d" : echo $d."
";
case "123" : echo $d."It's not safe";
}
$d_safe = "123.3d";
$d_safe=intval($d_safe);
switch($d_safe){
case "123.3d" : echo $d_safe."
It's not safe";
case "123" : echo $d_safe."It's safe";
}
?>
运行结果:
123.3dIt's not safe
123It's safe
$d在switch中并未被转换为整型,还是原样输出。而$d_safe则被转换了。
while 示例代码0x7.php
$e=123e;
while(intval($e)){
echo $e."引发死循环";
}
?>
运行结果:
Parse error: parse error in D:\xampp\htdocs\80vul\src\0x7.php on line 2
while中的判断永远为真,引起死循环,但$e并未被强制转换未整型。
经测试,得出结论,由于变量都是经过强制类型转换后,返回的值并未赋给某个变量,所以均存在漏洞隐患。
实例演示,大家可以参照我博客上的《 PHP、MySQL注入总结》。
地址:Http://hi.baidu.com/menzhi007
二,引申PHP函数共性
引申示例1,当magic_quotes_gpc = Off,代码0x8.php如下:
$id = $_GET[id];
$id=htmlspecialchars($id);
echo $id."
";
$id_safe=$_GET[id];
$id_safe= htmlspecialchars ($id_safe);
echo $id_safe;
?>
提交URL:http://127.0.0.1/80vul/src/0x8.php?id=1' and 1=2'
运行结果:
1' and 1=2'
1\' and 1=2\'
引申示例2,再看个有意思的例子,代码0x09.php 如下:
$id = $_GET[id];
$id=htmlspecialchars($id);
echo $id."
";
$id_safe=$_GET[id];
$id_safe==htmlspecialchars($id_safe);//注意这里是双等号
echo $id_safe;
?>
运行结果:
1' and 1=2'
1' and 1=2'
居然没有过滤,两个双等号并不是赋值语句,所以还是不会起到过滤作用。
当我们提交 URL?id=a and 1=2时,变量 id并不会经过htmlspecialchars函数过滤,从而引发安全隐患。
引申示例3,代码0x10.php如下:
$str = 'one|two|three|four';
explode('|', $str, 2);
echo "
";
print_r($str);
$str_safe='one|two|three|four';
$str_safe=explode('|', $str_safe, 2);
echo "
";
print_r($str_safe);
?>
运行结果:
Array ( [0] => one [1] => two|three|four )
one|two|three|four
倘若我们不将返回结果赋值给一个变量名,将导致过滤毫无意义。
经测试,得出结论,分析PHP语言特性,PHP是一种弱类型语言,允许定义两个相同的变量名,后定义的变量将覆盖前定义的变量。若没有任何变量名指向这个变量时,PHP 垃圾回收机制(Garbage Collector),会将其在内存中销毁,防止内存溢出。__destruct() 析构函数,是在垃圾对象被回收时执行。unset 销毁的是指向对象的变量。我们得以扩充,所有经过函数过滤的变量,如果返回的结果未赋给另一个变量,那么这个变量将被PHP内存回收机制GC自动回收,其值并不会因其前函数的影响而变化。比如0x1.php示例中的代码“ (int)$b; ” 经过int强制转换的整型数据会放置于内存中,而后被内存回收,并未起到强制转换的作用。
此文引申于80vul的pch-001,强烈致谢80vul、Ph4nt0m,并向他们致敬。导致此安全隐患的是PHP函数共性,作为程序设计人员我们应尽可能的做好自己的编码规范,以避免此类隐患。当然本文中出现的错误疏漏之处,望大家予以指正。
三、实际利用
Wordpress 2_0_5 Trackback UTF-7 Remote SQL Injection
http://www.milw0rm.com/exploits/3095
参考文献:
http://www.80vul.com/pch/pch-001.txt
http://superhei.blogbus.com/logs/4255503.html