php安全代碼審計(jì)小結(jié)
文章僅為一段時(shí)間整理的php安全代碼審計(jì)筆記總結(jié),一個(gè)分析框架,沒(méi)有實(shí)例化分析。
一、工具篇
編輯器(notepad++,editplus,UE等等,看個(gè)人習(xí)慣)
TommSearch(字符串檢索) || grep
HttpProtocolDebugger(http協(xié)議調(diào)試器)
Fiddler(分析包,改包)
Seay PHP代碼審計(jì)工具(php-code-audit分析輔助)
幾個(gè)有趣的項(xiàng)目
dvwa(代碼審計(jì)測(cè)試平臺(tái))
phpmvs
php security audit check
PHP Vulnerability Hunter
#p#
二、函數(shù)篇
addslashed()添加反斜杠
stripslashed()去掉反斜杠
get_magic_quotes_gpc() 判斷是否開(kāi)啟gpc
expode(".",$array)分割成數(shù)組
is_numeric()判斷是否為數(shù)字
sizeof()判斷長(zhǎng)度
trim() 去除字符串開(kāi)頭和末尾的空格或其他字符
system() 輸出并返回最后一行shell結(jié)果。
exec() 不輸出結(jié)果,返回最后一行shell結(jié)果,所有結(jié)果可以保存到一個(gè)返回的數(shù)組里面。
passthru() 只調(diào)用命令,把命令的運(yùn)行結(jié)果原樣地直接輸出到標(biāo)準(zhǔn)輸出設(shè)備上。
EscapeShellCmd(),把一個(gè)字符串中所有可能瞞過(guò)Shell而去執(zhí)行另外一個(gè)命令的字符轉(zhuǎn)義。這些字符在Shell中是有特殊含義的,象分號(hào)(;),重定向(>)和從文件讀入 (<)等。
EscapeShellArg() 。在給定的字符串兩邊加上單引號(hào),并把字符串中的單引號(hào)轉(zhuǎn)義,這樣這個(gè)字符串就可以安全地作為命令的參數(shù)。
用popen()函數(shù)打開(kāi)進(jìn)程
上面的方法只能簡(jiǎn)單地執(zhí)行命令,卻不能與命令交互。但有些時(shí)候必須向命令輸入一些東西,如在增加Linux的系統(tǒng)用戶(hù)時(shí),要調(diào)用su來(lái)把當(dāng)前用戶(hù)換到root才行,而su命令必須要在命令行上輸入root的密碼。這種情況下,用上面提到的方法顯然是不行的。
popen ()函數(shù)打開(kāi)一個(gè)進(jìn)程管道來(lái)執(zhí)行給定的命令,返回一個(gè)文件句柄。既然返回的是一個(gè)文件句柄,那么就可以對(duì)它讀和寫(xiě)了。在PHP3中,對(duì)這種句柄只能做單一 的操作模式,要么寫(xiě),要么讀;從PHP4開(kāi)始,可以同時(shí)讀和寫(xiě)了。除非這個(gè)句柄是以一種模式(讀或?qū)?打開(kāi)的,否則必須調(diào)用pclose()函數(shù)來(lái)關(guān)閉 它。
例子1:
/* PHP中如何增加一個(gè)系統(tǒng)用戶(hù)
下面是一段例程,增加一個(gè)名字為james的用戶(hù),
root密碼是 verygood。僅供參考
*/
$sucommand = "su --login root --command";
$useradd = "useradd ";
$rootpasswd = "verygood";
$user = "james";
$user_add = sprintf("%s "%s %s"",$sucommand,$useradd,$user);
$fp = @popen($user_add,"w");
@fputs($fp,$rootpasswd);
@pclose($fp);
require在被包含文件有錯(cuò)誤代碼時(shí)將不再往下執(zhí)行
include在被包含文件有錯(cuò)誤代碼時(shí)仍然往下執(zhí)行
htmlspecialchars() 函數(shù)把一些預(yù)定義的字符轉(zhuǎn)換為 HTML 實(shí)體。
預(yù)定義的字符是:
& (和號(hào)) 成為 &
" (雙引號(hào)) 成為 "
' (單引號(hào)) 成為 '
< (小于) 成為 <
> (大于) 成為 >
move_uploaded_file() 函數(shù)將上傳的文件移動(dòng)到新位置。
extract() 函數(shù)從數(shù)組中把變量導(dǎo)入到當(dāng)前的符號(hào)表中。
對(duì)于數(shù)組中的每個(gè)元素,鍵名用于變量名,鍵值用于變量值。
第二個(gè)參數(shù) type 用于指定當(dāng)某個(gè)變量已經(jīng)存在,而數(shù)組中又有同名元素時(shí),extract() 函數(shù)如何對(duì)待這樣的沖突。
本函數(shù)返回成功設(shè)置的變量數(shù)目。
語(yǔ)法
extract(array,extract_rules,prefix)
parse_str() 函數(shù)把查詢(xún)字符串解析到變量中. (常見(jiàn)于變量覆蓋漏洞)
語(yǔ)法
parse_str(string,array)
參數(shù) 描述
string 必需。規(guī)定要解析的字符串。
array 可選。規(guī)定存儲(chǔ)變量的數(shù)組名稱(chēng)。該參數(shù)指示變量存儲(chǔ)到數(shù)組中。
針對(duì)變量指定攻擊
不使用foreach遍歷$_GET變量,改用$_GET[(index)]
eval() 函數(shù)把字符串按照 PHP 代碼來(lái)計(jì)算。該字符串必須是合法的 PHP 代碼,且必須以分號(hào)結(jié)尾。
如果沒(méi)有在代碼字符串中調(diào)用 return 語(yǔ)句,則返回 NULL。如果代碼中存在解析錯(cuò)誤,則 eval() 函數(shù)返回 false。
preg_replace 執(zhí)行一個(gè)正則表達(dá)式的搜索和替換
/e參數(shù)執(zhí)行代碼
#p#
三、漏洞篇
-----------------------------------------------
[1].Sql-Injection
留意:cookie及x-forward-for,寬字節(jié),報(bào)錯(cuò)注射等
挖掘漏洞參考
變量
$_GET[""],$_POST[""],$_COOKIE[""], $SERVER[""]
數(shù)據(jù)庫(kù)操作函數(shù)
mysql_query()
數(shù)字型注入防范:
1.is_numeric() ctype_digit() intval()
2.str_length()確定長(zhǎng)度
字符型注入防范:
1.mysql_real_escape_string()
2.數(shù)據(jù)庫(kù)查詢(xún)語(yǔ)句前加@防爆錯(cuò)
3.str_length()確定長(zhǎng)度
-----------------------------------------------
[2].Command-Execution
函數(shù):
system(),passthru(),popen(),exec()
數(shù)據(jù)庫(kù)操作函數(shù):
exec,system,popen,passthru,proc_open,shell_exec
執(zhí)行命令管道符 % | >
測(cè)試如0 | dir c:
|| 雙豎線(xiàn)的作用,前面語(yǔ)句執(zhí)行錯(cuò)誤則執(zhí)行后面語(yǔ)句
如xx"+||+whoami+||+echo
-----------------------------------------------
[3].File-Inclusion
函數(shù):
include(),require(),include_once(),require_once()
遠(yuǎn)程文件包含漏洞要求
allow_url_fopen() allow_url_include() file_get_contents()
繞過(guò):zlib://和ogg://
5.2.0之后版本
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+ //
@eval(file_get_contents('php://input')); //POST phpinfo();
配合%00截?cái)啵掳姹咀詣?dòng)轉(zhuǎn)義\0
-----------------------------------------------
[4].CSRF
CSRF防范策略
1>驗(yàn)證http-referer字段
安全性低,易被偽造
2>在請(qǐng)求地址中添加token并驗(yàn)證
token可在用戶(hù)登錄后存放在session中,每次請(qǐng)求時(shí)將token從session中取出,去請(qǐng)求的token對(duì)比以防范CSRF
GET方式:http://url/?=token
如果一個(gè)網(wǎng)站接受請(qǐng)求地方比較多,則在每次頁(yè)面加載時(shí)遍歷整個(gè)dom樹(shù),在dom中每個(gè)a和form標(biāo)簽后加入token
但在動(dòng)態(tài)頁(yè)面加載后產(chǎn)生的html代碼,則需要以硬編碼的形式手工添加
這種方式安全性弱點(diǎn)在于,如在論壇等交互比較頻繁的地方hacker可構(gòu)造環(huán)境盜取token并進(jìn)而構(gòu)造csrf攻擊
故手工關(guān)閉referer
3>在HTTP頭中自定義屬性并進(jìn)行驗(yàn)證。通過(guò)XMLHttpRequest類(lèi)。
通常用于Ajax方法對(duì)頁(yè)面局部的異步刷新
但適應(yīng)性一般,對(duì)已有的網(wǎng)站架構(gòu)局限性較大
-----------------------------------------------
[5].XSS(Cross Site Script)
反射型與存儲(chǔ)型
控制$_GET,$_POST,$_COOKIE 各種傳入的變量
使用htmlspecialchars()函數(shù)進(jìn)行基礎(chǔ)過(guò)濾
結(jié)合CSRF實(shí)現(xiàn)自動(dòng)化利用
-----------------------------------------------
[6].File_Upload
函數(shù):move_uploaded_file()
變量:$_FILES
php文件上傳利用form表單進(jìn)行文件上傳時(shí)必須為post使用multipart/form-data才能完整的傳遞文件數(shù)據(jù)
php利用$_FILES系統(tǒng)函數(shù)的相關(guān)參數(shù)與函數(shù)move_upload_file函數(shù)來(lái)實(shí)例把由$_FILES全局變量生成的臨時(shí)文件移動(dòng)到指定目錄完成文件的上傳
$_FILES['files']['name']客戶(hù)端文件的原名稱(chēng)
$_FILES['files']['type']文件的MIME類(lèi)型
$_FILES['files']['size']已上傳文件的大小
$_FILES['files']['tmp_name']儲(chǔ)存的臨時(shí)文件名,一般為系統(tǒng)默認(rèn)
$_FILES['files']['error']該文件上傳到相關(guān)的錯(cuò)誤代碼
防范方式:
1>判斷MIME TYPE文件類(lèi)型如$_FILES['files']['type']=="image/jpeg",判斷文件大小,如$_FILES['files']['size']<10000 && $_FILES['files']['size']>100
2>指定上傳文件名,如依賴(lài)時(shí)間生成hash(time).jpg等方式
3>根據(jù)文件后綴名判斷文件
如file_ext=substr($filename,$strrpos($filename,'.')+1);
注意是否可能有雙擴(kuò)展名,二次上傳突破等邏輯問(wèn)題
4>服務(wù)器嘗試渲染文件等方式判斷是否為圖片
5>不依賴(lài)于客戶(hù)端js腳本限制上傳文件類(lèi)型
6>白名單規(guī)則
apache服務(wù)器常見(jiàn)上傳安全問(wèn)題
1>配合.htaccess利用上傳
AllOverride ALL 允許子規(guī)則覆蓋父規(guī)則
.htaccess添加AddType Application/x-httpd-php .jpg
2>文件名解析漏洞
*.php.123
在.htaccess添加AddHandler php5-script .php,文件名中包含php擴(kuò)展名可以php腳本執(zhí)行,如x.php.jpg
.php3 .php4擴(kuò)展名
#p#
四、配置篇
1>關(guān)注漏洞信息,及時(shí)更新版本
2>php.ini httpd.conf .htaccess文件配置
1)safe_mode相關(guān)配置
2)register_globals關(guān)閉
3)open_basedir配置,防范目錄遍歷
4)allow_url_fopen關(guān)閉
5)disable_functions配置
6)magic_quotes_gpc打開(kāi)
7)error_reporting=E_ALL & ~E_NOTICE
8)display_errors=Off避免攻擊者獲取更多信息
9)expose_php=Off隱藏版本信息
3>最小化服務(wù)器其他賬戶(hù)權(quán)限
4>第三方安全加固軟件安裝
5>調(diào)用第三方安全防護(hù)文件,配置php.ini
include_path=".:/php/includes"
auto_pretend_file="anti-inj.php"
auto_appent_file=
五、思路篇
剛開(kāi)始練習(xí)審計(jì)時(shí),拿到一套源碼,馬上做的事情就是,丟到工具里,去掃敏感的函數(shù),然后去一個(gè)一個(gè)的回溯它,找到入口點(diǎn)。但是,這樣審計(jì)很浪費(fèi)時(shí)間,每次都要在回溯過(guò)程中,不斷的去尋找源碼中定義的一些通用函數(shù)。由于不了解整個(gè)源碼的流程,導(dǎo)致在找這些通用函數(shù)的過(guò) 程中浪費(fèi)了很多的時(shí)間與精力。
所以,我重新調(diào)整了我的審計(jì)流程。在拿到源碼之后,先從它開(kāi)始的地方(一般是根目錄下的index文件)按照?qǐng)?zhí)行的順序去讀代碼,一直到它的初始化內(nèi)容, 和基本功能實(shí)現(xiàn)完畢為止。這樣,可以明確的了解整套源碼的結(jié)構(gòu),哪一種函數(shù)文件放在哪個(gè)文件夾下;知道通用函數(shù)放在哪個(gè)文件中。這對(duì)我們?cè)谥箝喿x“疑似”有問(wèn)題的代碼時(shí),有很好的幫助,例如,在看到一個(gè)通用函數(shù)時(shí),我們可以快速的切換到通用函數(shù)文件,查找這個(gè)函數(shù)的實(shí)現(xiàn)代碼。
注:此處引用修改唐門(mén)三少文章《PHP代碼審計(jì)學(xué)習(xí)總結(jié)》
六、小結(jié)
代碼審計(jì)一如逆向工程,均需要耐心與細(xì)心。
此外,關(guān)注漏洞發(fā)布平臺(tái)上最新漏洞并跟蹤加以分析也是一個(gè)很快提升自己能力的方法。