一名代碼審計新手的實戰經歷與感悟
個人認為,作為一個要入門代碼審計的人,審計流程應該從簡單到困難,逐步提升。因此我建議大家的審計流程為——DVWA——blueCMS——其他小眾CMS——框架。同時做總結,搞清楚漏洞原理。
好,進入正文!(PS:本文Ka1ier 師傅用當初自己的經驗告訴你如何去學習代碼審計,值得一看)
blueCMS是一款小眾的CMS,在2012年左右的時候,就有人發布其相關漏洞。
但是,我個人感覺用blueCMS進行新手實戰代碼審計入門,是一個非常不錯的選擇。
而我在進行blueCMS審計之前,網上也搜索了blueCMS的審計文章。
一般都是文章中只提到了兩個漏洞,一個是位于根目錄下的ad_js.php文件中,存在sql注入。

另一個就是,在common.fun.php中,自定義函數getip()可以偽造IP地址,造成注入。
而我,作為一個新手,很清楚新手在初學代碼審計的時候,會遇到什么樣的困惑,因此,決定花一些時間,認真記錄下自己實戰過程中的所見所聞所想,以及解決思路的方法。
爭取給新手一個非常全面的借鑒過程。
環境準備
BlueCMS v1.6 sp1
phpstudy php5.4.45+apache+mysql
操作系統 WIN7 64位 專業版
BlueCMS資源在i春秋上有一篇文章,上面有下載地址。其他的文章中出現的工具自己完全可以獨立解決。
因此文章中不再詳述這部分內容。若不會搭建本地網站環境,百度一下即可。
開始實戰!
代碼審計有好幾種辦法,有人習慣于追蹤數據流,有人習慣追蹤危險函數,然后回溯。有人喜歡按功能點來進行審計。當然也有人直接通讀全文。
我作為一個新手,按道理講最好通讀全文,熟悉架構。但是,我發現我的性格和習慣,更偏向于追蹤數據流。俗話說,適合自己的才是最好的。
因此,作為新手,讀者應當結合自己的習慣,去進行代碼審計。
我之所以選擇追蹤數據流,一方面是個人習慣,另一方面是因為余弦大佬曾經說過,web安全重點就三個詞——“輸入”,“輸出”,“數據流”。
雖然我是追蹤數據流,但是基本的網站結構該看還是要看的。因此,我做的第一件事,就是簡單瀏覽網站目錄結構。

實際上,很多情況下,根據命名,就可以猜出來這部分的文件具體是干嘛的了。
比如admin文件夾,那么里面的文件肯定是和管理員的操作有關。
include文件夾,里面肯定包含了一些常用的函數文件。
簡單看完目錄后,我會看主頁的index.php,在看這個文件的同時,我還會打開它的網頁,做到一邊看代碼,一邊看網頁的效果。
這種方法可以讓我們這些對代碼不熟悉的新手腦海中可以構建相應的情景圖。
以后即使做滲透,也不會手足無措。
接著,我會打開主頁index.php,“裝模作樣”的瀏覽一番。
實際上,我也就是看看里面的注釋罷了。。。。。
因為在Index.php里面,往往不需要獲取用戶輸入。
之所以看它,是想知道在這個主頁上,會引用哪些操作文件,一個網站的大概樣子是啥樣子。

比如說blueCMS中,根據主頁,我就猜測,如果將一些xss語句存到數據庫,那么他主頁顯示的時候,是不是就會有存儲型xss呢?
另外,主頁還引入了一些文件,但是,我個人是沒有去看的。
個人習慣吧,盡管那本《代碼審計:企業級web安全》中,建議大家是看看常用函數庫文件,看看配置文件的。
不過,我本人更喜歡在用到相關函數、相關配置的時候再去查看。
接下來,有兩種審計思路,一種是通過點擊網頁,看網頁如何跳轉,來追蹤審計。比如下面這幅圖:

我一旦點擊主頁上的登錄按鈕,那么我就會跳轉到user.php中,執行act=index_login的操作。
那么這時候,你就可以打開相關的文件,查看該操作是如何進行的即可。

可以看到,先是獲取了用戶的輸入,然后有的地方過濾了,有的地方沒有。
實際上,這里咋一看是好像存在漏洞,但是,當你仔細看的時候,就會發現UC_API,并且引用了client.php中的函數。
可以這么說,由于我沒有網站開發能力,導致我在看client.php中的一些函數時,是看不懂的。
就比如下面這個,即使我知道是將那些參數全都傳入UC_API_FUNC中,可是依舊不清楚返回值是什么類型。

