<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/web/7333

            0x00 前言


            最近上(ri)網(zhan)上(ri)多了,各種狗啊盾啊看的好心煩,好多蜜汁shell都被殺了,搞的我自己也想開發這么一個斬馬刀,順便當作畢設來做了。

            未知攻,焉知防。我們先來看看shell們都是怎么躲過查殺的:加密、變形、回調、隱藏關鍵字……總之就是一句話,讓自己變得沒有特征,這樣就可以躲過狗和盾的查殺。但是萬變不離其宗,無論怎么變形,最終都會回到類似這樣的格式:

            #!php
            $_GET($_POST)
            

            分為執行數據部分($_GET)和傳遞數據的部分($_POST),也就是說,無論怎么變形,在執行的過程中都會變成這個樣子,接下來還是去執行類似system(), exec(), eval()等等函數,那么我們就直接定位到這里,檢測該腳本是否調用危險函數,或是在腳本調用這些函數的時候進行分析,判斷該腳本是否為非法用戶的shell,會取到很不錯的防御效果。

            0x01 如何獲取先機


            既然分析出來了問題的根結,那么下一步就是要控制住這些危險函數的入口點,即system()等函數的底層入口,就像Windows APIhookzw系列函數一樣,我們也要hooksystem()這些函數。

            這里我們需要用到PHP Extension, 即PHP擴展,位于PHP內核zend和PHP應用代碼之間,很明顯,擴展可以監控應用層代碼的執行細節,也可以調用內核提供的ZEND API接口,包括禁用類、禁用函數等等。圖為PHP結合其他必要組件的基本結構。

            在ZEND中提供了接口供我們進行這樣的操作,通過zend_set_user_opcode_handler就可以達到目的,根據某大牛所說,只需要hook三個OPCODE即可。

            #!c
            ZEND_INCLUDE_OR_EVAL        處理eval()、require等
            ZEND_DO_FCALL               函數執行system()等
            ZEND_DO_FCALL_BY_NAME       變量函數執行 $func = "system";$func();等
            

            ZEND_DO_FCALL為例,在MINIT中將ZEND_DO_FCALL替換掉:

            #!c
            zend_set_user_opcode_handler(ZEND_DO_FCALL, LIGHT_DO_FCALL);
            

            接著定義自己的處理函數,這里是LIGHT_DO_FCALL

            #!c
            static int LIGHT_DO_FCALL(ZEND_OPCODE_HANDLER_ARGS)
            {
                /* 檢測是否為合法調用 */
            
                if (/*非法調用*/)
                {
                    /*攔截掉,不執行。*/
                }
                else
                {
                    /*合法調用,繼續執行*/
                    return ZEND_USER_OPCODE_DISPATHC;
                }   
            }
            

            這樣一個骨架就完成了,其余的內容可以自行添加。這樣每當調用system()等函數時,都會經過你自己的LIGHT_DO_FCALL,于是就可以對上層的代碼執行進行檢查了。

            0x02 上面的方法太粗暴


            上面的方法固然是好,但是誤判會很多,甚至一個普通的函數調用都會直接被ban掉。這樣的WAF放在業務中一定會被噴出翔的。我們需要換個思路。

            之前已經說過了,無論如何變形、加密、隱藏關鍵字等,最終都需要調用到system()eval()等函數,終究是逃不過這些的。那么我們把剛才的思路的變一下,不要直接hook那幾個OPCODE,向上走一層。

            eval()函數在底層是要調用zend_compile_string函數的,那么我們是不是可以在底層重載掉zend_compile_string函數,或是用自己的函數替換掉呢?答案肯定是可以的,我們只需要重寫自己的zend_compile_string函數即可。

            但是對于system來說有些不一樣,上面我們hook掉了ZEND_DO_FCALL,但是太粗暴了,我們這里可以在function_table中刪除掉system,并用我們自己的函數注冊system函數,這樣就可以做到了實時檢查,如果為合法調用,可以調用舊的system函數并繼續執行。

            很明顯這樣做的話,確實減少了誤殺,但是也增加了風險,因為除了system()之外還有很多可以用于執行命令的函數。除了執行命令,還有遍歷目錄、讀敏感文件的問題,需要考慮很多的方面,偏僻的函數都要考慮在內,還有PHP SPLDirectoryIterator等等各種方面。

            0x03 說了這么多我們看看實際效果吧


            我們來看看hook三個OPCODE的方式會產生什么樣子的效果。首先我們在MINIT函數中將這三個OPCODE的處理函數換成我們自己的。

            #!c
            ZEND_MINIT_FUNCTION(lightWAF)
            {
                /*
                 * hook掉ZEND_DO_FCALL
                 * 處理system函數等
                 */
                zend_set_user_opcode_handler(ZEND_DO_FCALL, LIGHT_DO_FCALL);
            
                /*
                 * hook掉ZEND_DO_FCALL_BY_NAME
                 * 處理$func='system';$func();等類型
                 */
                zend_set_user_opcode_handler(ZEND_DO_FCALL_BY_NAME, LIGHT_DO_FCALL_BY_NAME);
            
                /*
                 * hook掉ZEND_INCLUDE_OR_EVAL
                 * 處理eval, require等
                 */
                zend_set_user_opcode_handler(ZEND_INCLUDE_OR_EVAL, LIGHT_INCLUDE_OR_EVAL);
            
                return SUCCESS;
            }
            

            LIGHT_DO_FCALLLIGHT_DO_FCALL_BY_NAMELIGHT_INCLUDE_OR_EVAL的內容類似,均為判斷腳本文件是否在upload目錄中,如果在的話就禁止危險函數的執行,否則放行。這里我們用LIGHT_DO_FCALL說明一下:

            #!c
            /* LIGHT_DO_FCALL */
            static int LIGHT_DO_FCALL(ZEND_OPCODE_HANDLER_ARGS)
            {
                char *filePath;
            
                filePath = zend_get_executed_filename(TSRMLS_C);
                php_printf("[Debug] filePath: %s\n<br />", filePath);
            
                if (strstr(filePath, "/upload/"))
                {
                    /* 非法調用,攔截 */   
                    php_printf("[Warning] Execute command via system() etc.\n<br />");
                    return ZEND_USER_OPCODE_RETURN;
                }
                else    /* Not Found */
                {
                    /* 合法調用,放行 */
                    return ZEND_USER_OPCODE_DISPATCH;
                }
            }
            

            這里我們看到,一旦檢測到在upload目錄中,就返回ZEND_USER_OPCODE_RETURN,實際作用就是不繼續執行危險函數了,如果不在upload目錄中,就返回ZEND_USER_OPCODE_DISPATCH,實際作用就是繼續執行該函數。

            擴展編寫完成后,編譯即可得到so文件,如果對PHP擴展開發以及編譯不熟悉的同學,請參考這個擴展開發教程的第五章:http://www.walu.cc/phpbook/ ,開發方面的知識超出了本文的內容,所以這里不再贅述了。

            編譯完成后,將得到的lightWAF.so文件復制到php的擴展目錄下,我這里是/usr/local/php/lib/php/extensions/,根據自己的實際情況進行調整。接下來修改php.ini,讓PHP自動加載我們的擴展,在其中加上一行:

            extension=/path/to/our/lightWAF.so
            

            我這里是:

            extension=/usr/local/php/lib/php/extensions/lightWAF.so
            

            根據之前so文件放的位置不同,請自行修改路徑。

            最后我們重啟PHP服務,讓設置生效。PHP重啟完成后,在終端中執行php -m,來驗證擴展是否加載成功,如果在結果中看到了lightWAF,則說明加載成功。(請無視圖中的錯誤信息,那個是我本地環境的問題,對lightWAF以及PHP沒什么影響)

            成功加載后,我們看一下實際效果,這里有一個上傳文件的頁面,可以上傳任何類型的文件并將上傳的文件保存在upload目錄中。現在模擬一下getshell的過程。

            上傳頁面

            我們寫個小馬,內容如下:

            #!php
            <?php
                system($_GET["cmd"]);
            ?>
            

            進行上傳,并訪問shell,結果如下:

            可以看到被成功攔截了,好吧,我們換只牛逼的馬兒,試試反射型的。

            #!php
            <?php
                $func = new ReflectionFunction("system");
                echo $func->invokeArgs(array("$_GET[c][/c]"));
            ?>
            

            繼續上傳并訪問,看看結果:

            依然被攔截了,只不過這次觸發的是ZEND_DO_FCALL_BY_NAME這個OPCODE。 接下來我們看看正常的文件會不會被攔截,在lightWAF目錄下寫入一個test.php,用于模擬正常的業務文件,內容如下:

            #!php
            <?php
                system('ls');
            ?>
            

            訪問一下看看結果:

            可以看到ls命令成功的執行了,也就是說我們的正常文件是不會被攔截的,而只有upload目錄中的文件會被攔截,這樣做又會引發另一個弊端,倘若攻擊者通過某種方法將shell寫入正常的文件中,或是與業務結合起來,那么這種防御手段就很難生效了。具體如何防御還要結合其他的特征進行檢測,并不是沒有辦法了,實際應用中不能只依靠檢測文件路徑這一條規則,需要結合業務進行部署防御方案。

            另一種方法與這個hook三個OPCODE的方法類似,無非就是麻煩一點,感興趣的同學可以圍觀下面的參考文獻:http://security.tencent.com/index.php/blog/msg/19 ,這里描述了hook函數的比較詳細的思路。

            0x04 不談業務的安全都是耍流氓


            總結一下,攔截的方法大概就是上面兩種,但是攔截的依據還沒有決定,如何判斷一個調用system()的腳本是否為webshell呢?如果我們的WAF放到生產環境,啥都不管亂殺,很有可能造成正常的業務無法工作。我自己總結了幾個方法,可能聯合起來使用效果更好一些。

            0x05 所以這WAF有啥用


            安全需要和業務結合起來進行,不談業務的安全都是耍流氓。因為是擴展級別的WAF,在部署的時候可能需要重新編譯,修改配置文件等等。批量式部署可能顯得不是那么方便,而且要根據業務需要進行各種細微的調整。如果僅僅是幾臺服務器,相信這種WAF還是十分棒的,調整起來也十分方便。

            如果能開發出適合批量部署的基于擴展的WAF,那么可能會比較容易普及,畢竟在業務上部署WAF不是一個簡單的事情。

            http://security.tencent.com/index.php/blog/msg/57 http://security.tencent.com/index.php/blog/msg/19 http://www.walu.cc/phpbook/ http://www.php-internals.com/book/?p=index http://www.nowamagic.net/librarys/veda/detail/1543

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线