php黑魔法
前言
php的很多特性再帶來編程方面的同時也會帶來很多安全隱患,其中一些漏洞在CTF中經常出現,本文大部分示例也是取自CTF題目。這里首先說一下和=區別。是比較運算,它不會去檢查條件式的表達式的類型,=是恒等,它會檢查查表達式的值與類型是否相等,NULL,0,”0″,array()使用和false比較時,都是會返回true的,而使用=卻不會。
注:本文部分內容及代碼并非原創,而是從網上搜集而得。
一、數組
數組的hash計算
很多時候,PHP數組都發揮了至關重要的作用,比如下面代碼
首先判斷name和password是否一致,一致的話提示’Your password can not be your name.’,不一致的話無法通過第二個if判斷,但是php對數組進行hash計算都會得出null的空值。比如下面代碼,當我們直接進行數組的MD5運算時會提示警告
<?php #error_reporting(0); echo md5($_GET['username']); ?>警告信息說MD5()函數需要一個string類型的參數
我們加幾條調試信息,并將error_reporting(0)取消注釋
<?php error_reporting(0); $flag = 'flag{test}'; if (isset($_GET['username']) and isset($_GET['password'])) { echo 'null md5 is ';echo md5(null); echo '<br>'; echo 'null length is '; echo strlen((md5($_GET['username']))); echo '<br>'; echo 'md5(md5($_GET["username"])) md5 is '; echo md5(md5($_GET["username"])); echo '<br>'; echo 'md5(md5($_GET["password"])) md5 is '; echo md5(md5($_GET["password"])); echo '<br>';if ($_GET['username'] == $_GET['password'])print 'Your password can not be your username.';else if (md5($_GET['username']) === md5($_GET['password']))die('Flag: '.$flag);elseprint 'Invalid password'; } ?>給username和password傳入數組,
http://192.168.219.3/CTF/index.php?username[]=1&password[]=2
其運行結果為
因為usrename和password是兩個不同的數組,通過第一個判斷,又因為他們都是數組,MD5結果都是null,通過第二個判斷,輸出flag。
strcmp()
Strcmp()函數的比較作用
這里,使用strcmp去比較password和flag,如果0的話,就給出flag,但是strcmp比較,如果相等才會返回0,如果不相等的 話,要么大于0,要么小于0,但是strcmp只會處理字符串參數,如果給個數組的話呢,就會返回NULL,而判斷使用的是,NULL==0是 bool(true)的,所以運行結果
ereg()和strpos()
<?php require 'flag.php'; 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是處理字符串的,所以,按照原理,我們將password構造一個arr[],傳入之后,ereg是返回NULL的,=判斷NULL和 FALSE,是不相等的,所以可以進入第二個判斷,而strpos處理數組,也是返回NULL,注意這里的是!,NULL!==FALSE,條件成 立,拿到flag
二、數字的比較
十六進制與數字
<?php error_reporting(0); function noother_says_correct($temp) {$flag = 'flag{test}';$one = ord('1');$nine = ord('9');$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的數字,但是后面卻讓比較一串數字,平常的方法肯定就不能行了,大家都知道計算機中的進制轉換,當然也是可以拿來比較的,0x開 頭則表示16進制,將這串數字轉換成16進制之后發現,是deadc0de,在開頭加上0x,代表這個是16進制的數字,然后再和十進制的 3735929054比較,答案當然是相同的,返回true拿到flag。
數字運算(一)
<?php error_reporting(0); $flag = 'flag{test}'; if ("POST" == $_SERVER['REQUEST_METHOD']) { $password = $_POST['password']; if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) { echo 'Wrong Format'; 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'); foreach ($ps as $pt) { if (preg_match("/[[:$pt:]]+/", $password)) $c += 1; } if ($c < 3) break; if ("42" == $password) echo $flag; else echo 'Wrong password'; exit; } }這里的意思就是:接收post參數password的值,必須滿足12位以上字符,必須是非空格非TAB之外的內容,然后就是你的password要有大小寫數字,字符內容,而且匹配到的次數要大于6次,最后還有一個考點:
if ("42" == $password) echo $flag; else echo 'Wrong password';最后的答案就是:42.00e+00000000000
當然也可以這樣:420.000000000e-1
數字運算(二)
<?php $flag = "flag{test}"; $temp = "1337a"; is_numeric($temp)?die("nope"):NULL; if($temp>1336){echo $flag; } ?>代碼中先將變量放到is_numeric函數中判斷,如果數字或數字字符串則返回true,否咋返回false。然后一個判斷,如果temp大于1336則顯示flag。這里用到了PHP弱類型的一個特性,當一個整形和一個其他類型行比較的時候,會先把其他類型intval再比。如果輸入一個1337a這樣的字符串,在is_numeric中返回true,然后在比較時被轉換成數字1337,這樣就繞過判斷輸出flag。
MD5的巧合(一)
<?php error_reporting(0); $flag = 'flag{test}'; $temp = $_GET['password']; if(md5($temp)==0){echo $flag; } ?>輸入password,要求其MD5值為0,這里有兩個字符串,經過MD5運算后,為0e*****的形式,其結果為010的n次方,結果還是零
240610708
QNKCDZO
MD5的巧合(二)
<?php error_reporting(0); $link = mysql_connect('localhost', 'root', 'root'); if (!$link) { die('Could not connect to MySQL: ' . mysql_error()); } // 選擇數據庫$db = mysql_select_db("security", $link); if(!$db) {echo 'select db error';exit(); }// 執行sql $password = $_GET['password']; $sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'"; var_dump($sql); $result=mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' ); $row1 = mysql_fetch_row($result); var_dump($row1); mysql_close($link); ?>這里的sql語句是
$sql = "SELECT * FROM users WHERE password = '".md5($password,true)."'";其中md5運算函數有一個true參數,它的作用是將md5后的hex轉換成字符串,這里如果后的字符串又單引號之類的字符就可以注入了。比如字符串:ffifdyop
md5后,276f722736c95d99e921722cf9ed621c
將其轉成字符串的話就是
可以看到起字符串類似于 ‘ or ‘6…………這樣的字符串,其中’or’6是個永真的條件,如果把它放到查詢中就可以where語句的判斷,比如我們在url輸入password=ffifdyop可以看到dump出的數據
三、控制語句
switch沒有break
<?php error_reporting(0); if (isset($_GET['which'])) {$which = $_GET['which'];switch ($which){case 0:case 1:case 2:require_once $which.'.php';echo $flag;break;default:echo GWF_HTML::error('PHP-0817', 'Hacker NoNoNo!', false);break;} } ?>這個代碼是關于switch的小bug,比如讓我們包含當前目錄中的flag.php,給which為flag,這里會發現在case 0和case 1的時候,沒有break,按照常規思維,應該是0比較不成功,進入比較1,然后比較2,再然后進入default,但是事實卻不是這樣,事實上,在 case 0的時候,字符串和0比較是相等的,進入了case 0的方法體,但是卻沒有break,這個時候,默認判斷已經比較成功了,而如果匹配成功之后,會繼續執行后面的語句,這個時候,是不會再繼續進行任何判斷的。也就是說,我們which傳入flag的時候,case 0比較進入了方法體,但是沒有break,默認已經匹配成功,往下執行不再判斷,進入2的時候,執行了require_once flag.php。
【網絡安全學習資料免費領取】
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: 世安杯CTF writeup
- 下一篇: 可以使任何人获得管理员权限的Window