"當你遇到問題的時候,就是你成長的契機"我非常喜歡這句話。
同理,我們都是新手,初學代碼審計,肯定很多東西看不懂。怎么辦?
這個問題會將一大批想學代碼審計又沒有開發背景的人刷下去。
我們唯有硬啃,把這硬骨頭啃碎了,才能繼續下去,否則代碼會讀的昏昏沉沉。
于是我通過搜索引擎查閱UC_API_FUNC知道,這個玩意兒是判斷用戶提交信息是否正確,然后返回正確情況下的uid。
知道這個,目前來說,就夠了。沒必要再深入了。
我這里舉這個例子,就是想讓新手知道,初學代碼審計,會有大批代碼看不懂。死磕下去,才會看懂越來越多的代碼。
也為了以后審計邏輯漏洞打下基礎。
但是,也不能盲目死磕一個不會的點,這個度只能自己把握。
另外,由于我是追蹤數據流的,所以我個人還有另外一種審計思路,就是不看網頁,也不必刻意搞懂網站的整體架構。就一句話“我只關心用戶輸入”。
這種方法說的直白點,我就利用正則匹配,每個網頁里面我都找到用戶能控制的變量,一個一個的排查。優點就是不會像上面那種方法那樣,漏掉一些頁面。
缺點就是,理清這個網站的結構有點麻煩。
因為是追蹤用戶輸入么,所以各種頁面都要打開,很麻煩。

比如,在根目錄下,我就看到了這些PHP文件,那么就挨個進去查有沒有用戶輸入,沒有就跳過。有就繼續跟讀。
恩,很巧,第一個就是那個網上流傳很多的ad_js.php。那就先進去看看吧。

雖說這里有過濾,但是他這過濾的是string型注入,而上面的sql語句壓根不需要閉合。
所以這里的過濾根本沒用。就造成了int型注入。
簡單驗證一下,union select 1,2,3,4,5,6,7,發現到7的時候沒報錯。
那么就可以在7這個位置構造語句。不過,不知道是不是我的這套blueCMS有問題,雖然存在注入,可是頁面始終顯示空白。
估計應該是這套cms有地方沒寫好吧。
具體的讀者可以看別人的這方面文章,他們都在文章中詳細寫了這個漏洞。文章鏈接在我這篇文章快要結束的時候會提供。
按照這種找用戶輸入的方法,以此類推,繼續下一個文件查看。

像上面這個文件,明顯進行了過濾,用的還是白名單思想。
這種情況,我們代碼審計就可以節約時間了,這個文件也就這兩個地方有輸入,還對輸入進行了非常好的過濾,inval()只獲取變量的整數值。
那么就可以直接跳過這個文件,不往下繼續看了。節約時間成本。

又遇到了一種情況是什么呢?
就是上面這幅圖,明明$act沒有過濾,是個可控變量,可是在這個文件中,并沒有用到這個可控變量。那么,這就說明了兩種情況,第一,這個文件在其他地方被引用。
第二,程序員純粹寫著玩的。。。當然,我相信第一種情況是更有可能的。
于是,我們可以這時候搜索一下看看到底哪些文件引用了這個文件。
在搜索之前,由于我記憶力不好,做了其他事情就會忘了之前要干嘛,因此,我還記錄了筆記。我也建議新手審計的時候做標注,做筆記,否則除非你是有天賦的人,像我這樣的普通人,真的會邏輯混亂,忘了前一個文件看到哪里。
為什么要去找那個文件。我是誰?我為什么要打開這個文件?(或許我是提前進入老年癡呆了?手動滑稽)
于是乎,作為新手的我就在筆記上記錄下這么一句話:

圖片中你可能還會看到我寫的其他記錄,那是我之前審計這個cms時候寫的。我就想告訴新手,這樣可以讓我們新手的思路更清晰一點。
思維是要慢慢鍛煉的,萬事開頭難。難歸難,但是我們要掌握解決問題的方法。這或許也是一種黑客思維。
經過追蹤發現,凡是涉及到category.php文件的相關文件,哪怕是html文件,無一例外的全都對name=act進行了賦值,這就導致了該文件中$act變量與xss是無緣了。
大家可以看到,基本上都是下圖這種情況,提前在html頁面中將name=act賦值,然后服務器端再獲取,也是白名單思想。

