PHP是世界上最好的語言,是的,php在世界上養活了兩類人,一類是編寫php代碼的人,一類是從事安全×××的這類人,由於在php中存在着有漏洞的函數。
在必定條件做用下,這些函數沒有按着函數發明者的意願去解析。
在ctf的web世界中也算待了幾個月了,對學習當中的遇到的有問題的函數略作一下總結。php
md5()函數web
定義:Md5()函數計算字符串的MD5散列
問題1:通過MD5()函數處理的字符串散列若是出現0e開頭的,在被php處理的時候會被認爲等於0。
源碼:正則表達式
<?php $user=$_GET['user']; $pass=$_GET['pass']; if($user!=$pass){ if(md5($user) == md5($pass)){ echo "this is flag"; } else{ echo "no no no"; } }else{ echo "字符串不能相同"; } ?>
代碼功能:判斷get方法傳入的參數的MD5的hash字符串是否相等,而且是在兩參數值不相等的狀況下。
測試結果:
sql
原理分析:
當兩個字符串被MD5加密的時候,會產生以0e開頭的哈希值。Php進行判斷的時候0e…會被當作科學計數法,0的n次方都是等於零。因此這兩個字符串被md5()加密後都等於零。
var_dump(0 == 0e123456)
問題2:處理數組的時候返回爲null
源碼:數據庫
<?php $user=$_GET['user']; $pass=$_GET['pass']; if($user!=$pass){ if(md5($user) == md5($pass)){ echo "this is flag"; } else{ echo "no no no"; } }else{ echo "字符串不能相同"; }
測試:
數組
原理分析:
md5(string,raw)語法:string必須,規定要計算的字符串,raw可選,規定輸出格式,TRUE-16字符二進制格式;false-默認,32字符十六進制數。
函數做者規定處理的字符串類型爲str字符串型,才能正常輸出。傳遞數組沒法處理。形成輸出結果爲null安全
問題3:形成sql注入漏洞ide
某些查詢語句,特別是查詢密碼的時候,每每會使用md5()函數處理後再去數據庫查詢對比,這樣就會形成一些風險。
查詢語句:
Select from user where passwd = ‘.md5($password,true)’;
這個地方有一個特殊的字符串能夠繞過,在你不知道密碼的狀況下查詢到user表的內容。
字符串:ffifdyop
測試:
能夠看到字符串被md5()加密後成爲了 ‘ or ‘ 6…,
添加到查詢語句中就變成了
Select from user where password=’’or’6…’(…表示亂碼,並不影響查詢)
這時候咱們將加密後的東西拿去到數據庫查詢一下。
看能夠將數據庫中的全部的數據所有查出。
原理分析:
Or語句就不在贅述,只說一下,數據庫中的操做,在查詢的時候,where 的後面並不必定非要跟上參數纔可以查詢。
只要where後面的值不等於零,就表示true。等於零爲false
而且where後面的值是字符串的時候,可是第一個是一個非零數的時候也能查詢全部有內容
因此也解釋了,爲甚前面亂碼依然成立的問題。函數
***擴展同類函數:sha1()函數。具體用法相同。其哈希值0e開頭的有,sha1('aaroZmOk') sha1('aaK1STfY') sha1('aaO8zKZF') sha1('aa3OFF9m')學習
ereg()函數
定義:正則表達式匹配
問題1:能夠00截斷
源碼:
if(isset($_GET['num'])){
if(ereg('^[a-zA-Z0-9]+$', $_GET['num']) === FALSE){
echo "請輸入符合要求的";
}else{
echo "您輸入的符合要求";
}
}
代碼分析:只有當輸入數字和字母的時候,輸入正確,若是輸入包含符號的就會提示不正確。
漏洞利用:輸入123%00
看字符串中含有符號,應該返回「請輸入符合要求的」的提示。可是漏洞就經過00截斷產生了
原理解析:
當ereg()函數碰到%00的時候,就會認爲字符串結束,並不會繼續往下檢測。
問題2:碰到參數是數組的返回爲null
源碼相同,測試結果:
實現原理:由於返回爲null,null != FALSE,因此匹配正確
intval()函數
定義用法; 獲取變量的整數值,容許以使用特定的進制返回。默認10進制 注:若是參數爲整數,則不作任何處理。
問題:能夠構造字符串繞過
測試:
問題解析:
經過上面的實驗說明,當取回字符串整數的時候,若是字符串中含有非數字的字符,將會返回第一次出現非數字符的前面的整數。若果沒有數字返回0.
unset()函數
定義用法:unset() 銷燬指定的變量。
存在問題:可能銷燬原有定義的變量,進行繞過
源碼:
$abc['a'] = true; foreach(array('_GET','_POST') as $method) { foreach($$method as $key=>$value) { unset($$key); } } if ($abc['a'] == false) { echo 'flag {123}'; } ?>
測試:
解析:
若是unset變量存在請求參數當中,便會出現銷燬變量實現繞過的現象。就如這裏,經過$$符號,$key=abc;$$key=$abc;最後unset()函數便會變成unset($abc),將原來定義的$abc[‘a’]=true給銷燬。
extract()函數
定義用法: extract() 函數從數組中將變量導入到當前的符號表。 該函數使用數組鍵名做爲變量名,使用數組鍵值做爲變量值。針對數組中的每一個元素,將在當前符號表中建立對應的一個變量。 EXTR_OVERWRITE - 默認。若是有衝突,則覆蓋已有的變量。 EXTR_SKIP - 若是有衝突,不覆蓋已有的變量。 EXTR_PREFIX_SAME - 若是有衝突,在變量名前加上前綴 prefix。 EXTR_PREFIX_ALL - 給全部變量名加上前綴 prefix。 EXTR_PREFIX_INVALID - 僅在不合法或數字變量名前加上前綴 prefix。 EXTR_IF_EXISTS - 僅在當前符號表中已有同名變量時,覆蓋它們的值。其它的都不處理。 EXTR_PREFIX_IF_EXISTS - 僅在當前符號表中已有同名變量時,創建附加了前綴的變量名,其它的都不處理。 EXTR_REFS - 將變量做爲引用提取。導入的變量仍然引用了數組參數的值。 這個函數的重點就是默認將已經有的變量給覆蓋掉
存在問題;將原來的變量覆蓋,進行繞過
源碼:
$a = 'yaun'; extract($_GET); if($auth == 1){ echo "private!"; } else{ echo "public!"; }
測試:
問題解析:當我傳遞a=1的時候,extract()函數發現有原來的變量,因而將原來變量的值覆蓋掉,變成a=1,在進行if條件語句的判斷。
parse_str()函數
定義用法: parse_str() 函數用於把查詢字符串解析到變量中,若是沒有array 參數,則由該函數設置的變量將覆蓋已存在的同名變量。 極度不建議 在沒有 array參數的狀況下使用此函數,而且在 PHP 7.2 中將廢棄不設置參數的行爲。此函數沒有返回值。
源碼:
if(empty($_GET['id'])){ show_source(__FILE__); die(); }else{ include('flag.php'); $a = "https://blog.51cto.com/12332766"; $id = $_GET['id']; @parse_str($id); if($a[0] == 'yaun'){ echo "yes is flag"; }else{ exit('其實很簡單,其實並不難'); } }
測試:
問題解析:
當傳遞參數id=a[]=yaun的時候,通過parse_str()函數的處理將a變成變量。可是原來有同名的變量,因而就將原來的變量覆蓋掉,同時覆蓋的還有變量的值。
變量覆蓋拓展:php中遇到$$的時候也會出現變量覆蓋的狀況。詳情請到:http://www.javashuo.com/article/p-zgqjwlhd-dt.html。
strcmp()函數
定義用法: Strcmp(string1,string2)函數比較兩個字符串
返回值:
0 - 若是兩個字符串相等
<0 - 若是 string1 小於 string2 >0 - 若是 string1 大於 string2
問題:處理數組的時候返回null
源碼「
$a="yaun"; $pass=$_GET['pass']; if(strcmp($a, $pass) == 0){ echo "成功"; }else{ echo "失敗"; }
測試:
問題解析:
傳遞數組的時候,函數沒有辦法比較數組,返回null,php語言自己就是弱類型的語言,null==0 在數值上相等。可是在類型上不等。
is_numeric()函數
定義用法: is_numeric() 函數用於檢測變量是否爲數字或數字字符串。 若是指定的變量是數字和數字字符串則返回 TRUE,不然返回 FALSE
問題1:傳遞十六進制的話,會讓檢測無效。
源碼:
$a=$_GET['num']; if(is_numeric($a)){ echo "您輸入的是數字"; }else{ echo "請輸入合法字符"; }
測試:
輸入一串查詢語句轉換過得十六進制
成功繞過函數的檢測
問題解析:這個函數不只可以檢測十進制,同時還認爲十六進制也是合法的。因而就能夠構造十六進制的語句進行繞過此函數。
preg_match()函數
定義用法: Preg_match()函數匹配正則表達式。
返回值:
返回 pattern 的匹配次數。 它的值將是 0 次(不匹配)或 1 次,由於 preg_match() 在第一次匹配後 將會中止搜索。若是發生錯誤preg_match()返回 FALSE。
問題:若是在進行正則表達式匹配的時候,沒有限制字符串的開始和結束(^ 和 $),則能夠存在繞過的問題
源碼:
$ip=$_GET['ip']; if(!preg_match("/(\d+)\.(\d+)\.(\d+)\.(\d+)/",$ip)) { die('error'); } else { echo "this is flag"; }
測試:
輸入一個不符合匹配規則的字符串,依然返回flag
問題解析:
由於這個函數在執行匹配規則的時候沒有說明是以什麼開頭或者結尾,因此只須要在這個字符串中存在規定的字符,其餘的字符也能夠添加上去進行繞過。
in_array()函數
定義用法: in_array(search,array) 函數搜索數組中是否存在指定的值。
返回值:
若是給定的值 search 存在於數組 array 中則返回 true。若是沒有在數組中找到參數,函數返回 false。
$array=[0,1,2,'3']; var_dump(in_array('abc', $array)); var_dump(in_array('1bc', $array));
測試:
問題分析:
能夠看到上面的狀況返回的都是true,由於’abc’會轉換爲0,’1bc’轉換爲1。
在全部php認爲是int的地方輸入string,都會被強制轉換
unserialize()函數
具體使用方法見另外一篇博客:http://www.javashuo.com/article/p-fgdwykvd-ch.html
strpos()函數
定義用法: strpos() 函數查找字符串在另外一字符串中第一次出現的位置(區分大小寫)。 註釋:strpos() 函數是區分大小寫的。 註釋:該函數是二進制安全的。
語法:
strpos(string,find,start) string 必須,規定被搜索的字符串;find 必須,規定查找的字符串;start 可選,規定開始搜索的位置。
返回值:
返回字符串在另外一字符串中第一次出現的位置,若是沒有找到字符串則返回 FALSE。註釋: 字符串位置從 0 開始,不是從 1 開始
源碼:
if(strpos($_GET['password'],'abc') == 0 ){ echo '123'; } else{ echo '456'; }
傳遞一個數組進去測試,使得返回結果是123
能夠看到咱們輸入的並不符合要求,可是仍是給了123的輸出。
問題解析:
這個函數也是隻解析string類型的字符串,給他個數組就不知道如何解析,因而就返回爲null。Null==0!
strlen()函數
定義用法: strlen() 函數返回字符串的長度
語法:
strlen(string) string-必須,規定要檢查的字符串。
源碼:
if(strlen($_GET['password']) == 0 ){ echo '1233'; }else{ echo '4566'; }
測試:
傳遞一個數組,使得返回爲1233
問題分析:
這個函數也是隻解析string類型的字符串,給他個數組就不知道如何解析,因而就返回爲null。Null==0!
此博客做爲我的筆記,如有帶來錯誤之處請諒解!