一、PHP语言弱类型简介
第一次打CTF比赛,关于php语言弱类型这块了解的还不够,所以写一篇笔记学习一下。
- 强类型是两个不同类型的变量不能用同一块内存存储
- 弱类型是两个不同的类型的变量可以用同一块内存存储
- PHP语言是弱类型的,简单来说就是数据类型可以被忽视的语言,类型可以一个变量赋不同数据类型的值
PHP里面也有一些强语言类型相关的强制定义数据类型的结构方法,比如PHP里面的== 和 ===的区别,==是松散比较,只比较值,不比较数据类型。
例1:
例2:
$c='123abc'; $d=123; if($c==$d){ echo "不比较数据类型"; } else echo "比较数据类型"; //输出为不比较数据类型 $c='123abc'; $d=123; if($c===$d){ echo "不比较数据类型"; } else echo "比较数据类型"; //输出为比较数据类型
但是,php 内核的开发者原本是想让程序员借由这种不需要声明的体系,更加高效的开发,所以在几乎所有内置函数以及基本结构中使用了很多松散的比较和转换,防止程序中的变量因为程序员的不规范而频繁的报错,然而这却带来了安全问题。
二、类型转换问题
1、比较操作符
- ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较。
- ==在进行比较的时候,会先将字符串类型转化成相同,再比较。
- 如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行
例1 Hash比较缺陷:
"0e132456789"=="0e7124511451155" //true"0e123456abc"=="0e1dddada" //false"0e1abc"=="0" //true在进行比较运算时,如果遇到了 0e\d + 这种字符串,就会将这种字符串解析为科学计数法。如果不满足 0e\d + 这种模式,就会当作字符串进行比较,所以不会相等。
例二 十六进制转换:
"0x1e240"=="123456" //true"0x1e240"==123456 //true"0x1e240"=="1e240" //false当其中的一个字符串是 0x 开头的时候,PHP 会将此字符串解析成为十进制然后再进行比较。
2、类型转换问题
当一个字符串当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.’,’e’,’E’ 并且其数值值在整形的范围之内该字符串被当作 int 来取值,其他所有情况下都被作为 float 来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,也就是字符串开头否则其值为 0。
例一:
$test=1 + "10.5"; var_dump($test);//float(11.5) $test=1+'-1.3e3'; var_dump($test);//float(-1299) $test=1+"bob-1.3e3"; var_dump($test);//int(1) $test=1+"2admin"; var_dump($test);//int(3) $test=1+"admin2"; var_dump($test);//int(1)
intval () 函数:intval () 转换的时候,会将从字符串的开始进行转换知道遇到一个非数字的字符。即使出现无法转换的字符串,intval () 不会报错而是返回 0。
例二:
var_dump(intval('2')); // int(2) var_dump(intval('3abcd')); // int(3) var_dump(intval('abcd'));// int(0)
3、bool欺骗
当存在 json_decode 和 unserialize 的时候,部分结构会被解释成 bool 类型
$json_str = '{"user":true,"pass":true}'; $data = json_decode($json_str,true); var_dump($json_str['ss']); //输出为 string(1) "{" 因为并不是字符串 if ($data['user'] == 'admin' && $data['pass']=='secirity') { print_r('logined in as bool'."\n"); } //输出为logined in as bool
unserialize示例代码
$unserialize_str = 'a:2:{s:4:"user";b:1;s:4:"pass";b:1;}'; $data_unserialize = unserialize($unserialize_str); var_dump($data_unserialize['user']);//bool(true) if ($data_unserialize['user'] == 'admin' && $data_unserialize['pass']=='secirity') { print_r('logined in unserialize'."\n"); } //输出结果为logined in unserialize
4、数字转换问题
var_dump("1" == 0.9999999999999999);//false var_dump("1" == 0.99999999999999999);//true
int 和 intval在转换数字的时候都是低的,小于等于的整数部分
print((int)'0.9999999999999');//0print((int)'1.8');//1intval 还有个尽力模式,就是转换所有数字直到遇到非数字为止
5、绕过函数问题
0x01.MD5(),sha1()
这个函数用于对字符串进行MD5加密
函数格式:md5(要加密的字符串,规定的输出格式)
绕过方法
通过传入数组去绕过,这两个函数不能处理数组数据,md5还有一个加密的问题
示例代码:
error'; else if (md5($_GET['name']) === md5($_GET['passwd'])) die('Flag: '.$flag); else echo 'Invalid passwd.
';}else echo 'Login first!
';?>
测试效果如下
SHA-1绕过也是一样。
0x02.md5加密相等问题
关于md5还有个科学计数法的问题,
绕过方式
php在处理0e开头,后接的字符都为数字的的字符串的时候,会将整个字符串解析为科学计数法,当有另一个0e开头的字符串和这个进行对比时,php会都当作0来处理
示例代码如下:
这里我们传入一个md5编码后开头是0e的字符串,网上找了一个,aabC9RqS,效果为:
0x03.strcmp()
这个函数的作用是比较两个字符串并且区分大小写
当字符串1大于字符串2就返回>0,当字符串1小于字符串2就返回<0,相等则返回0
函数格式: strcmp(第一个需要比较的字符串,第二个需要比较的字符串)
绕过方式
通过传入数组去绕过,该函数处理数组时会发生错误,strcmp会返回结果0,导致绕过
测试代码
0x04.json_decode()
这个函数用于解码json格式的字符串
函数格式 json_decode(需要解码的字符串)
绕过方式
当有不同类型的数据时,该函数会转换为同一类型比较,这里会把原有的数据的数据类型转换为和传入数据的数据类型相同
测试代码
flag == $flag) { echo "233"; } else { echo "haha"; }}else{ echo "hahaha";}?>
这里要传入一个json格式的字符,字符要用json格式去编写,这里设定名称为flag,传入json格式字符为{“flag”:0},为什么这里传入的值要是0呢?这里当我们传入数字时,它会转化为同一类型进行比较(这里关键点在于,要把字符转换为数字有效,因为字符转换为数字会出问题,字符会被转为0),这里字符被转为0,我们传入的参数为0,所以相等:
0x05.switch函数
绕过方式
switch选择的时候,处理的变量会被强转为int类型
这个函数一般用于根据多个条件选择执行不同动作,和if…. else相似
函数格式
switch(表达式,通常是变量)
{
case xxxx1:
当条件符合xxxx1时执行的代码
break;
case xxxx2:
当条件符合xxxx2时执行的代码
break;
….
}
这里的问题在于switch选择的时候,处理的变量会被强转为int类型
这里当我们输入字符串时,会被强转为int类型的
当我们输入字符串4abcdesdf的时候,这个 a 就 会 被 强 转 为 i n t 类 型 , 使 a就会被强转为int类型,使 a就会被强转为int类型,使a的值为4
0x06.in_array函数
函数格式 in_array(需要在数组内搜索的数值,被搜索的数组,一个可选参数,设置TURE检查数据和数组值类型是否相同)
绕过方式
这个函数的作用是检查数组中是否存在某个值,当没有最后的检测参数为true时,默认为松散比较,导致弱类型,传入不同数据类型来绕过
关键就在于这个最后的检查的参数,如果没有这个参数的话,就会使用松散比较来判断
这里如果没有设置检查参数为ture的话,会进行松散比较,数据类型不同的话,会进行适当的类型转换,这里aaa被转换为int类型,被转换为0,匹配失败,但是1aa 在转换时被转换为1,匹配成功
0x07.array_search()函数
绕过方式
1.传入不同数据类型来绕过,没有最后的检测参数为true时,默认为松散比较,导致弱类型
2.当数据类型不同会先把原有数据的数据类型的进行转换,导致弱类型的产生
该函数的作用是在数组中搜索某个键值,并返回键名
函数格式 array_search(要搜索的键值,被搜索的数组,设置TURE检查数据和数组值类型是否相同)
这个函数有两个问题
第一个:
和前面in_array一样,当没有设定检测参数为true时,会进行松散比较,会把数据类型进行转换,然后执行
第二个:
0x08.intval()函数
该函数用于获取变量的整数值
函数格式 intval(要转换的参数,指定转换需要的进制)
绕过方法:
该函数处理本身不能处理的字符串时并不会报错,直接返回0,当函数内有其他的操作时,会把字符串这些转为数字类型再操作,而且在处理一些特殊数据的时候会有不同的处理结果
echo intval('+25'); //25echo '
'; echo intval('-25');//-25echo '
'; echo intval(025);//21echo '
'; echo intval('025');//25echo '
'; echo intval(1e10);//1410065408echo '
'; echo intval('1e10');//1echo '
'; echo intval(0x1A);//26echo '
'; echo intval(25000000);//25000000echo '
'; echo intval(250000000000000000000);//2007498752echo '
';echo intval('420000000000000000000');//2147483647echo '
';echo intval(25, 8);//25echo '
';echo intval('0x42');//0echo '
';echo intval(array());//0echo '
';echo intval(array('aaa', 'abb'));//1echo '
';echo intval('aaa', 8);//0echo '
';echo intval('fgh');//0echo '
';echo intval('1e10'+8);//1410065416echo '
';echo intval('0x2000'+8);//8200?>
0x09.is_numeric函数
这个函数用于检测变量是否只由数字组成,否则返回false
函数格式is_numeric(目标字符)
绕过方法
当传入数字开头,字母在后的字符串时,参数可以绕过某些检测,从而进行到下一步,而php处理不同字符串时又会把两个字符串转换到同一类型去处理
2010) { echo $a;}?>
这个代码里需要传入一个参数,参数要求大于2010又不能是数字,如果数字就会中途中断从而无法输出变量a里的内容,这里我们输入一个2020a的字符串,输入后由于不是纯数字,执行到下一步,又因为php在处理数据对比时会将两个数据转换为同一类型的,从而达到绕过效果,传入2010a即可;
0x10. strpos函数
strpos函数用于查找目标字符在字符串中出现的第一次的位置
函数格式:strpos(字符串,目标字符)
绕过方法
strpos在处理数组时直接返回null,而没有返回false,导致问题发生
这里通过数组的形式去传入一个数字,首先绕过第一道检测,然后在第二处strpos检测时,通过数组去绕过,直接返回null,从而绕过。
0x11.strlen函数 + intval函数
让你传一个值,这个值长度要小于4,又要比500000大,你要如何处理?
500000){ echo file_get_contents('flag');}}绕过方法:5e5因为在strlen()函数的判断中,5e5被认为是字符串,长度为3,但是当他跟数字比较或者进行数学运算的时候,会被当成科学计数法,转成数字,也就是5乘以10的5次方,也就是500000,500000+1,自然是比500000大的
来源地址:https://blog.csdn.net/nobugnomoney/article/details/124944096