前言:
新手记录一下学习过程,把一些知识点做成笔记打包到博客中,本文主要说一些关于php函数的漏洞缺陷,此类漏洞大多出现于CTF以及php白盒测试中,所以本文内容会偏向于CTF方向。主要讲解一些基础的php函数漏洞,如有不对的地方还请各位师傅对该文章进行补充和斧正
1.弱类型的安全比较
= 、 == 、 === 三个等号的比较中各有一些基础的绕过方式
= 是属于单纯的赋值
而 == 比较时则不会对比类型
=== 则会对比类型是否相等,通常会先将字符串类型转化成相同,再比较。
例如:
- 当 a==b 时 会先将a,b的值转换成同类型再比较值
- 当 a===b 先判断a,b类型,若相同,则比较值,若不相同,则返回false
2.弱类型比较之MD5对比
我们来看看以下代码:
$flag ="abcdewoshishabi";
if ($_GET['name'] != $_GET['password']){
// 如果账号不等于密码
if (MD5($_GET['name']) == MD5($_GET['password'])){
//就会进行MD5比对,这样我们就可以利用这个缺陷进行绕过
echo $flag;
}
echo '?';
}
我们可以看到,他的逻辑关系是:
利用get请求的name和password接收值,此时第一个 if 判断,是要求不相等的,但是第二个 if 判断的时候,又要求他们的MD5值相等。
绕过原理:
这里我们可以通过 科学计数法进行绕过,因为计算器表达10的幂一般是用E或e
如:2 760 000 = 2.76×10^6 = 2.76e6
所以0e,无论后面跟什么值,都是0
0e开头常见的为字符串MD5值:
加密后的密文 原值
QNKCDZO 0E830400451993494058024219903391
s214587387a 0E848240448830537924465865611904
s878926199a 0E545993274517709034328855841020
s155964671a 0E342768416822451524974117254469240610708 0E462097431906509019562988736854
接下来的php函数漏洞介绍,我采用CTF题来为大家讲解
3.str_replace函数
题目链接:青少年CTF训练平台 | 原中学生CTF平台 | 青少年CTF
我们先看看题目代码:
<?php
include('flag.php');
error_reporting(0);
if(isset($_GET['value'])){
$value = $_GET['value'];
$replace_value = str_replace("滕子京","", $value);
$replace_value = str_replace("巴陵郡","", $replace_value);
$replace_value = str_replace("岳阳楼","", $replace_value);
if($replace_value === "庆历四年春,滕子京谪守巴陵郡。越明年,政通人和,百废俱兴,乃重修岳阳楼,增其旧制,刻唐贤今人诗赋于其上,属予作文以记之。"){
echo $flag;
}else{
echo "你写错了!我不管,你就是写错了!";
}
}
注: 这里的isset() 函数 咋们看不懂可以不管
它主要是通过 if判断指定变量是否存在且不为 NULL,则返回 TRUE,否则返回 FALSE。
replace()的函数缺陷: 只会去除替换一次,当我们采用双写输入 selectselect 时 他就会输出select,所以该函数只能过滤一次。
分析:
这里可以看到它文件包含了flag.php
然后就是 进入 第一个 if 循环 它首先要求 利用vulue变量进行 GET接收
之后就进行 if 的判断条件 为ture时 所要运行的内容
我们能看到,它这里是替换了 3个 内容 替换完之后 我们可以看到,第二个if循环的内容还是完整的,所以我们就能通过 replace()的函数缺陷进行绕过
构造playload:
file.php?value=庆历四年春,滕滕子京子京谪守巴巴陵郡陵郡。越明年,政通人和,百废俱兴,乃重修岳岳阳楼阳楼,增其旧制,刻唐贤今人诗赋于其上,属予作文以记之。
此时replace 只会过滤第一个巴陵郡,过滤了之后 还会在此组合,所以就能进行绕过。
4.in_array函数
我们先来了解一下这个函数的基础用法
语法格式:
in_array(search,array,type)
参数说明:
search是要搜索的值,array是被搜索的数组,这两个参数是必须的;
type是可选的,如果设置为true,则检查搜索的数据与数组的值的类型是否相同,当search的值是字符串时,开启type搜索区分大小写。
简单来说这个函数就是从数组中去搜索特定值,而这个函数大多数的漏洞就存在于 type 是否设置为true 。
刨析:
<?php
$flag="aabadadada";
$arr1 = [1];
if(isset($_GET['i'])){
if($_GET['i'] !== "1"){
if (in_array($arr1)){
echo $flag;
}
else{
echo "Error! Check error!";
}
}
else{
echo "Error! Invalid value!";
}
}
?>
我们可以看到,上面的代码in_array函数是没有设置type值,所以这里就存在弱类型的比较,它是不会比较是否为同类型,所以当我们输入1a的时候也会成功输出flag
5.strpos
$flag= 'adsdada';
$x = '666';
$y = $_GET['h'];
if (strpos($x,$y,"0")){
// 参数后面加个0的话就是转换成8进制,就输入的内容为666的8进制,但如果在前面加上0x,那就可以转换成16进制
echo $flag;
}
strpos() 函数 是用来查找字符串中第一次出现的位置
主要的漏洞一般出现在是否有赋值0的情况
对于strpos()函数 我们可以利用换行进行绕过: %0a
6.CTF习题讲解
[BJDCTF 2020]easy_md5
当我们打开时只有一个访问框
老步骤,先F12 看看源码发现什么也没,然后抓个包
随便输入数据,然进行抓包分析:
不难发现在 Response 包中的 Header 中存在
Hint 为:hint: select * from 'admin' where password=md5($pass,true)
我们尝试进行绕过:
传入 password=ffifdyop 即可绕过
原理:
执行 PayLoad 后会跳转到:
这里我们一样老规矩,先F12看看
有收获,我们看到注释那有PHP函数的漏洞,flag也许就在那,我们可以尝试进行绕过
<!--
$a = $GET['a'];
$b = $_GET['b'];
if($a != $b && md5($a) == md5($b)){
header('Location: levell14.php');
-->
分析:
a和b变量 分别利用 get请求接收
此时第一个循环要求我们满足 a 不等于b 并且 a和b的md5值必须相等
这里我们可以通过 科学计数法 0e开头的md5值进行对比,即可绕过
构造 playload :
http://node4.anna.nssctf.cn:28987/levels91.php?a=QNKCDZO&b=240610708
即可绕过
执行 PayLoad 后会跳转到:http://node4.anna.nssctf.cn:28987/levell14.php
分析:
变量都为POST方式进行接收
并要求 param1 和 param2 的数据不能相同,但是 MD5 后进行强类型比较后必须相同;
这里我们可以利用数组绕过
Paylaod:
param1[]=1¶m2[]=2
即可得到flag
[NSSCTF 2022 ]babyphp
好家伙,上来直接给代码,不多说,我们分析一波
这里有四个 if 判断,也就是说,我们满足 4个条件 即可获得flag
<?php
highlight_file(__FILE__);
include_once('flag.php');
if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a'])){
if(isset($_POST['b1'])&&$_POST['b2']){
if($_POST['b1']!=$_POST['b2']&&md5($_POST['b1'])===md5($_POST['b2'])){
if($_POST['c1']!=$_POST['c2']&&is_string($_POST['c1'])&&is_string($_POST['c2'])&&md5($_POST['c1'])==md5($_POST['c2'])){
echo $flag;
}else{
echo "yee";
}
}else{
echo "nop";
}
}else{
echo "go on";
}
}else{
echo "let's get some php";
}
首先我们看第一个条件:
if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a'])){
它这里要求,我们的 a 变量 为post方式发送,并且 不能输入 0-9的数字
那我们直接利用数组将其绕过 a[]=666
第二和第三个条件:
if(isset($_POST['b1'])&&$_POST['b2']){
if($_POST['b1']!=$_POST['b2']&&md5($_POST['b1'])===md5($_POST['b2'])){
这里我们可以看到,b1和b2变量也都是用post接收,第一层 if 判断的要求是 赋值b1 和 b2
随后它要求 b1 和 b2 的内容不相同 并且 b1 和 b2 的md5 值 相同,我们可以很清楚看到, md5对比是利用了 三个等于号
所以我们这里 继续使用 数组绕过 b1[]=566&b2[]=666
第四个条件:
if($_POST['c1']!=$_POST['c2']&&is_string($_POST['c1'])&&
is_string($_POST['c2'])&&md5($_POST['c1'])==md5($_POST['c2'])){
echo $flag;
最后是第四个 if :
c1 和 c2 不相等 并且 c1和c2 要求是字符串 且 md5值相等
那我们这里数组就明显是不能用了,我们这里可以看到 md5值是 两个等于号的弱比较
那么我们则可以使用 0e 的方法进行绕过
c1=QNKCDZO&c2=240610708
然后把上面几个playload串联起来即可获得flag
b9bd75e5-d2dc-4068-9eef-7ccea0c95a30