怎樣進行代碼審計

怎樣來php代碼審計

概況:

  1. 工具和常見的幾種思想

  2. 常見的點

  3. 一套簡單的CMS源碼審計

  4. 總結

這篇是關於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/

分類:

太多,顯得臃腫,太少,顯得不走心

所以這裏作者把知識點分類(讀者也可以自己去找下思維圖)

  1. 防護類

  2. sql注入

  3. 代碼或命令執行

  4. 文件操作

  5. 其他類

  6. 其他的情況

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 REQUEST_REQUEST,當然還有其他_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,上面什麼漏洞都有,可以去搜下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章