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 開頭的即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM