第一部分: 特征函数
接触到几个常用的函数:
\=\=
\=\=\=
md5
intval
strpos
in_array
preg_match
str_replace
php用这些函数实现过滤一些代码,漏洞可能有一些特性,利用这些特征代码进行对比;账号密码对比;强制检测数据类型等都会用到这些函数,接下来具体讲解一下各个函数的使用:
- = 赋值
- == 弱类型对比,但是不会对比类型,这种存在缺陷绕过,举个例子,判断一个值是否==1,这种情况用户输入的+1,1.0,1a都会被当成正确的情况进行处理,这点在数据库中也是,当输入的值是字符串类型的时候,1a和1对比时,1a只取1和1对比,和后面的a无关,利用这点,如果1a=1报错,说明是数字型,如果1a=1不报错,说明时字符型,在这里时为了说明如果不进行强类型的对比1后面可以接入字符串。
让我们来拓展一下,如果只进行弱类型对比,假设密码用md5进行加密了存放在数据库中,存放的密码和用户输入的密码的md5加密值中间用/=/=判断而不是用/=/=/=判断的后果,就是可能产生hash碰撞,如下图所示。:
这样就达到了绕过的目的。 - . === 对比,会对比类型
此时可以使用数组进行绕过,对于数组name[]来说$_GET[‘name’]获取到的值就是null,让我们来看个例子:
如果输入的值是name[]=1&password[]=2的结果就是获取到的值为null,在图中上面的部分,判断的值也就相等了,从而达到绕过的目的。这种情况在ctf中比较常见。
- intval()函数:就是把其他类型的东西变成10进制int类型的数据。具体用法参考:https://www.runoob.com/php/php-intval-function.html
这个东西,可以用来过滤sql注入,比如id=1 and 1=1 ,使用这个函数,只能获取到id = 1 ,同时根据文档里的这段话:语法
int intval ( mixed $var [, int $base = 10 ] )
参数说明:
$var:要转换成 integer 的数量值。
$base:转化所使用的进制。
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
如果字符串以 “0” 开始,使用 8 进制(octal);否则,
将使用 10 进制 (decimal)。
666的十六进制的值是0x29a,此时如果用户输入的值是0x29a,这两个值仍然是相等的关系,也会输出flag。
-
strpos()函数:查找字符串在另一字符串中第一次出现的位置(区分大小写)。
参考:https://www.runoob.com/php/func-string-strpos.html
-
in_array() 函数:
参考:https://www.runoob.com/php/func-array-in-array.html
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
参数 描述
needle 必需。规定要在数组搜索的值。
haystack 必需。规定要搜索的数组。
strict 可选。如果该参数设置为 TRUE,则 in_array() 函数检查搜索的数据与数组的值的类型是否相同。
如果strict的位置是true可以检查类型相当于/=/=/=,如果是false的话,就相当于/=/=,这种情况就相当于存在绕过,例如1e在[1,2,3]中也算是出现过,1d/+1/1.0也出现过。
- preg_match()函数:用于执行一个正则表达式匹配
参考:https://www.runoob.com/php/php-preg_match.html
这里可以使用数组绕过:
并且这里最后获取的值num的值是1,这是chatgpt给我的解释:
$_GET['num'] 是一个数组,而数组的首个元素是1,可以成功转换为数字。
因此,intval($num) 的结果是1,条件 if(intval($num)) 为真,导致 echo $flag; 被执行。
后面我去手动输出验证了一下,该段代码中:
<?php
$flag = 'success ----';
if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/",$num)){
die("no no no");
}
if(intval($num)){
echo "===================";
echo intval($num);
echo $flag;
}
}
?>
$_GET[‘num’]获取到的值是Array,而intval( $_GET[‘num’])的值是1,所以可以输出最后正确的结果。
- str_replace()函数:以其他字符替换字符串中的一些字符(区分大小写)。
//参考:https://www.w3school.com.cn/php/func_string_str_replace.asp
<!DOCTYPE html>
<html>
<body>
<?php
echo str_replace("world","Shanghai","Hello world!");
?>
</body>
</html>
//输出结果:Hello Shanghai!
常常用于过滤sql
这种过滤无法迭代执行,例如输入sselectelect,最终还是会出现select的字样。
正则表达式中/i 是否区分大小写;/m 是否接受换行,可以使用%0a %20进行绕过。
%0a:代表换行符(Line Feed),ASCII码值为10。在URL编码中,换行符被表示为 %0a。
%20:代表空格字符。在URL编码中,空格通常被表示为 %20。
这类ctf题不仅可以参加比赛,而且还可以学习实际代码,将来实战很有帮助,不要单纯做ctf狗。
循环过滤一般用的就是正则表达式,顺序过滤用的是str_replace(),但是这个有比较特殊的用法,如下列代码:
<?php
$s = "..//..../";
$search = array('../','./');
$dir = str_replace($search,'',$s);
echo $dir;
?>
首先会把$s中先将…/的结果替换完之后,再将结果中的./替换成空,这是我从实验中得来的,所以这种替换是按照数组的顺序进行替换的。