PHP特征&缺陷对比函数&CTF考点

发布于 2024-04-10  188 次阅读


PHP常用过滤函数

一、== 与 ===

  • 松散比较:使用两个等号 == 比较,只比较值,不比较类型。
  • 严格比较:用三个等号 === 比较,除了比较值,也比较类型。
<?php
if(42 == "42") {
    echo '1、值相等';
}
 
echo PHP_EOL; // 换行符
 
if(42 === "42") {
    echo '2、类型相等';
} else {
    echo '3、类型不相等';
}
?>

以上实例输出结果为:

1、值相等
3、类型不相等

二、MD5函数缺陷绕过 ==弱对比 ===强类型对比

2.1 使用和进行变量比较:md5()==

md5 0e绕过0e开头的字符串在参与比较时,会被当做科学计数法,结果转换为0

比如将两个md5值进行弱类型比较:

md5('QNKCDZO') == md5(240610708)

MD5加密后会变成这个样子:

0e830400451993494058024219903391 == 0e462097431906509019562988736854

由于0e开头的字符串会转换为0,所以真正比较的过程会变成下面这样

0 == 0

返回结果为true,也就是说0e开头的md5值进行弱类型比较时,结果相等

if($_GET['name'] != $_GET['password']){
 if(MD5($_GET['name']) == MD5($_GET['password'])){
 echo $flag;
 }
 echo '?';
}
​
?name=QNKCDZO&password=240610708

2.2 当使用md5()和进行变量对比的时候===

数组绕过md5不能加密数组,传入数组会报错,但会继续执行并且返回结果为null

比如将两个md5值进行强类型比较:

md5(name[]=1) === md5(passwd[]=2)

由于md5函数无法处理数组,会返回null,所以md5加密后的结果是下面这样

null === null

结果返回true,也就是说数组的md5值进行比较时,结果相等

if($_GET['name'] != $_GET['password']){
 if(MD5($_GET['name']) === MD5($_GET['password'])){
 echo $flag;
 }
 echo '?';
}
​
?name[]=1&password[]=23

需要注意的是0e绕过只能绕过弱类型比较(==),而数组绕过不只可以绕过弱类型比较,还可以绕过强类型比较(===)

三、intval缺陷绕过

int intval( $var, $base )

函数作用:intval() 函数用于获取变量的整数值。 intval() 函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。

  • 如果 $var 以 0 开头,就使用 8进制
  • 如果 $var 以0x开头,就使用 16进制
  • 否则,就使用 10进制

intval()函数漏洞的绕过思路:

1)当某个数字被过滤时,可以使用它的 8进制/16进制来绕过;比如过滤10,就用012(八进制)或0xA(十六进制)。

if(10 != $_GET['i']){
    if(10 == intval($_GET['i'],0)){
        echo '漏洞利用成功';
    }
}else{
    echo 'NONONO';
}

2)对于弱比较(a==b),可以给a、b两个参数传入空数组,使弱比较为true。

if($_GET['a']!=$_GET['b']) {
    if (intval($_GET['a']) == intval($_GET['b'])) {
        echo '漏洞利用成功';
    }
}else
    echo 'NONONO';

3)当某个数字被过滤时,可以给它增加小数位来绕过;比如过滤3,就用3.1。

4)当某个数字被过滤时,可以给它拼接字符串来绕过;比如过滤3,就用3ab。(GET请求的参数会自动拼接单引号)

intval()函数转换字符串时,会判断字符串是否以数字开头;
​
//当字符串以数字开头,就会返回1个或多个连续的数字;
​
//当字符串以字母开头,就会返回0;

5)当某个数字被过滤时,可以两次取反来绕过;比如过滤10,就用~~10。

6)当某个数字被过滤时,可以使用算数运算符绕过;比如过滤10,就用 5+5 或 2*5。

四、strpos()函数,我们可以利用换行进行绕过(%0a)

strpos(string,find,start)

函数作用:strpos() 函数查找字符串在另一字符串中第一次出现的位置。(区分大小写)

函数语法: string必需。规定要搜索的字符串。 find 必需。规定要查找的字符串。 start可选。规定在何处开始搜索。

五、in_array第三个参数安全

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

in_array --- 检查数组中是否存在某个值

说明: 在 haystack 中搜索 needle ,如果没有设置 strict 则使用宽松的比较(相当于 == 判断)

<?php
$id =3 and 1=1;  // 定义变量 $id 为 3 并且判断 1=1(永远为 true)
$whitelist = range(1, 5);  // 声明一个从 1 到 5 的白名单数组
if (!in_array($id, $whitelist)) {  // 如果 $id 不在白名单中
    echo "你想搞事";  // 输出 "你想搞事"
} else {
    echo "你通过了";  // 输出 "你通过了"
}
?>
    这里in_array()也是没有设置第三个参数,会进行弱类型比较,会将3 and 1=1转化为3从而绕过了白名单,输出你通过了。
    
​
<?php
$id =3 and 1=1;  // 定义变量 $id 为 3 并且判断 1=1(永远为 true)    
$whitelist = range(1, 5);  // 声明一个从 1 到 5 的白名单数组
if (!in_array($id, $whitelist,true)) {  // 如果 $id 不在白名单中
    echo "你想搞事";  // 输出 "你想搞事"
} else {
    echo "你通过了";  // 输出 "你通过了"
}
?>   
    当我设置第三个参数为true时,此时会进行强类型检查。所以我们将上文第三行代码修改为:if (!in_array($id, $whitelist,true)),再执行就会输出:“你想搞事”。//由于$id被赋值为true,而不是预期的3,所以$id的类型不是整数,而是布尔值。由于true不在范围为$whitelist的整数数组中,因此in_array()函数会返回false,导致代码输出"你想搞事"而不是"你通过了"

六、preg_match

preg_match 函数用于执行一个正则表达式匹配

preg_match 只能处理字符串,如果不按规定传一个字符串,通常传一个数组进入,就会使 preg_match 失效,从而绕过该函数的检测。

<?php
 if(isset($_GET['num'])){ //如果存在名为num的GET参数,则执行后续的代码。
 $num = $_GET['num'];  //将num参数的值赋给变量$num。
 if(preg_match("/[0-9]/", $num)){  //然后通过正则表达式检查num是否包含数字,如果包含数字就输出"no no no!"
 die("no no no!");      
 }
 if(intval($num)){   ////如果$num能转换为整数,则输出"你通过了"。
 echo '你通过了';
 }
}
?>
    
?num[]=1 //你通过了

七、str_replace 无法迭代过滤

str_replace() 函数用于替换字符串中指定字符(区分大小写)

//简单的sql过滤
$sql=$_GET['s'];   //接收名为's'的GET参数,并将其赋值给变量$sql
$sql=str_replace('select','',$sql);     //然后,使用str_replace函数将$sql中的'select'字符串替换为空字符串,然后将结果输出。
echo $sql;      //这样的话,如果's'参数中包含'select'字符串,它会被删除,然后输出结果。
​
//?s=sselectelect 

关于代码审计的系列文章分享

GitHub - hongriSec/PHP-Audit-Labs: 一个关于PHP的代码审计项目

後悔しない
最后更新于 2024-04-11