怎樣來php代碼審計
概況:
-
工具和常見的幾種思想
-
常見的點
-
一套簡單的CMS源碼審計
-
總結
這篇是關於php;
常見的也是php的代碼審計,類似java框架,asp.net,python的Django還是有漏洞之類的,只是比較固定和稀少;
像php這種語言本身就是弱語言類型(很多點都利用了弱類型比較和轉換),功能函數也是奇多(導致了其與sql,xml等語言的花式組合),加之處理一個問題的方法也是奇多(其中就難保不會出現漏洞)
當然php的thinkphp框架,有機會來(這款框架就安全性沒有其他幾種語言那麼強的安全措施,倒轉還有一個爛大街的log寫webshell的常見問題,以及最近挺火的遠程代碼執行)
一.工具和常見的幾種思想
(一)工具
這裏 我介紹兩款免費常用的審計工具和一款php編寫調試工具
Seay源代碼審計系統
下載地址:https://www.waitalone.cn/seay-source-code-auditv2.html
2.(PHP代碼審計工具——rips)
下載地址: https://sourceforge.net/projects/rips-scanner/
使用教程: https://www.cnblogs.com/Jewel591/p/7477483.html
3.phpstorm
如果對於版本沒有什麼要求
直接按照http://www.3322.cc/soft/17468.html激活即可
這款工具的調試運行,作者還是多提醒一點
.php等文件必須在www文件夾下,其他的搜下就知道了
4.xdebug
這款debug暫時還沒有用,不過看各位前輩經常用到,特提出來
(二)幾種常見的審計思想
其實個人認爲就兩種,由點破面,由面破點
當然還有
1.由點破面
根據經驗和工具找漏洞關鍵詞,來溯源調用過程
看是否可控,可控後看調用的輸入入口
如果單個可控條件滿足我們的要求,但是無法實施漏洞觸發,再全局看下哪裏有滿足我們條件的地方,組合起來觸發
其中用到我們的工具Seay源代碼審計工具
2.由面破點
通讀全文,理清大意,再根據問題關鍵詞,勾畫對應位置 —英語閱讀
深入理解一套CMS源碼就是這樣
如果追求速度,那麼無疑第一種最好,也是入門首選,暫時只專注於問題地方(作者目前也是用的第一種)
二.常見的點
這裏就不寫那些有的沒的了,只詳細給出作者認爲個人目前階段審計挖掘常用的點
推薦學習:
1.php常見漏洞
審計中弱類型比較,首當其衝,其他的函數問題大多圍繞這個比較特性
== 與=== 的差別;
數組,字符,數字,對象之間的比較轉換;
當然還有其他web漏洞,這裏不多說
2.php常見漏洞函數
還是必須要寫出來有個清晰的認識,寫出一些前輩的總結吧
先寫出 == 與 ===的差別
=== 在兩個數比較時,也會比較參數的類型
來看一個代碼簡單理解下
<?php
if("0admin" == 0){
echo "ok1"."<br>";
}
if("0a" === 0){
echo "ok2"."<br>";
}
沒錯,只輸出了 ok1,第二個進行了類型的比較
md5的0開頭比較,利用了php其弱類型比較特點
其實也是利用了 ==來比較造成的漏洞
從網上隨便抄了兩個payload,看下下面的代碼
<?php
$a=md5("s155964671a");
$b=md5("s214587387a");
echo $a."<br>";
echo $b."<br>";
if($a == $b){
echo "ok1"."<br>";
}
if($a === $b){
echo "ok2"."<br>";
}
輸出也只有 ok1:
0e342768416822451524974117254469
0e848240448830537924465865611904
ok1
ereg的00截斷,一道很多ctf類型的題原型
<?php
if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '*-*') !== FALSE)
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}
?>
payload ?password=1e9%00*-*
字符長度滿足,內容包括特殊字符且爲a-zA-Z0-9,且大於9999999,%00後內容ereg不再判斷
3.strcmp
判斷輸入密碼是否相等
如果 str1 小於 str2 返回 < 0;
如果 str1 大於 str2 返回 > 0;
如果兩者相等,返回 0。
<?php
$m=$_GET['a'];
$n="admin";
if(strcmp($m,$n) == 0){
echo "ok1"."<br>";
}
if(strcmp($m,$n) === 0){
echo "ok2"."<br>";
}
paylaod a[]=1
也只返回了ok1
顯然還是利用了我們的 ==
這裏payload可以是數組,也可以是對象(但是作者目前還沒有找到合適的payload)
md5和sha1不能處理數組
先來md5,這個很有意思,數組可以繞開 === 判斷
<?php
$a=$_GET['a'];
$b=$_GET['b'];
if(md5($a) == md5($b)){
echo "ok1"."<br>";
}
if((md5($a) === md5($b))){
echo "ok2"."<br>";
}
payload ?a[]=1&b[]=2
ok1 和 ok2都輸出了
同樣sha1
<?php
$a=$_GET['a'];
$b=$_GET['b'];
if(sha1($a) == sha1($b)){
echo "ok1"."<br>";
}
if((sha1($a) === sha1($b))){
echo "ok2"."<br>";
}
payload ?a[]=1&b[]=2
ok1 和 ok2都輸出了
is_numeric
輸出這個代碼觀察
<?php
if(is_numeric(0x23)) {
echo "ok1" . "<br>";
}
if(is_numeric("23aa")) {
echo "ok1" . "<br>";
}
只有ok1,說明我們能用其他編碼繞過
6.in_array
也是一個弱類型,當in_array函數第三個參數不爲true時
1sh.php會被認爲是1.php
具體下面介紹的項目有練習
serialize 和 unserialize漏洞
有點多,有興趣自己搜索下
淺顯的認識可以參考作者的個人博客:http://47.106.140.244/2018/08/php%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96%e6%bc%8f%e6%b4%9e/
8.filter_var()的問題
FILTER_VALIDATE_URL 這個問題,很容易就可以繞過的
給出幾個前輩的paylaod
?url=http://xxx.com()sec-redclub.com
()裏換成@&?/,:#這些任意一個即可
9.htmlentities()
ENT_COMPAT(默認值):只轉換雙引號。
ENT_QUOTES:兩種引號都轉換。
ENT_NOQUOTES:兩種引號都不轉換。
算一個bug吧
10.就不繼續列舉了,有興趣自己去擴展下視野
3.
有一個代碼審計的項目蠻有趣的,https://github.com/hongriSec/PHP-Audit-Labs,也是作者這幾天在學習的內容
4.
這裏還是想推薦sqli-labs注入天書,因爲審計中存在很多硬剛sql注入的正則繞過
5.也可以參考作者的個人博客總結
http://47.106.140.244/2018/09/%e5%88%9d%e6%8e%a2php%e5%ae%a1%e8%ae%a1/
http://47.106.140.244/category/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/
分類:
太多,顯得臃腫,太少,顯得不走心
所以這裏作者把知識點分類(讀者也可以自己去找下思維圖)
-
防護類
-
sql注入
-
代碼或命令執行
-
文件操作
-
其他類
-
其他的情況
0.防護類
先把它單獨拉出來分析,總沒錯
sql,xss過濾函數等等
無外乎看看正則過濾,preg_replace,replace等等
具體的繞過,下面分析
1.SQL注入類
這個問題,作者反覆想了下
主暫時分爲三類
1.沒有任何防護
2.硬剛正則
3.二次注入
1.沒有任何防護
漏洞嘛,多找找,極具耐心,總能找到一個點的
翻看了以前dede的漏洞,發現很多都是漏掉的防護點導致的問題
自己以前審計的大米cms的存儲xss,就是沒有任何防護,sql防護倒是可以
不多說
2.硬剛正則
這個可能是極有意思的主題了
目前看到繞過正則的一共這幾種
(1)查找正則匹配遺留的關鍵詞
等於= 換成regexp like等
延時注入的代替 推薦一篇文章:https://www.cdxy.me/?p=789
除開-- + – - # %23等,還有一個很有意思的註釋 ;%00
(2)其實,更多的作者看到的結合其他的奇思繞過(註釋,閉合)
hpp污染,md5(str,true),超全局變量覆蓋,request請求的差異等,addslashes函數和gpc濫用
這個作者只有提醒下,具體要自己慢慢接觸
3.二次注入
也簡單分爲兩種
1.入數據庫函數一次,出數據庫函數一次,就還原了
2.利用其他沒有檢查的數據進行注入
4.
作者看到的大多數情況都要和其他齊思結合,只能說有一個點有問題,就再去尋找另一個點與之結合突破
2.代碼或命令執行
像system eval 函數系統命令調用,反引號,動態函數執行就不在這討論了,遇到了就去考慮繞過或者替換就行了
有一個點作者一直覺得很有意思,preg_replace /e修正執行代碼
如果在構造正則表達式的時候,使用了/e修正符,這時,preg_replace() 就會將 replacement 參數當作 PHP代碼執行。但是replacement一般都是開發者事先寫好的,這是就要考慮怎樣去替換掉了,超全局變量覆蓋,get,post,request的請求解析順序,hpp污染
3.文件操作
作者分爲四類 :上傳繞過,任意文件讀寫,任意文件刪除,文件包含
具體文件操作函數,讀者自己搜下,因爲都有用,所以就不挑到寫了
1.上傳繞過
作者不多說什麼函數問題,個人覺得黑盒的一個知識更有趣;可以不會其他,這個必須會,拿shell呀
https://github.com/c0ny1/upload-labs 推薦一套github上的源碼,有20個知識點吧,就不復制過來佔位置了
https://github.com/LandGrey/upload-labs-writeup/ 這套源碼的解題答案(本來想寫文章的,看了這篇解題,瞬間寫的想法沒有了,哈哈哈)
http://47.106.140.244/2018/10/%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0%e7%9a%84%e5%a7%bf%e5%8a%bf/ 個人博客關於文件上傳的總結
2.任意文件讀寫
讀文件,作者目前還沒有觀察到明顯的函數漏洞
個人遇到比較多的還是log的寫shell,如thinkphp的問題
其他的看到後臺寫模板的情況比較多
3.任意文件刪除
unlink函數
一個很容易忽視的點,開發人員和審計人員
4.任意文件包含
分爲本地包含和遠程包含,看配置文件
一般是繞過正則,進行其他有用文件的包含以及php僞協議進行其他文件內容的查看
4.其他類
這個點是作者認爲最有意思的,因爲前面寫的很多地方都有總結
這個點的宗旨就是結合各種情況進行利用
前面其實作者都寫了,只提一點核心思想
既是 “有一個點有問題,就再去尋找另一個點與之結合突破”
5.其他的情況
不談學習知識,能不硬剛正則就不硬剛
這種,作者比較關心的偏門的地方和情況
1.header頭的sql注入
暫不談crlf注入以及xss等頭跳轉,是開發人員對user-agent,refer,cookies進行了一個數據庫的入庫查詢,但是並沒有像post和get的數據一樣進行check_sql和check_xss等檢查
就構成了沒有任何防護的注入
其中也有可能系統開了gpc的,但由於它們是在$_SERVERE變量中不受GPC的影響,那我們就可以去查找HTTP_CLIENT_IP和HTTP_X_FORWARDFOR關鍵字來快速尋找漏洞
2.寬字節只提一筆
3.也是gpc的問題
有些CMS源碼中並沒有判斷gpc情況,直接addslashes一次,如果本身就開啓了,相當於兩次轉義了,可以使用函數 get_magic_quotes_gpc() 進行檢測
4.超全局變量
常見的三個$_GET $_POST _FILE等
這個有什麼用?變量覆蓋結合其他點進行繞過,注入,寫文件,getshell等
PHP-Audit-Labs項目day-15,還是day-14就有一個題,可以去看看
5.這裏給出一個很有用的知識點
php中雙引號解析變量、而單引號不解析變量(相信很多新手都有一會雙信號,一會單引號的習慣)
運行
<?php
$s="ok";
echo "1".'$s'."<br>";
echo "2"."$s";
6.其他版本以及配置問題,並不在我們開始審計的範疇,就不多討論了
三.一套簡單的CMS源碼審計
這套源碼是作者寫這篇文章現找的,目的就是更真實還原個人是怎樣審計的
直接給出哪有問題,對於個人而言太沒有學習價值了
可以去這找找http://www.mycodes.net/43/,拿小的CMS練手
這裏作者用的phpcom這套源碼審計 ,下載鏈接: http://www.mycodes.net/43/5429.htm
這裏提醒一下,不怕它防,就怕我們看不懂它們,因爲知識點其實就那些
1.sql
來看下sql的問題,發現它的數據庫操作都是往DB類送
找到class DB,在src/class/db/database.php裏
作者發現裏面沒有任何防禦,有點失望的時候,去頁面搜索框payload了一下,發現非法字符,搜索後發現確實是做了全局防禦的,但是是在phpcom.php裏做的
看下輸入函數裏:
if (!MAGIC_QUOTES_GPC) {
$_GET = phpcom::addslashes($_GET);
$_POST = phpcom::addslashes($_POST);
$_COOKIE = phpcom::addslashes($_COOKIE);
$_FILES = phpcom::addslashes($_FILES);
}
這裏就做的很好,先判斷了的
作者沒有發現ip入庫操作,但是有獲取ip的操作,不過在phpcom.php用了htmlspecialchars函數來防禦,其他文件繼承了它的防禦
2.xss
phpcom.php裏
來看下這個防禦xss的函數
public function check_urlxss() {
$uri = strtoupper(urldecode(urldecode($_SERVER['REQUEST_URI'])));
if (strpos($uri, '<') !== FALSE || strpos($uri, '"') !== FALSE || strpos($uri, 'CONTENT-TRANSFER-ENCODING') !== FALSE) {
showmessage('request_tainting');
}
return TRUE;
}
兩次url解碼後再全部轉爲大寫,看到strpos函數,想起了前幾天看到的一個有趣的花式
strpos 函數返回查找到的子字符串的下標。如果字符串開頭就是我們要搜索的目標,則返回下標 0 ;如果搜索不到,則返回 false (而本來搜索到是返回true的)
這裏我們可以不用<,但必須用到雙引號" 來閉合value的雙引號,本來是過了strpos的,可惜用了 === 來判斷,如果前面過了,後面的雙引號可以用註釋符號,這樣只是頁面會出錯而已
if(strpos($a,'"') == FALSE){
echo "222";
}
(2)作者還看到用了幾次的header函數
phpcom::header('Location: ' . $_SERVER['HTTP_REFERER']);
public static function header($string, $replace = true, $http_response_code = 0) {
$string = str_replace(array("\r", "\n"), array('', ''), $string);
@header($string, $replace, $http_response_code);
if (preg_match('/^\s*location:/is', $string)) {
exit();
}
}
過濾 \r \n 預防crlf
3.因爲是練習,所以install.php文件,順便看下
$lockfile = ROOT_PATH . 'data/install.lock';
if(file_exists($lockfile)) {
show_message('install_locked');
它在文件開頭直接判斷了的,不存在調到step的步驟問題,略
4.代碼執行
作者翻到一個反引號,可以作爲執行代碼,數組的鍵值
function implode_field_value($array, $glue = ',') {
$sql = $comma = '';
foreach ($array as $k => $v) {
$sql .= $comma."`$k`='$v'";
$comma = $glue;
}
return $sql;
}
跟蹤它,可以發現,phpcom.php 數據庫的增刪改查都用到了它,說明,全套源碼有數據庫操作的頁面,就可以看下是否存在變量覆蓋漏洞
溯源跟蹤代碼每個頁面,搜索看有沒有變量覆蓋的地方(結合seay審計工具查看變量覆蓋特徵函數)
作者搜索發現像 $$ 這種覆蓋,鍵值都是確定了的,意味着無法入手,考慮結合變量覆蓋
這套源碼還有像parse_str這樣的覆蓋,也是人工的,這裏無用
找到eval函數,也是發現寫死了的
找到call_user_func_array函數,暫時無理解,不分析,記得webshell的免殺它很有用
preg_replace /e操作是註釋了的,看來開發人員也拿不準
如果有幸存在超全局變量,也是個思路
5.文件操作(能力原因,還有很大的操作空間)
用seay工具最煩的就是文件操作類,因爲大部分都是它誤報(因爲基本上關於文件的變量都是寫死了的),所以放到最後看
(1)像include等包含太多了,作者也沒有跟蹤代碼
文件寫入和讀取
(2)尋找php file_get_contents
有兩個地方轉了4,5個文件發現文件名變量前面兩個是環境變量,寫死了的
其實只要一個最後一個可控就行,就可能進行目錄穿梭讀取
(3)尋找file_put_contents
也是寫死了的
(4)@ulink刪除文件函數的跟蹤有時間再來觀察
(5)當然還有fread等文件操作函數,實在不想跟了,留下來自己看吧
6.當然如果老老實實的把上面做完,還有一個黑盒和邏輯要做
夜深了,腳冷,就寫到這吧。
四.總結
最後認慫去搜這套源碼的漏洞,沒有百度到這套源碼的歷史漏洞,運氣確實有點黴
如果練習,作者記得有一套老的phpcms,上面什麼漏洞都有,可以去搜下