做者:ZERO 所属团队:Arctic Shelljavascript
参考资料:php
http://archimesan.me/2017/12/21/php%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%BC%8F%E6%B4%9E/html
https://www.cnblogs.com/Mrsm1th/p/6745532.htmljava
https://blog.spoock.com/2016/06/25/weakly-typed-security/mysql
0x1::弱类型与强类型
一般语言有强类型和弱类型两种,强类型指的是强制数据类型的语言,就是说,一个变量一旦被定义了某个类型,若是不通过强制类型转换,这个变量就一直是这个类型,在变量使用以前必须声明变量的类型和名称,且不经强制转换不容许两种不一样类型的变量互相操做。咱们称之为强类型,而弱类型能够随意转换变量的类型例如能够这样:git
$text=1; $text=”string”
也就是说php并不会验证变量的类型,能够随时的转换类型,估计开发者的意图是让程序员能够进行更高效的开发,因此在大量内置函数以及基本结构中使用了不少松散的比较和转换,防止程序中的变量由于程序员的不规范而报错,虽然提高了效率,可是引起了不少安全问题。程序员
类型转换问题
类型转换最多见的就是int转String,String转int。sql
Int转String:编程
$num = 5;
方式1:$item = (string)$num;
方式2:$item = strval($num);json
String转int:
intval() 函数。(取整函数)
主要问题就出如今这个intval()函数上了。
例子:
var_dump(intval(4))//4 var_dump(intval(‘1asd’))//1 var_dump(intval(‘asd1’))//0
上面三个例子说明了intval()函数在转换字符串的时候即便碰到不能转换的字符串的时候它也不会报错,而是返回0。
例子(来自于大佬的博客http://archimesan.me/2017/12/21/php%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%BC%8F%E6%B4%9E/)
<?php if($_GET[id]) { mysql_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT,SAE_MYSQL_USER,SAE_MYSQL_PASS); mysql_select_db(SAE_MYSQL_DB); $id = intval($_GET[id]); $query = @mysql_fetch_array(mysql_query("select content from ctf2 where id='$id'")); if ($_GET[id]==1024) { echo "<p>no! try again</p>"; } else{ echo($query[content]); } } ?>
主要问题就是你输入一个1024.1这样就能够利用取整性质进行绕过了。
0x2:比较操做符
在编程中类型转换是不可避免的一个事情,好比说网络编程中get方法或者post方法传入一个须要转换成int的值,再好比说变量间进行比较的时候,须要将变量进行转换,鉴于php是自动进行类型转换,因此会引起不少意想不到的问题。
“= =”与“= = =”比较操做符问题
php有两种比较方式,一种是“= =”一种是“= = =”这两种均可以比较两个数字的大小,可是有很明显的区别。
“= =”:会把两端变量类型转换成相同的,在进行比较。
“= = =”:会先判断两端变量类型是否相同,在进行比较。
这里明确说明,在两个相等的符号中,一个字符串与一个数字相比较时,字符串会转换成数值。
例如:
<?php var_dump("name"==0); //true var_dump("1name"==1); //true var_dump("name1"==1) //false var_dump("name1"==0) //true var_dump("0e123456"=="0e4456789"); //true ?>
观察上述代码,颇有意思,按照咱们前面讲的规则,name与0相比较的时候,由于name是字符串,因此说转换成数字若是是0的话,0与0相比较天然是true,那末问题来了,下一句中的1name正常来讲也是字符串,按照上一句的成立方式,1name应该是0,与1相比较的时候应该为false才对,为什莫为true了呢?咱们能够假设一下,带数字的字符串不会变成0,会变成1,这样前三条逻辑就解释的清楚了,可是到第四条就又错了,为此我查了一下php的官方文档,文档是这样说的:当一个字符串看成一个数值来取值,其结果和类型以下:若是该字符串没有包含'.','e','E'而且其数值值在整形的范围以内,该字符串被看成int来取值,其余全部状况下都被做为float来取值,该字符串的开始部分决定了它的值,若是该字符串以合法的数值开始,则使用该数值,不然其值为0。
上述的代码为什莫出现那种奇怪的状况也就解释的清楚了。当不一样类型的变量进行比较的时候就会存在变量转换的问题,在转换以后就有可能会存在问题。
hash比较操做符问题
在hash比较的时候也会出现问题例如:
"0e132456789"=="0e7124511451155" //true "0e1abc"=="0" //true4219903
当出现xex模式的时候表明科学计数法,好比1e3=1*10三次方,在进行比较运算时,若是遇到了0e\d+(意思就是0e就是0e,d+的意思是后面所有是数字)这种字符串,就会将这种字符串解析为科学计数法。因此上面例子中2个数的值都是0于是就相等了。若是不知足0e\d+这种模式就不会相等。这个题目在攻防平台中的md5 collision就有考到。
例子:《MD5碰撞》源于南邮攻防平台题目(https://cgctf.nuptsast.com/challenges#Web)
<?php if (isset($_GET['Username']) && isset($_GET['password'])) { $logined = true; $Username = $_GET['Username']; $password = $_GET['password']; if (!ctype_alpha($Username)) {$logined = false;} if (!is_numeric($password) ) {$logined = false;} if (md5($Username) != md5($password)) {$logined = false;} if ($logined){ echo "successful"; }else{ echo "login failed!"; } } ?>
这一段代码的大体意思是输入一个数字和一个字符串,而且让他们的MD5值相同,才能够获得successful, 上文提到过,0e在比较的时候会将其视做为科学计数法,因此不管0e后面是什么,0的多少次方仍是0。因此咱们只须要输入一个数字和字符串进行MD5加密以后都为0e的便可得出答案。md5('240610708') == md5('QNKCDZO')成功绕过!。
十六进制转换问题
首先咱们看一下例子:
"0x1e240"=="123456" //true "0x1e240"==123456 //true "0x1e240"=="1e240"//false
php在接受一个带0x的字符串的时候,会自动把这行字符串解析成十进制的再进行比较,0x1e240解析成十进制就是123456,而且与字符串类型的123456和int型的123456都相同。
例子:《起名字真难》源自源于南邮攻防平台题目(https://cgctf.nuptsast.com/challenges#Web)
<?php function noother_says_correct($number) { $one = ord('1'); $nine = ord('9'); for ($i = 0; $i < strlen($number); $i++) { $digit = ord($number{$i}); if ( ($digit >= $one) && ($digit <= $nine) ) { return false; } } return $number == '54975581388'; } $flag='*******'; if(noother_says_correct($_GET['key'])) echo $flag; else echo 'access denied'; ?>
题目大体的意思就是输入一串key,key呢不能够是数字的形式,可是却要求与54975581388相等,看完题目就知道要求字符串和数字进行比较,想到的就是弱类型,54975581388与之匹配的十六进制的字符串是0xccccccccc。这就很巧了,全不是数字,天然就绕过了,获得flag。
布尔值转换问题
举例:
<?php If ( true=“name”){ echo “success”; }
布尔值能够和任何字符串相等。
0x3:总结
一、字符串和数字比较,字符串会被转换成数字。
二、混合字符串转换成数字,看字符串的第一个。
三、字符串开头以xex开头,x表明数字。会被转换成科学计数法(注意必定要是0e/d+的模式)。可是也有例外如:-1.3e3转换为浮点数是-1300。
四、0x开头的字符串会先解析成十六进制再进行比较
五、布尔值跟任意字符串都弱类型相等。
php内置函数的参数的松散性
主要意思就是php内部函数在调用时给函数传递函数没法接受的参数类型可是却没有报错的状况
json绕过(这个不符合松散型)
首先咱们介绍一下什莫是json:JSON概念很简单,JSON 是一种轻量级的数据格式,他基于 javascript 语法的子集,即数组和对象表示。因为使用的是 javascript 语法,所以JSON 定义能够包含在javascript 文件中,对其的访问无需经过基于 XML 的语言来额外解析。
例子:
<?php if (isset($_POST['message'])) { $message = json_decode($_POST['message']); $key ="*********"; if ($message->key == $key) { echo "flag"; } else { echo "fail"; } } else{ echo "~~~~"; } ?>
输入一个数组进行json解码,若是解码后的message与key值相同,会获得flag,主要思想仍是弱类型进行绕过,咱们不知道key值是什莫,可是咱们知道一件事就是它确定是字符串,这样就能够了,上文讲过,两个等号时会转化成同一类型再进行比较,直接构造一个0就能够相等了。最终payload message={"key":0}。
MD5 ,sha1绕过
首先仍是介绍一下这两个函数,这俩都是加密函数,分别进行的时给字符串进行MD5加密和计算字符串的 SHA-1 散列。
可是这个函数都有着缺陷,就是不能处理数组。
这样就很容易绕过了
例子:
<?php if (isset($_POST['a']) and isset($_POST['b'])) { if ($_POST['a'] != $_POST['b']) if (md5($_POST['a']) === md5($_POST['b'])) die('Flag: '.$flag); else print 'Wrong.'; } ?>
直接构造数组就能够绕过了payload: a[]=1&b[]=2
switch绕过
缺陷原理相同,绕过姿式相同,若是switch是数字类型的case的判断时,switch会将其中的参数转换为int类型。以下:
<?php $i ="3name"; switch ($i) { case 0: case 1: case 2: echo "this is two"; break; case 3: echo "flag"; break; } ?>
strcmp绕过这个时候程序输出的是,类型转换的i,结果为3返回flag
strcmp()函数在PHP官方手册中的描述是int strcmp ( string $str1 , string $str2 ),须要给strcmp()传递2个string类型的参数。若是str1小于str2,返回-1,相等返回0,不然返回1。strcmp函数比较字符串的本质是将两个变量转换为ascii,而后进行减法运算,而后根据运算结果来决定返回值。
例子:
<?php $password="*************** if(isset($_POST['password'])){ if (strcmp($_POST['password'], $password) == 0) { echo "Right!!!login success";n exit(); } else { echo "Wrong password.."; } ?>
在这个题目中咱们须要本身输入一个password的值和$password相比较可是咱们不知道这个password的值,有可能时字符串有可能时数字,这个时候怎末办呢,依然时相同的绕过姿式,试一试数组绕过假设若是传入一个数组会怎末样呢?咱们传入password[]=xxx ,绕过成功。
原理是由于函数接受到了不符合的类型,将发生错误,函数返回值为0,因此判断相等。
array_search()、in_array()绕过
首先介绍一下什莫是array_search()函数, array_search() 函数在数组中搜索某个键值,并返回对应的键名。in_array() 函数搜索数组中是否存在指定的值。基本功能是相同的,也就是说绕过姿式也相同。Array系列有两种安全问题,一种是正常的数组绕过,一种是“= =”号问题。先讲第一个数组绕过。
举例:
<?php if(!is_array($_GET['test'])){exit();} $test=$_GET['test']; for($i=0;$i<count($test);$i++){ if($test[$i]==="admin"){ echo "error"; exit(); } $test[$i]=intval($test[$i]); } if(array_search("admin",$test)===0){ echo "flag"; } else{ echo "false"; } ?>
这段代码的意思就是先判断是否是数组,而后在把数组中的内容一个个进行遍历,全部内容都不能等于admin,类型也必须相同,而后转化成int型,而后再进行比较若是填入值与admin相同,则返回flag,如何绕过呢?
基本思路仍是不变,由于用的是三个等于号,因此说“= =”号这个方法基本不能用,那就用第二条思路,利用函数接入到了不符合的类型返回“0”这个特性,直接绕过检测。因此payload:test[]=0。
第二个 “= =”的问题
在PHP手册中,in_array()函数的解释是bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),若是strict参数没有提供或者是false(true会进行严格的过滤),那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。
例子:
$array=[0,1,2,'3']; var_dump(in_array('abc', $array)); //true var_dump(in_array('1bc', $array)); //true
经过例子咱们就知道了,这个松散的判断就是等于号,因此出现了“= =”号的特性“abc”==0、“1bc”==1,若是不加true的话就能够利用“= =”轻松绕过。array_search同理。
0x4:结束—时刻防备弱类型
做为一个程序员,弱类型确实提高了程序员书写代码的效率,可是也带来了严重的安全问题,能够说一切输入都是有害的,一切输入的类型也是可疑的,永远都要带有怀疑精神去看待弱类型的php下任何比较函数,任何数学运算!