你要問我審計過什麼項目發現過什麼漏洞,那還真沒有。記得第一家公司的老闆娘問我「你會代碼審記嗎」,我不懂該怎麼回答。要說懂我不願定代碼審計的具體定義是什麼;要說不懂我看懂了ecshop和公司的代碼框架,在我認爲中看懂代碼和源代碼審計沒什麼區別嘛。如今說的話,真要說區別我以爲和滲透測試是測試的子集同樣,代碼審計也是「看懂代碼」的子集。php
子集換意思,就是基本能力和基本思想是能夠通用的,但由於注意的點更集中,因此通用方法能夠簡化、優化出專門的方法。具體到看懂代碼和代碼審計上,看懂代碼講究從總體上弄清數據流向和各模塊功能,代碼審計則能夠只追究局部,好比只要我看到char strdst[9];strcpy(strdst,"0123456789"),我不用明白程序功能是什麼就能夠判定這裏必然溢出。前端
如開頭所說我沒搞懂「代碼審計」和「看懂源代碼」的區別,因此買了本書,是尹毅的《代碼審計:企業級Web代碼安全架構》(名字很牛逼書卻很薄,我常常想少於300頁的書質量都不太行的。。),具體內容基本忘了,只記得其中提到代碼審計大概有兩種思路:第一種是正向追蹤數據流,第二種是逆向溯源數據流。java
前不久看王煒的《揭祕家用路由器0day漏洞挖掘技術》,其中也說代碼審計大概有正向追蹤和逆向溯源兩種方式,另外進一步指出對於fgets/read/recv等輸入函數適合正向追蹤,對於strcpy/system等操做函數適合逆向溯源。mysql
最後再回頭去看Marcus Pinto的《黑客攻防技術寶典:Web實戰篇》,其在第19章中說查找源代碼中的源洞分三步:第一步是肯定接收用戶輸入的位置,第二步是查找漏洞簽名,第三步是詳細分析漏洞簽名的上下文看有沒有漏洞。所謂漏洞簽名就是漏洞常常出現的特徵代碼,更簡單點說就是漏洞常常伴隨出現的函數,好比緩衝區溢出的strcpy,再好比命令注入的sehll_exec等。sql
我看遍了全部理論,感受說得都看有道理,但要我進行代碼審計,仍是徹底不懂該怎麼操做。最主要的問題就是正向追蹤,從什麼位置開始追?追到什麼位置算結束?逆向溯源,從什麼位置開始溯?溯到什麼位置算結束?shell
直到我反覆琢磨以上三本書的說法,發現獲取用戶輸入的函數與漏洞簽名函數互爲起止點。代碼審計思路總結爲如下四個步驟:安全
1. 肯定要審計的代碼是什麼語言(好比是java仍是.net)、什麼框架(好比是ssh仍是其餘)。cookie
2. 肯定該語言及框架,從get/post中獲取用戶輸入的函數或形式(好比java的getParameter,php的$_GET和$_POST);肯定該語言各種漏洞的簽名函數(好比JAVA和SQL漏洞相關的的是execute、executeQuery、createStatement,和命令執行漏洞相關的是getRuntime和exec等等)。session
3. 使用Source Insight打開要審計的項目。架構
4. 使用search project在整個項目中查找獲取用戶輸入函數和漏洞簽名函數,對獲取用戶輸入的函數使用正向追蹤數據流看用戶能控制的數據有沒有進入漏洞簽名函數,對漏洞簽名函數使用逆向溯源數據流看數據來源是否是源於獲取用戶輸入的函數。
其實咱們去看代碼審計工具,第一步都是查找獲取用戶輸入函數和漏洞簽名函數,第二步則是或正向或逆向追蹤一下函數的變量(追蹤能力的強弱差異則是審計工具能力強弱的差異)。好比下邊兩圖是VCG(VisualCodeGrepper)和Seay對dvwa的審計結果:
通常而言,正向追蹤能含蓋更多的數據流分支,但有可能大多數數據都不會進入漏洞簽名函數因此效率可能會低一點;逆向溯源從漏洞簽名函數出來發現漏洞的機率會大一點,可是容易漏掉一些沒有漏洞簽名的函數(好比後門密碼等)。
如下表格整理自《黑客攻防技術寶典Web實戰篇》(第二版),並不全只是作個參考,增強所謂漏洞簽名函數的意思(不少實際上是類可是類仍是函數並非重點)。
語言 | 獲取用戶輸入 | 會話交互 | sql注入 | 系統命令注入 | 動態代碼執行 | 路徑遍歷 | url重定向 |
Java | getParameter getHeader等 |
getAttribute setAttribute等 |
executeQuery executeUpdate |
getRuntime exec |
readObject writeObject |
FileInputStream FileReader |
sendRedirect addHeader |
.Net | Params QueryString |
Add | SqlCommand SqlDataAdapter |
Process ProcessStartInfo |
Execute ExecuteGlobal |
File.Open FileStream |
Redirect Addheader |
PHP | $_GET $_POST |
$_SESSION session_register |
mysql_query mssql_query |
exec shell_exec |
eval call_user_func |
fopen include等 |
http_redirect header |
Perl | param cookie |
pm | selectall_arrayref | system exec |
eval | open sysopen |
redirect |
下面以從之前幫妹子寫的一個畢業設計中經過逆向溯源查找sql注入來進行演示上一點所說的思路具休操做是怎麼個樣子(說來妹子最終都沒是本身的妹子實在是悲劇)。
第一步----使用的語言和框架----java、struts2。
第二步----獲取用戶輸入函數和漏洞簽名函數-----struts2獲取用戶輸入是經過繼承ActionSupport實現getter/setter方法來獲取的,java中sql注入的漏洞簽名函數是executeQuery、executeUpdate。
第三步----使用Source Insight打開要審計的項目----結果以下圖所示
第四步----使用search project查找executeUpdate並溯源其變量是否源於用戶輸入----操做過程以下
列出來的那些就是使用executeUpdate函數的位置,點擊紅色連接按鈕便可跳轉過去,在審計中咱們將逐個審計這些位置。
咱們點擊連接按鈕進入第一處,往前看executeUpdate執行的sql語句,能夠看到語句中對大多數變量使用了預編譯形式(ps.setString)但對user.getLoginPwd()使用了拼接方式
咱們要追蹤user.getLoginPwd()的來源,看是否是來自於獲取用戶輸入的函數。user是register函數的參數,咱們要回溯哪裏調用了register。在register上右鍵,點擊「Jump To Caller」
以下圖所示即進入調用位置,咱們往前看到user.setLoginPwd(),將光標置於其loginPwd參數上,在下方的定義窗口能夠看到loginPwd是本類定義的一個變量
雙擊下方定義窗口的中「loginPwd」主窗口即跳轉到該位置,觀察上下文loginPwd正是繼承ActionSupport類後實現的getter方法(getLoginPwd)從前端表單中獲取的
因此,sql漏洞簽名函數中,拼接的loginPwd變量來源於,用戶輸入;因此該處存在sql注入。