CTF-WEB:PHP 弱类型

PHP 弱类型

PHP 比较 2 个值是否相等可以用 “ == ” 或 “ === ”,“ == ” 会在比较时自动转换类型而不改变原来的值,因此这个符号经常出现漏洞。“ == ” 比较相等的一些常见值如下,当某些语句的判断条件是使用 “ == ”来判断时,就可以使用弱类型来替代。值得一提的是 “0e” 开头的哈希字符串,PHP 在处理哈希字符串时会把每一个以 “0E” 开头的哈希值都解释为 0。

'' == 0 == false
'123' == 123
'abc' == 0
'123a' == 123
'0x01' == 1
'0e12346789' == '0e987654321'
[false] == [0] == [NULL] == ['']
NULL == false == 0
true == 1

如果遇到了 “===” 则不会进行类型转换,但也并不代表无从下手。如果条件表达式中含有函数,也可以通过传入数组让函数返回 NULL 使得条件满足。

MD5 碰撞漏洞

MD5 信息摘要算法是一种被广泛使用的密码散列函数,可以产生出一个 128 位(16字节)的散列值用于确保信息传输完整一致。而 PHP 在处理哈希字符串时,“0e” 开头的值利用 “==” 判断相等时会被视为 0。所以如果两个不同的密码经过哈希以后,其哈希值都是以 ”0E” 开头的,那么 PHP 将会认为他们相同,这就是所谓的 MD5 碰撞漏洞。如果数据库中存在这种哈希值以 ”0E” 开头的密码的话,就可以以这个用户的身份轻易地登录进去。
常用的 MD5 碰撞有:

MD5 原值
0e830400451993494058024219903391 QNKCDZO
0e545993274517709034328855841020 s878926199a
0e342768416822451524974117254469 s155964671a
0e848240448830537924465865611904 s214587387a
0e848240448830537924465865611904 s214587387a
0e545993274517709034328855841020 s878926199a

例题:bugku-矛盾

打开网页,看到一段 PHP 代码:

$num=$_GET['num'];
if(!is_numeric($num))
{
      echo $num;
      if($num==1)
            echo 'flag{**********}';
}

代码首先用 get 方法读取一个变量 num,注意到想要使 flag 显示,需要运行代码“echo 'flag{**********}';” 。此时需要 2 个 if 语句都成立,第一个 if 语句需要用 is_numeric() 判断变量是否是数字,第二个 if 语句要用 “ == ”判断变量是否等于 1。要使得一个变量值等于 1 且不是数字,这真是件矛盾的事情,不过通过传递一个弱类型的变量就解能出来。构造出 payload,提交获得 flag。

?num=1abc

例题:攻防世界-simple_php

打开题目,看到一段 PHP 代码。这段代码需要用 get 方法接收 2 个变量,其中第一个变量 a 需要判断是否为数字 0,而且直接判断 a 要为真,第二个变量 b 需要不是一个数字,并且数值大于 1234。

<?php 
show_source(__FILE__); 
include("config.php"); 
$a=@$_GET['a']; 
$b=@$_GET['b']; 
if($a==0 and $a){ 
    echo $flag1; 
} 
if(is_numeric($b)){ 
    exit(); 
} 
if($b>1234){ 
    echo $flag2; 
} 
?> 

很明显又是一个弱类型,此时可以根据需求构造出 payload,提交获得 flag。

?a=abc&&b=1235a

例题:bugku-十六进制与数字比较

题目的源码如下,需要传入一个字符串变量 password,返回 flag 的条件是 password 的值和 number 变量的值相等。但是在判断两个变量是否相等之前,代码要先遍历 password 字符串,如果字符串中的字符转换为 ASCII 在 0 ~ 9 之间就会返回错误。

<?php
error_reporting(0);

function noother_says_correct($temp)
{
    $flag = 'flag{test}';
    $one = ord('1');    //ord — 返回字符的 ASCII 码值
    $nine = ord('9');    //ord — 返回字符的 ASCII 码值
    $number = '3735929054';
    // Check all the input characters!
    for ($i = 0; $i < strlen($number); $i++)
    {
          // Disallow all the digits!
          $digit = ord($temp{$i});
          if ( ($digit >= $one) && ($digit <= $nine) )
          {
                // Aha, digit not allowed!
                return "flase";
          }
    }
       if($number == $temp)
             return $flag;
}

