<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/tips/879

            題記:這是09年自己寫的總結文章,之后多年也不搞這個了,技術顯然是過時了,但我覺得思路還是有用的,算拋磚引玉吧,各位見笑?

            0x00 前言


            這是一篇學習總結,首先對幾位未曾謀面也不知道名字的老師表示感謝,通過對你們大作的學習,使我逐漸入門開始跨入XSSer的行列,雖然我現在的水平和大師們比起來還差得太遠,腳本方面的基礎也不不行,但我會繼續努力學習。

            0x01 概述


            曾經有一度,在N年前,我對網絡安全對抗的總結就是“過濾與反過濾”,雖然現在看起來這種理解太狹隘了,不過在很大程度上過濾和突破過濾確實是很重要的手段。安全的目的就是過濾掉有害信息,保存安全的信息,而黑客的目的就是突破過濾把有害信息傳遞過去。無論是SQL注入、XSS、溢出等等,最最核心的東西就是突破過濾。本文主要講針對存儲型XSS過濾器的fuzz。

            0x02 XSS filter:安全域邊界


            隨著XSS攻擊越來越流行,對XSS的過濾也越來越成熟,尤其是存儲型XSS,由于攻擊更加靈活多變,應用更加廣泛,所以過濾手段也更復雜,從最早的黑名單字符串過濾,發展到現在基于語法分析的黑白名單相結合的過濾器,對攻擊的難度要求也越來越高。

            由于對反射型XSS的過濾相對簡單,所以本文只討論存儲型XSS過濾。現在的存儲型XSS過濾大多是基于HTML語法進行過濾,為什么不用簡單的字符串過濾呢?這是因為HTML支持的編碼方式非常多,而且要區分哪些是HTML語言哪些是文本內容也很麻煩。例如:

            #!html
            <div style=width:expression(alert(0))></div>
            

            是要過濾的,而

            #!html
            <div>style=width:expression(alert(0))</div>
            

            是正常的內容,所以必須進行HTML語法分析。

            filter另一個要考慮的問題是用戶體驗,一些很嚴格的過濾器雖然相對安全,但是對正常內容的改變太大,頁面都變樣了,正常用戶使用起來感覺不太好。其實大多數XSS filter的漏洞并不是不能發現XSS,而是發現以后不能完全清除,在某些方面看來也是為了考慮正常用戶的使用感受。

            基于HTML語法分析的filter會把輸入內容劃分成很多不同的安全域,例如HTML標簽之外的內容不過濾,是最低安全級別,在HTML之內也要劃分,樣式表是容易出問題的地方,要重點掃描,甚至考慮用白名單,即使在樣式表內部也要劃分不同的區域使用不同的掃描級別。因為不同的安全域有不同的檢測方法,所以容易出問題的地方就兩個安全域之間的邊界,一旦邊界搞錯了就有可能突破過濾。

            0x03 fuzzer的設計:突破邊界


            當一些普通的嘗試無法成功突破filter,就要考慮用fuzzer。首先要設計測試模型,這也是最核心的問題。我經常想為什么fuzzing能夠成功呢,一定是filter存在某些隱藏比較深的漏洞,例如前面談過的安全域邊界,所以我們設計fuzzing模型就重點對這些地方進行測試,舉個例子說明一下安全域邊界的問題:

            #!html
            <div style="width:expression(alert(9));">
            

            其中:

            expression(alert(9))
            

            是重點掃描區域也就是最高安全級別區域,對這個區域應該過濾/* */expression等,filter怎么確定這個區域呢?首先在div標簽內找到style屬性,在=后面找到""之間的內容,根據:來劃分樣式名稱和內容,以;分隔幾個樣式。那么在這里=":;這些都是關鍵字。如果XSSer提交以下內容:

            #!html
            <div style="width:expre/*"*/ssion(alert(9));">
            

            最高安全區域還應該是

            expre/*"*/ssion(alert(9))
            

            但是如果filter在劃分區域的時候首先根據第一個封閉的雙引號對來劃分,那么就會針對下面的內容做過濾:

            width:expre/*
            

            這樣顯然是不正確的,因為/* */中的內容是注釋,是要丟棄的內容,如果先根據第一個雙引號就確定了區域邊界的話,那么在綠色區域也是過濾不到expression的,就會導致過濾被繞過。所以在這里正確的確定邊界的方法是找到最后一個雙引號來進行封閉,如果沒找到的話還要自動添加,否則又會造成高級別安全區域的擴大,擴大了并非會更安全,安全區域的擴大會引起后面的邊界混亂,同樣會造成漏洞。當然了,這個情況只是我想象出來的用來舉例的,實際情況不太可能這么簡單,這里要說明的只是區域邊界對于XSS fuzzing的重要性。

            因此我們設計fuzzer的時候就要在一個模版的有可能導致邊界混亂的地方添入一些元素組合來作為testcase,所以第一要考慮的是確定模版,例如我們可以把

            #!html
            <div style="width:expre/*位置*/ssion(alert(9));">
            

            作為模版,元素填充在/**/之間。這樣就有了一個簡單的fuzz模型。

            當然了這并不是一個好的模版,因為不夠復雜,filter一般都會考慮到了。

            那么在前邊加點東西:

            #!html
            <div id="位置" style="width:expre/*位置*/ssion(alert(9));">
            

            這樣有兩處地方填充元素,稍微復雜了一點,比第一個模版好點。我的想法是,越復雜的模版和越多的填充位置、填充元素,才越有可能跑出漏洞。但實際應用中因為我們是黑盒測試,所以還要考慮其他一些問題,例如效率、可識別性等等,這個后邊再說。

            有了模版,用什么樣的元素進行填充,就是我們要考慮的第二個重要問題。首先邊界元素肯定是要的,例如上邊的例子里面的邊界元素

            =":;
            

            能想到的還有

            空格、<、>、</div>
            

            等,有時候雖然看起來不可能確定錯邊界,但實際上filter的行為往往出人意料,這也正是fuzzing存在的意義。

            除了邊界元素之外還有一種要考慮的是filter過濾的元素,例如filter過濾expression,我們就把expression也當做隨機填充的元素,還有//、onXXX()等,當filter刪除或改變這些內容后就有可能會導致邊界的改變。

            另一個元素是不可視的特殊字符,例如\t、\r、\n、\0等等,半個UNICODE字符等。還有被filter反向解碼的字符串,例如&#XX、%XX、\XX等。

            最后要考慮的確定模版和元素的情況是瀏覽器的undocument行為,例如

            #!html
            <<div/style="width:expression(alert(9))">
            

            會被IE正常解析。這里有兩個<,而且div后面用/分隔而不是空格,某些filter可能考慮不到這樣的行為,因為這樣寫不符合HTML語法規范。類似這樣的瀏覽器的奇怪解析方式還有很多,去年分析過YAHOO Mail跨站的DX都會對CSS里面的不正常的url()記憶猶新吧,我挺佩服發現這種方式的那位老師的,這個的問題看起來似乎并不是僅僅是filter的問題,連IE對邊界的劃分都是存在問題的,當然這種問題單單對IE來說只是個小BUG而不能算漏洞,但是結合了YAHOO的filter之后卻導致了XSS。

            0x04 實踐練習:本地fuzzing的例子


            在實際進行遠程黑盒fuzzing之前我們先來構造一個本地fuzzing的例子來練習一下,看看模版的構造和元素的選擇能否達到預想的效果。

            我選擇了htmLawed作為fuzzing對象,這是一個php寫的開源的HTML過濾器,有興趣的話可以先到http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawedTest.php這個網站,先手工測試一下看看能不能手工繞過它對CSS的過濾,也許大牛是可以手工XSS的,反正我試了一會沒有成功,沒辦法,智商就只有這么多。這里我說的是上面那種例子里的樣式表XSS,你可別直接輸入一個<script>人家是不過濾的,因為這個filter還要加參數。

            我下載了htmLawed之后,在本地做了一個測試程序:

            #!php
            <?php
            include './htmLawed.php';
            $m1=array("'","\""," ","");
            $m2=array("","","\"","'","<","","","","","","","","");
            $mag=array("'","\""," ","</div>","/*","*/","\\","\\\"","\\\'",";",":","<",">","=","<div","\r\n","","&#","/","*","expression(","w:expression(alert(9));","style=w:expression(alert(9));","");
            for($i=0;$i<10000;$i++)
            {
            $fname = "tc\\hush".$i.".html";
            $fp = fopen($fname, "a");
            $mtotran = "";
            for($j=0;$j<1000;$j++)
            {
            shuffle($mag);
            shuffle($m1);
            shuffle($m2);
            $mstr=$m2[0];
            $mstr.="<div id=";
            $mstr.=$m1[0];
            $mstr.=$mag[0];
            $mstr.=$mag[1];
            shuffle($mag);
            $mstr.=$mag[0];
            $mstr.=$m1[0];
            $mstr.=" style=";
            shuffle($m1);
            $mstr.=$m1[0];
            $mstr.="w:exp/*";
            shuffle($mag);
            $mstr.=$mag[0];
            $mstr.=$mag[1];
            $mstr.="*/ression(alert(9));";
            shuffle($mag);
            $mstr.=$mag[0];
            $mstr.=$mag[1];
            $mstr.=$m1[0];
            $mstr.=">".$j."</div>\r\n";
            fwrite($fp, $mstr);
            $mtotran.=$mstr;
            }
            fclose($fp);
            $outcont = htmLawed($mtotran);
            // print $outcont."\r\n";
            $fp1 = fopen("C:\\Inetpub\\wwwroot\\out\\hush".$i.".html", "a");
            fwrite($fp1, "<HTML>\r\n<HEAD>\r\n<TITLE>".$i."</TITLE>\r\n<meta http-equiv=\"refresh\" content=\"1;url=hush".($i+1).".html\">\r\n</HEAD>\r\n<BODY>\r\n");
            fwrite($fp1, $outcont);
            fwrite($fp1, "</BODY>\r\n</HTML>");
            fclose($fp1);
            print $i."\r\n";
            // break;
            }
            ?>
            

            各位見笑了,我沒寫過php,現學了一下攢出個程序很爛,僅僅是實現我的思路而已,沒有考慮效率啊穩定之類的。程序功能很簡單,按照前面提到的簡單模版隨機填充進一些元素,生成testcase,然后用htmLawed進行過濾之后生成結果文件,放在WEB服務器上讓他自動運行,看能否彈出alert。前面說過了這個模版不怎么好,太簡單,所以我多生成了一點,總共下來是一千萬個用例,結果訪問第一個文件就彈出了alert對話框0_0。可見htmLawed也不是一個久經考驗的filter。

            我看了一下通過的用例,發現其實很多方法可以繞過htmLawed的過濾,這里舉一個簡單的例子:

            #!html
            <div id= \""&# ?style="w:exp/*\\'<div*/ression(alert(9));'=">722</div>
            <div id="\">/" style="w:exp/*&#*/ression(alert(9));&#</div>">723</div>
            

            這兩條一起傳給filter之后,過濾結果為:

            #!html
            div style="w:exp ?'<div*/ression(alert(8));'=">722</div>
            <div>/" style="w:exp/*&#*/ression(alert(9));&#</div>">723
            

            就會彈出alert了,證明已經繞過了過濾。經過分析發現,主要原因在于對<div標簽的過濾有問題,當同時存在兩個<div的時候,會保留第二個舍棄第一個,這樣導致了原有的安全域邊界改變了。再結合第二條div中的雙引號封閉前面的內容,所以本來第二條的style應該是標簽之外的內容的低安全級別域,沒有過濾/**/和expression,和前面結合之后就進入了高安全級別域的范圍里面,導致了XSS。是不是看得有點頭暈?這就證明了fuzzing能夠做到人腦所做不到的事情(我這里說的是普通人腦,大牛們的腦子除外)。

            0x05 實戰:遠程fuzzing


            剛才看到本地fuzzing的例子其實也是黑盒測試而不是白盒測試,因為我們是不考慮filter源代碼的,也不是直接用程序加載filter來跑,我們關系的只是過濾結果。以前看過一篇老外寫XSS fuzzing的文章,說破解一個遠程filter系統,先在本地模擬該filter的行為,然后直接在程序里邊跑,根據跑出的結果去遠程驗證,在根據遠程的結果修正本地模擬filter,不斷這樣修正,直到最后完全在本地實現遠程filter的所有特性。最后直接在程序里面fuzzing本地filter。這種方法聽起來是很不錯的,因為在程序內部進行fuzzing效率是非常高的,每秒鐘可以測幾萬次甚至幾十萬次,如果遠程fuzzing的話有時候一分鐘都測不完一次,就算一次發過去幾百個testcase,算上生成用例時間,發送接收時間,驗證結果時間,總共平均下來也是非常耗費時間的。但是,實際做起來就會知道本地模擬遠程filter的方法更多的是紙上談兵,因為僅僅根據輸入輸出結果是模擬不了fliter程序的全部特性的,也會漏掉絕大多數的漏洞。

            所以要想真正跑出漏洞還是得靠遠程fuzzing,那么就需要考慮測試效率的問題了,一個fuzzer的好壞我覺得有兩個要素:首先是測試模型的設計,再一個就是fuzzer本身的效率。就算用很爛的測試用例,如果fuzzer效率足夠高的話,還是有可能跑出漏洞的。

            如何提高測試效率呢?這是我一直在思考的問題。比如我們fuzzing的對象是一個WEB郵件系統,那么fuzzer的基本設計應該是這樣設計:根據模版生成testcase->發送testcase->驗證結果。那么提高效率也要從這三個方面入手。

            首先是生成testcase的模塊,我一般式根據隨機數來選擇元素填充到模版,隨機數的生成就是一個很關鍵的要素,我們需要的盡量均勻的隨機數,這樣就會減少重復數據,能夠在一定程度上提高測試效率。用C語言來生成均勻分布的隨機數是很困難的事情,生成的隨機數總是不斷重復,又要判斷很浪費時間,最后還是用python感覺好多了,可以用string.join和random.sample生成出隨機字符串組合。

            其實生成testcase的效率還要依賴于發送效率,因為即使每秒生成一萬個用例,但每分鐘只能發10個,那么生成再多也是浪費。所以更重要的是發送效率,這里我也沒想到什么好辦法,只能是采取組合發送的策略,就像前面fuzzing htmLawed一樣,把1000乃至更多用例組合起來,這樣做的好處不僅僅是提高效率,而且因為組合之后提高的混亂程度所以往往能產生意想不到的過濾結果。組合發送的方式是考慮效率優先的,但有時候我們也需要一條一條發送,這是為了能夠精確查看每次的過濾情況,看看用例在filter處理的過程中會產生怎么樣的變化,因為filter每一次的替換或者刪除都有可能導致安全邊界的改變。

            前邊兩個模塊的效率都是次要的,呵呵,最最主要的其實是驗證結果的效率,這個往往要根據目標的情況來決定最佳的驗證辦法。可以手工去驗證,人工打開瀏覽器去點擊接收到得內容,但是效率極低,對于幾萬幾十萬的測試用例來說顯然是不適合的。也可以通過程序自動化驗證,常用的方式一般有兩種:一種是通過程序模擬瀏覽器從WEB應用接收結果,然后判斷是否存在特征字符串,來驗證是否成功。另一種是借助瀏覽器打開輸出頁面來驗證,程序只控制IE依次訪問發送用例,可以用模擬鼠標鍵盤的方式。前一種方法的好處是效率高,但是容易誤報漏報,像前面fuzzing出的htmLawed漏洞一樣,非常不容易通過程序來判斷。后一種方法雖然效率很低,但是100%不會漏報。所以我一般多采取第二種方法。其實還有第三種方法,就是前兩種方法的組合,先寫程序從WEB獲取內容,然后在本地生成可以自動依次打開的html文件,然后用IE打開。這樣當然是比較完美的情況,但是設計編寫程序也是很麻煩的,對于一般的目標不太值當費這個勁,對于某些目標嘛,呵呵,還是值得弄一套程序出來的。這里還有個基本要求就是能夠找到過濾之后字符串所對應的原始輸入,只要考慮到了實現起來也不難。

            說了半天還沒有舉例子呢,這個因為某些原因,我就不寫實際遠程fuzzing的代碼和過程了,聰明的讀者可以自己去試一下,我對國內外一些常用的WEB郵箱進行了測試,成功fuzzing出XSS漏洞的郵箱有:

            國外:

            @y*.com
            @h*.com
            @aol.com
            @hanmail.com
            @fastmail.fm
            @hushmail.com
            @epochtimes.com
            

            國內:

            @1*.com
            @si*.com
            @so*.com
            @t*.com
            @21*.com
            @q*.com
            

            (別當真,都是過去時了)

            其實國內的大多數不是fuzzing出來的,而是手工測試出來的,因為國內郵箱的filter還比較初級,過濾得很不完善,一些最簡單的小技巧就能騙過filter了。當然后來用fuzzer測試的時候又發現了更多漏洞。

            0x06 思考:如何做出完善的filter


            如果一篇文章只寫攻擊不寫防范是會被同行鄙視,尤其到了我這把年紀的人。那么通過fuzzing測試對于改進filter能夠有什么幫助呢?如果每個公司對自己的產品做足夠的fuzzing測試,而不是又黑客來測試的話,我想會大大的改進產品安全性。更何況公司自己掌握著源代碼,可以把fuzzing測試的效率提高幾個數量級。很多公司我想也是有這方面測試的。但是為什么還是會有漏洞呢?大概是因為很多公司的開發測試人員不是搞安全出身的,至少是不精通安全,從另一個角度來說某些網絡公司對其產品安全的重視程度是不夠的。還有一方面是利益驅動的問題,微軟自己測不出來的漏洞黑帽子也都能挖出來,這就不用我細說了。

            那么到底應該怎樣設計filter才能盡量減少漏洞呢?談談我自己一點看法大家討論。首先一點要明確安全邊界,并盡量使用白名單方式進行過濾,這個很多filter都做到了。其次在邊界明確的基礎上要明確對于違規數據的處理辦法,最最安全的辦法是一旦發現違規數據,整條數據包丟棄,當然在實際應用中是不能這樣做的,因為會大大影響應用的用戶體驗。那么就要針對違規數據的安全域進行處理,前面已經說過了,無論刪除還是替換,都存在一定風險,可能會對其他的安全域邊界造成改變。這就要求必須有重審機制,對于違規處理過的數據還必須再次判斷每個安全域邊界,不斷循環直到沒有發現任何違規數據,這樣做有可能會導致另一個風險是DoS攻擊,就看如何取舍了。

            不過只要是人寫的程序就會有疏忽的地方,就有可能出漏洞,所以要兩方面相結合,第一,開發filter的時候明確安全規范,不要做想當然的事情,對于已經發現的漏洞修補之后要看看是否符合原來的安全要求,因為很多時候因為補老漏洞又產生了新漏洞。第二,要做盡量全面的黑盒測試,前提是必須有懂安全的人介入測試部門。做到這兩點仍然不能百分之百避免漏洞的產生,所以還要有漏洞發現機制,靠用戶報告只是一方面,另一方面還必須有自動化的漏洞監測機制,這種事情說起來容易做起來難,所以就不多說了。

            參考資料:

            不知名大牛的yahoo XSS樣本 Blackbox Reversing of XSS Filters(Alexander Sotirov) WEB應用安全設計思想(axis)

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

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

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

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

                      亚洲欧美在线