而唯一的一個php文件引用了category.php,卻還不涉及到$act變量。

于是乎,category.php中的那個可控變量也沒啥用了。。。
這里再補充一下,雖然按照cms的邏輯來看,是沒什么用了。
但是,我要沒記錯的話,有種漏洞叫基于dom的XSS,是可以修改html元素的。那么,既然這套cms這個地方沒有對頁面的name=act進行過濾,那么假如,有基于dom的xss,攻擊者隨意修改了name=act的值,構造攻擊語句,是不是就無法防御了呢?換句話說,利用其他漏洞來打出組合拳,那這個沒過濾還是很危險的。無奈我知識儲備有限,無法驗證我的思路。
故先在文章中寫下來。等深入研究了XSS再做實踐。
按照這樣的思路,可以繼續下一處追蹤。
不過,如果你看到現在,還沒發現我文章的問題,那么恭喜你,要么你沒有審計過這套cms,要么你和我一樣是新手。
我前面寫那么多,看似沒過濾的變量,實際上往往在引用文件的地方就進行了過濾。
比如下面這幅圖:

是不是覺得這里的$act也沒過濾?然而事實卻是在引入的common.inc.php中,早就進行了過濾。
早就對$_GET,$_POST , $_REQUIREST ,$_COOKIES進行了過濾。
但是,似乎還漏了一個$_SERVER。這個或許就會導致IP注入或XSS,前提是如果開發者將用戶IP寫入數據庫或展現出來的話。
這里提出來是因為作為新手的我居然也忽略了過濾文件。。。
都說過濾文件很重要,很重要,但是我實戰中還是忽略了,還好反應過來。
否則挖到一個漏洞,想復現就會遇到過濾問題。才會想到是否被過濾了,才會再回去查找,很麻煩。

而至于這里的過濾函數么,實際上就是判斷傳進來的數據是不是數組,要是數組,則遍歷數組的每個值進行addslashes過濾,不是數組,則直接用addslashes進行過濾。雖然引用了過濾文件,但是明顯是有可能突破的。
假如開發者在某處執行sql語句的時候,拼接變量沒有加單引號,那么int型注入就可以突破這種過濾。
其他的我這里就不一一敘述了。
簡單來說,我喜歡追蹤數據流,雖然挖掘漏洞的速度沒有那些按功能點審計,按危險函數回溯的方法快,而且對于自定義封裝的函數來說,我容易忽略這類漏洞,比如這里的getip()明顯有未過濾的情況,我卻忽略了,但是,我喜歡,不是么?
就好像讀者們為什么喜歡學黑客呢?不就是因為喜歡么?
最后,分享一下我挖到的漏洞。
blueCMS是我代碼審計第一戰!
漏洞分享
1.網站根目錄下文件讀取漏洞

在用戶進行登錄操作的時候,$from變量可控。
奇怪的是,這里還進行了base64解碼。showmsg函數功能如下:

當我看到這個assign的時候,我就有預感,這個可控變量還會在前端出現的。果不其然。
我去登錄頁面進行登錄,抓包。

我先構造了from參數的值為網站根目錄下的另一個網站DVWA的登錄主頁,然后發包

在返回包中,可以看到經過我base64編碼的值,成功解碼,并且路徑也成功的變成了網站根目錄下的DVWA

再去看一下瀏覽器,成功讀取到了DVWA的頁面

再附上一張我自己電腦上的路徑圖片。

而我用這個漏洞,只能讀取phpinfo頁面...
其他的文件讀取的話(比如管理員界面),由于有session限制,沒有權限讀取...
就會又跳轉到管理員登錄界面。

或許這個漏洞在大神眼中還可以有其他的利用姿勢吧...
2.注冊頁面存在反射型XSS
這個反射型XSS黑盒測試是測不出來的。
因為默認html中的name=from這個from的value是不提交的。
從下面的代碼中可以明顯看到(部分我分析過的無用代碼被我省略),程序的邏輯是在進入user.php中的時候,幾乎是同時獲取$act和$from的值,然后判斷$act的值,來進行相應的操作。
但是當$act的值為’reg‘,也就是注冊的時候,他會將$from的值渲染到瀏覽器上。
這也就導致了反射型XSS。