$temp = $_GET['password'];
echo noother_says_correct($temp);
?>

注意到判断变量使用的是 “==”,这又是熟悉的弱类型,只要 password 的数值和 number 一样,可以使用其他进制来替换。因此传入的 password 应该是 “3735929054” 其他进制的表示,结合题意应该是十六进制(注意十六进制以 0x 开头):

?password = 0xpassword

例题:bugku-数组返回 NULL 绕过

题目的源码如下,首先注意到第一个分支语句使用了 ereg() 函数,该函数用于搜索一个字符串中指定的字符串。分析一下 ereg() 函数中的第一个参数,方括号表示字符集,匹配大小写字母和数字其中一个字符,^ 表示字符串开始,$ 表示字符串结束,+ 号表示重复 1 到多次,整个表达式表示匹配由多个数字大小字母组成的字符串。第二个分支中使用了 strpos() 函数,该函数用于查找字符串在另一字符串中第一次出现的位置(区分大小写)。因此传入的 password 应该满足以数字或者字母开头,且必须在 password 参数中找到 “--”。

<?php
$flag = "flag"; 

if (isset ($_GET['password'])) {
    if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
          echo 'You password must be alphanumeric';
    else if (strpos ($_GET['password'], '--') !== FALSE)
          die('Flag: ' . $flag);
    else
          echo 'Invalid password';
}
?>

不过聪明的做法是传个 “password[]” 数组进去,因为 ereg 和 strpos 函数都只能处理字符,传入数组是返回的是 NULL。三个等号的时候不会触发弱类型,所以 NULL 不等于 false 满足条件。构造出 payload 提交:

?password[]=0

例题:bugku-urldecode 二次编码绕过

题目的源码如下,首先题目需要输入变量 id,且变量 id 不能包含字符串 “hackerDJ”。接着使用 urldecode() 函数进行 url 解码,需要令解码后的字符串等于 “hackerDJ”。

<?php
if(eregi("hackerDJ",$_GET[id])) {
      echo("not allowed!");
      exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ"){
      echo "Access granted!";
      echo "flag";
}
?>

在 URL 编辑框中可以使用 URL-encode 来替代字符,例如 “h” 的 URL 编码是 “%68”,此时输入 “%68ackerDJ” 等同于输入 “hackerDJ”。由于源码会使用 urldecode() 函数进行 url 解码,因此可以对 “%68ackerDJ” 进行 url 编码,让传入的字符串通过第一个条件语句,在解码之后通过第二个分支语句。所以提交的 payload 应该是 “%68ackerDJ” 的 URL 编码。

?id = %2568ackerDJ

例题:bugku-md5()函数

题目的源码如下,首先注意到第一个分支语句,如果 username 和 password 2 个变量相等会导致无法获得 flag。但是第二个分支条件又要求 username 变量和 password 变量经过 md5() 函数加密的结果相同。

<?php
error_reporting(0);
$flag = 'flag{test}';
if (isset($_GET['username']) and isset($_GET['password'])) {
      if ($_GET['username'] == $_GET['password'])
            print 'Your password can not be your username.';
      else if (md5($_GET['username']) === md5($_GET['password']))
            die('Flag: '.$flag);
      else
            print 'Invalid password';
}
?>

注意此时遇到的是 “===” ,不过也不是代表无从下手。在 md5() 函数传入数组时会报错返回 NULL,当 2 个变量都导致报错返回 NULL 时就能使使得条件成立。构造出 payload 提交:

?username[]=1&password[]=0

例题:bugku- md5 加密相等绕过

题目的源码如下,我们需要传入一个变量 “a”,这个变量 a 的要求是经过 md5 加密之后和 “QNKCDZO” 的加密结果相同。

<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
      if ($a != 'QNKCDZO' && $md51 == $md52) {
            echo "flag{*}";
      } 
      else {
            echo "false!!!";
      }
}
else{echo "please input a";}
?>

“QNKCDZO” md5 加密结果为 “0E830400451993494058024219903391”,注意这是个 “0e” 开头的值,利用 “==” 判断相等时会被视为 0。因此现在需要传入的是另一个字符串,该字符串的 md5 加密后的字符串也是 0e 开头的即可。

posted @ 2020-08-13 01:08  乌漆WhiteMoon  阅读(3287)  评论(2编辑  收藏  举报