
1 背景概述
WEB應用漏洞導致的入侵時有發生,掃描器和WAF并不能解決所有的問題,于是嘗試在主機側針對PHP環境做了一個防御方案。很榮幸的邀請到TSRC部分白帽子做了一次對抗演習,本文主要分享一下防御思路。
防御方案主要想解決的問題是getshell、主機敏感文件泄漏等問題。于是乎出了下面這樣一個題目:部署了防御方案的Nginx + PHP 的WEB環境,提供一個上傳入口,可上傳任意文件。找到WEB目錄下的一個flag文件讀取到內容即算突破。
2 PHP擴展
防御方案使用了PHP擴展來完成阻斷。PHP內核支持C/C++開發一些擴展功能,并且提供了一個框架 – ext_skel幫助生成基本的代碼簡化開發,由于PHP擴展并不涉及底層的資源管理,所以編寫一個PHP擴展和編寫一個C應用程序是一樣的。下圖展示了PHP擴展所處的結構層次,擴展位于PHP內核ZEND 和 PHP應用層代碼之間,那么利用PHP擴展可以:
1) 監控PHP應用層代碼的執行細節,包括執行CGI、函數名、參數等;
2) 調用PHP內核ZEND提供的API接口,包括禁用類、修改配置選項等。
圖1 PHP語言的結構
3 相關知識
3.1 HOOKPHP代碼
PHP是解釋型語言,代碼被翻譯為中間字節碼由ZEND引擎解析執行。PHP把中間字節碼稱之為OPCODE,每個OPCODE對應ZEND底層的一個處理函數,ZEND引擎最終執行這個處理函數。實現HOOK功能只需要改變HOOK OPCODE對應的處理函數即可,而ZEND預先就提供了一個現成的接口:zend_set_user_opcode_handler。防御方案只需要HOOK以下三個OPCODE:
ZEND_INCLUDE_OR_EVAL — eval、require等
ZEND_DO_FCALL — 函數執行system等
ZEND_DO_FCALL_BY_NAME — 變量函數執行 $func = “system”;$func();
舉例:
ZEND_DO_FCALL這個OPCODE對應的功能是函數調用,如果需要HOOK所有的函數調用:
1) 在模塊初始化函數中使用zend_set_user_opcode_handler修改ZEND_DO_FCALL新的處理函數為mysub:
PHP_MINIT_FUNCTION(phpips){
zend_set_user_opcode_handler(ZEND_DO_FCALL, mysub);
return SUCCESS;
}
2) 在自定義函數中實現自己需要的功能并返回原來的處理函數:
void mysub(){
自定義功能;
return ZEND_USER_OPCODE_DISPATCH;
}
3.2 ZEND接口
HOOK之外還需要獲取一些基本信息或功能,比如:
1) 獲取執行的PHP腳本名,可調用ZEND的接口zend_get_executed_filename:
char *cgi_name = (char*)zend_get_executed_filename(TSRMLS_C);
2) 禁用一些類庫,可使用zend_disable_class接口;
ZEND提供了很多豐富的接口,可根據需求選擇調用。
3.3 數據結構
PHP是弱類型語言,其上層并不區分變量類型,變量底層對應的是一個union結構體,以php-5.3.6版本舉例,結構體在Zend/zend.h文件中定義;內容如下:
typedef union _zvalue_value {
long lval; /* longvalue */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value*/
zend_object_value obj;
} zvalue_value;
PHP是在底層區分開了變量類型;在union結構體中有個非常重要的結構體HashTable,PHP中的數組結構就是利用HashTable這個結構體實現,如果變量是個數組,獲取變量內容需要遍歷這塊HashTable,數據結構這塊不做太多介紹。ZEND底層用zval這個結構體存儲變量,同時ZEND提供了幾個宏可以方便的轉換zval到具體的數據類型,比如把zval類型轉換為字符串 – zval el; Z_STRVAL_P(el),常用的還有:
Z_LVAL_P、Z_DVAL_P、Z_ARRVAL_P等,以上宏列表在 Zend/zend_operators.h有定義。
4 規則策略
4.1 基本思路
我們需要解決的問題是getshell、主機敏感文件泄漏等問題。如果限制住PHP腳本執行命令 和 翻閱文件的功能,就達到了目的。同時,由于讀取文件的接口非常多,使用場景也較多,因此采取禁用打開目錄接口來側面達到禁止翻閱文件的目的,不能遍歷目錄便無法得到文件名,可以有效的達到目標。故最終形成的最終策略如下:
1) 禁用掉執行命令的接口
2) 禁用掉打開目錄的接口
禁用函數有一些需要注意的問題:
1) PHP接口還是比較繁多復雜的,除了system、exec、opendir、scandir常用函數之外,一些生僻函數比如glob等函數都要考慮全面,另外還有一些回調函數、反射類(PS:部分考慮不全被blackeye和雪人繞過)
2) PHP SPL提供的一些類庫有文件管理的功能,也需要禁止,比如DirectoryIterator(不過好像沒有白帽子使用過SPL)
4.2 對抗變形
PHP使用assert或preg_replace /e執行代碼,調用zend_get_executed_filename獲取的PHP腳本名中會帶有辨別標識,assert對應的是assert code,preg_replace對應的是regexp code;當發現這些特殊的腳本執行了system等4.1中提高的高危函數直接阻斷。以下圖的nonalphanumeric -webshell舉例:
變形webshell解析后的形式和$_GET[1]($_GET[2])類似,比如傳入1=assert&2=system(whoami)執行代碼,PHP擴展層監控到system函數執行,并且發現PHP腳本名帶regexp code標識,清晰的知道是preg_replace的代碼執行了調用了system,阻斷system。Assert函數同理。總結:擴展并不關注在靜態層如何編碼運算或加密,擴展只監控最后的行為,所以可以很好的解決目前的變形難題。
圖2nonalphanumeric – webshell
4.3 減少誤殺
有時正常的PHP腳本也需要用到命令執行的功能,如何保證其正常運行,不被我們的防御策略阻斷呢?這里提供三種思路:
1) 寫入權限和執行命令權限互斥
考慮到入侵者利用上傳漏洞或者其他0DAY getshell得到的文件大多會有W寫權限,防御方往往希望阻止這部分文件擁有過高的權限。
那么,我們可以去除正常PHP腳本的可寫權限,然后只對有寫權限的PHP腳本實施命令執行等防御策略的阻斷邏輯。這樣即不會影響到業務的使用,又達到了預設場景的防護目標。不過這樣的思路,需要禁用chmod等可以修改文件權限的函數。
2) 使用白名單邏輯
粗暴一點的可以用文件名白名單,也就是業務報備過的文件不受阻斷,其它不認識的文件一律禁止。優雅一點的可以對函數參數做白名單,比如提取正常PHP腳本執行命令的參數或者代碼特征,對其進行放行,不認識的特征一律阻斷。
3) 使用黑名單邏輯
設定一些已知惡意腳本的黑特征參數,類似于殺毒軟件早期的特征碼思路。
上述思路在企業推廣時,需要和業務有足夠充分的配合,也都有不完美的地方,如果大家有更多方案或者思路,歡迎與我共同探討。
5 未來
利用PHP擴展可以做的事情還是比較多,比如在漏洞檢測方面、對數據庫函數參數過濾、阻斷SQL注入、阻斷存儲型XSS等場景都有非常大的想象空間。甚至在代碼審計這塊也可以考慮,國內有個大牛已經有比較成熟的開源產品,TAINT (作者:laruence),大家可能都已經比較熟悉了,就暫時不做贅述了。
行文比較倉促,有不足的地方請諒解,有任意疑問或建議歡迎聯系pandas(小毛驢)切磋交流。
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