于是,我開始構造彈窗。
雖然,在user.php中,一開始就引入了過濾操作,但是,只是用addslashes進行深度過濾罷了。
要知道,在XSS中,轉義字符的效果并不明顯。

話說回來,我這個彈窗構造了一些時間,可能是因為我對XSS的研究不深入導致的吧。
另外,以當初(幾年前)的水平來看,這個XSS是黑盒測試測不出來的漏洞,為什么這么說呢?
因為我理解的黑盒測試是從看到的參數中進行各種測試,而這個from默認在你訪問注冊操作的時候,是不提交的。
from早就在訪問user.php的時候提交了,提交的還是空值。必須要同時滿足兩個條件:
1.訪問user.php的時候,就已經提交了惡意請求。
2.并且要與此同時訪問注冊頁面。如果這兩個頁面無法同時滿足,那么這個XSS就無法形成。
3.注冊頁面存在文件讀取漏洞(原理同第一個漏洞一樣)
在審計注冊操作的同時,又偶然發現了一個文件讀取漏洞,網站根目錄下的文件還是可以任意讀取,不過還是那個問題,就是有的頁面判斷session,你能有權限讀取那個頁面,但是無權利訪問。
有的網頁能讀取,但是無法渲染。。。這個洞有點雞肋。

具體如何復現,參考我提供的第一個文件讀取漏洞。
一樣的原理,一樣的方法。只是出現的位置不同罷了。
都是由于一個可控變量$from引起的。
4、編輯資料處出現存儲型XSS
首先這個漏洞我們可以很清晰的找到輸入出,但是輸出在哪目前根據代碼是看不出來的。
依據嘗試,個人資料處一般都可以看到自己的email地址,所以推斷這個存儲型XSS肯定有輸出點。
打開網頁稍微瀏覽一下就找到了輸出點。

只需要在編輯個人資料的地方,將email地址中填入最基本的alert(1),便可以提交存儲到服務器的數據庫中。
之前我在審計關于email這一欄的時候,他里面還用正則匹配做了過濾。
可是到了后臺這里,卻沒有再用正則匹配進行過濾,或許是因為程序員太累了吧。。。這就造成了存儲型XSS漏洞。

這還沒什么,關鍵是上面的代碼仔細看的話,會發現大都沒過濾,那么就可以用各種姿勢的存儲型XSS往里面插入。
msn也可以插入">,插入之后,在點擊編輯資料,那么就會直接出現彈窗。下圖是我關閉彈窗之后的樣子。
生成的代碼看我F12.

其他幾個變量就不一個一個插入了。原理都是一樣的。存儲型XSS在這個頁面泛濫。
5、ip地址偽造漏洞,還可以注入
這個漏洞,實際上其他文章里面都寫了...
我就說一下自己挖到這個洞的經歷吧。
地毯式搜索用戶輸入,每個文件都看。一般在常用函數里面用戶輸入比較少,但是也存在這個可能性。結果一查看,還真找到了一個。

為了驗證這個漏洞是否只是偽造IP,還是說又存在注入,就再去追蹤這個函數。
結果第一個就發現可以偽造IP,但是這個還只是偽造IP

我看過的那篇文章中,利用這個點進行了注入,獲取到了管理員信息。
文章地址:https://chybeta.github.io/2017/03/14/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E4%B9%8BSQL%E6%B3%A8%E5%85%A5%EF%BC%9ABlueCMSv1-6-sp1/
原理我就簡單說一下吧,畢竟這個洞別人都挖好了,還利用的很漂亮,技巧學到了。
原理就是,既然getip()是可控的,那么只要在getip()處的輸入為:
1','1'),('','6','2','1','6',(select concat(admin_name,':',pwd) from blue_admin),'1','1
那么1','1')就是完成第一次插入,后面的則是完成第二次插入。至于回顯的位置,只能自己判斷了。
因為我的blueCMS有問題,很多功能無法實現,故這里沒辦法演示。
那篇文章地址我已經提供,感興趣的可以去看看。
總結:
作為一個代碼審計的新手,這雖然是我的第一次實戰,但是,當我靠自己實力挖到第一個洞的時候(盡管不是高危0day,只是小漏洞),內心的喜悅是難以言表的。
而我相信,代碼審計,只要你有耐心,細心,恒心,你肯定能成為一個合格的代碼審計人員。
希望這篇文章能對剛入門代碼審計的新手、或想入門PHP代碼審計的人,有一定的幫助。