從黑客角度深入剖析跨站請求偽造
在對Web應用程序進行測試的過程中,我們使用的自動化工具有時候會漏掉一些非常嚴重的安全漏洞。跨站請求偽造便是其中之一。因此,對于Web應用測試來說,通過人工方式分析代碼是非常重要的一種測試手段。
在近期的Web應用測試過程中,我利用手工方式發現了若干個CSRF漏洞,如果將其串聯使用,就能演變成更加嚴重的安全漏洞。下面,本文將詳細介紹這些漏洞的發現過程,并演示如何利用它們來控制受害者的賬戶。需要注意的是,為了保護客戶信息,部分內容已經做了模糊處理。
0×01 艱難的發現之旅
為了展開跨站請求偽造攻擊,首先要進行一些偵查工作。為此,我根據客戶提供的低權限賬戶登錄了需要測試的Web應用。在這個網站上,大部分頁面都要求預先知道帳戶名、產品序列號等信息。由于我對這些一無所知,所以只好轉而考察賬戶簡介頁面。這個頁面允許用戶更新他們的帳號信息,例如姓名、電子郵件地址、城市、省份,等等。
對于瀏覽器,我通常選擇Firefox,這是因為可以使用TamperData插件來方便地操作表單的字段,來觀察是否發生有趣的事情。比如,我經常在引號或星號之間插入一些奇怪的數據,來探索各種注入攻擊。就本例而言,我發現了一個會隨其他賬戶信息一同傳輸的隱藏表單字段,這個字段的名稱為_RequestVerificationToken。我不斷擺弄這個字段,但是就我觀察而言,并沒有找到注入漏洞。但是,我卻注意到了一個重要的事情,那就是這個站點好像并不在意我對這個字段所做的各種修改。
這對我來說是非常奇怪的。根據這個字段的名稱來看,它好像是用來驗證每個提交的請求是否來自該網站實際用戶的一項安全措施。這通常是用來防止跨站請求偽造(CSRF)攻擊的,但是,這個頁面看上去并沒有驗證這個令牌,這就意味著這個信息更新表單容易受到CSRF的影響。
在默默記住這一點之后,我開始繼續測試該網站的其他部分。我想,如果一個頁面有這種漏洞,那么其他頁面也很可能有這種漏洞。不幸的是,事實并非如此。對于所有其他頁面,只要修改這個令牌就會導致服務器錯誤,并且表單也無法進行處理。也就是說,除了個人信息頁面之外,其他地方的大門都被堵上了。接下來,我們就要想辦法利用它來進行更加邪惡的事情。
0×02 忘記密碼
就像某些網站一樣,這個站點也提供了一個“忘記密碼”功能。這個功能的初衷,當然是為哪些忘掉密碼的用戶提供幫助的。這個自動化系統非常簡單,用戶將其用戶名輸入到相應的表單字段,然后點擊提交按鈕即可。如果用戶名是有效的,系統就會給該用戶的賬戶生成一個新的隨機密碼,并將其發送至該用戶賬戶綁定的電子郵箱中。這個功能不僅對于用戶來說非常簡單,同時,也為黑客提供了極大的方便。
我意識到,如果我可以利用CSRF漏洞把用戶的電子郵件地址更新為自己的郵箱地址,那么,我就能夠利用忘記密碼功能令系統把用戶的密碼發給我,而不是用戶。不幸的是,我很快就遇到了一個難題。
客戶(這里指example.com)提供給我的測試帳戶所綁定的電子郵件地址中含有客戶的域名,如bob@example.com。雖然系統允許將這個電子郵件地址改為我想要的地址,但是這里有一個問題,當讓系統向我的電子郵箱發送新密碼時,實際收到的卻是一個服務器錯誤。不知道什么原因,它似乎只喜歡類似bob@example.com這樣的郵箱。
經過進一步的手工測試后,我發現它可以接受任何用戶名,只要是以example.com結尾即可。我不知道到底為何會發生這種情況,因為在系統中的其他帳戶包含了來自不同域的電子郵件地址。我覺得,這背后肯定還進行了某些額外的安全驗證,比如根據白名單對比電子郵件地址的域名等。但是,我的個人電子郵件地址或域名肯定是不在這個表中的。因此,我們需要借助于下面介紹的技巧。
0×03 Gmail來解圍
我有一種預感,即電子郵件地址的白名單匹配過程是可以破解的,后來事實證明,我的感覺還是很準的:我發現,任何電子郵件地址都是可以繞過白名單的,只要該地址的郵箱名稱部分含有“example.com”字樣即可。舉例來說,example.com@otherdomain.com就完全可以達到我們的目的。因此,我們只需要建立一個郵箱名部分含有example.com的電子郵件賬戶就行了。
現在,該Gmail上場了。之所以選擇Gmail,是因為它允許Gmail用戶名之后、@符號之前使用加號,這個特性對我們來說非常有用。舉例來說,如果你的電子郵件地址是rick@gmail.com,那么你也可以使用rick+test@gmail.com來接收郵件,實際上郵件還是會發送到rick@gmail.com郵箱中,因為Gmail會忽略加號和@符號之間的所有內容。
考慮到這一點,我又登錄到Web應用程序進行測試,并把帳戶的電子郵件地址更新為“rick+example.com@gmail.com”。果然,這種方法確實是可行的,密碼重置功能給我發來了一個新的密碼。既然如此,下面就該從poc驗證這個CSRF漏洞的可利用性了。
0×04 一個簡單的Web表單
由于這個有漏洞的應用使用POST方法來提交數據,所以我必須構建自己的表單來達成目標。由于這里只是一個poc,所以不要求太過精致。我建立的這個表單含有一些隱藏的文本字段,當個人簡介更新時,所有的表單字段都會被傳送給這個Web應用。這其中包括一個CSRF令牌,不過由于這個應用沒能有效對其進行驗證,所以該令牌的值實際上是無關緊要的。這里最為重要的部分是EmailAddress字段,雖然這個字段是隱藏的,但是卻包含一個重要的缺省值,即rick+example.com@gmail.com。
- <form action="https://www.example.com/account/EditProfile/" id="EditProfile" method="post" name="EditProfile"> <input name="__RequestVerificationToken" type="hidden" value="ANYTHING CAN GO HERE" />
- <input id="PasswordChgRequired" name="PasswordChgRequired" type="hidden" value="No" />
- <input id="UsersName" name="UsersName" type="hidden" value="ANITIAN" />
- <input id="returnURL" type="hidden" />
- <input class="userName text-box" id="FirstName" maxlength="25" name="FirstName" style="width:160px" type="hidden" value="Anitian" />
- <input class="userName text-box" id="LastName" maxlength="25" name="LastName" style="width:160px" type="hidden" value="Anitian" />
- <input id="Phone" name="Phone" style="width:160px" type="hidden" value="" />
- <input id="Phone" name="Phone" type="hidden" value="" />
- <input id="PhoneExt" maxlength="5" name="PhoneExt" style="width:160px" title="1 to 5 numbers. " type="hidden" value=" " />
- <input id="PhoneExt" name="PhoneExt" type="hidden" value=" " />
- <input class="text-box" id="City" maxlength="20" name="City" placeholder="Office Location" style="width:160px" title="Enter the city of your office location." type="hidden" value="Winterville" />
- <input id="EmailAddress" maxlength="50" name="EmailAddress" style="width:394px" title="Email is limited to 50 characters." type="hidden" value="rick+test.com@gmail.com" />
- <input class="jobFunc" id="JobFunction2" name="JobFunction2" type="hidden" value="Vice President" />
- <input class="jobFunc" id="JubFunction" name="JobFunction" type="hidden" value="ACCOUNT HACKED">
- Click this awesome button!
- <input id="submit" type="submit" value="Save" />
- </form>
在這個表單中,唯一可見的部分就是一個提交按鈕。為了進行poc,我將這個新的網頁加載到了自己本地的計算機上。對于實際的攻擊者來說,他們可以將其托管到互聯網上的某個Web服務器上面,并且很可能是他們已經成功入侵的系統上所搭建的服務器。一旦加載之后,受害者將會看到如下所示的頁面:
當然,這個頁面看起來有些簡陋,但是對于poc來說已經足夠了。接下來,我們必須自己來扮演受害者了。為此,我首先打開了瀏覽器,并使用正常賬號登錄上這個Web應用,這一切跟正常用戶沒有什么區別。然后,我將前面的惡意表單頁面上載到了另一個標簽中。攻擊者很可能會利用電子郵件將這個鏈接發給我,然后,我單擊了這個按鈕,象期望的那樣,攻擊成功了。
這個惡意網頁會把所有信息遞交給了目標站點,并按照要求更新了電子郵件地址字段。需要引起注意的是,即使目標頁面已經在受害者瀏覽器中被關閉的情況下,這次攻擊依然能夠得手,因為只要用戶的會話依然是打開的,那就足夠了。許多時候,一個會話可以在很長一段時間保持打開狀態,即使網頁關閉之后亦是如此。現在,攻擊者唯一需要去做的就是,利用密碼重置功能通過電子郵件獲得受害者(這里就是我們自己)的新密碼了。
這樣一來,受害者的賬號就完全處于我們的控制之下了。#p#
0×05 進一步改進攻擊手法
需要注意的是,該攻擊手法具有兩個缺點,一是要求受害者參與其中, 因為受害者至少需要訪問一個惡意頁面,這樣該手法才能成功。就本例來說,受害者還必須按下一個按鈕,不過,利用Javascript可以實現在頁面加載時自動完成表單的提交動作。
另一個問題是,你必須知道受害者的用戶名。為了利用忘記密碼功能,我們必須知道受害者的用戶名才行。幸運的是,我發現了另外一個辦法,可以巧妙地繞開這個問題。
0×06 隱秘的表單字段
我注意到,個人簡介的更新頁面還會提交一個用于UsersName的字段。這引起了我的興趣,因為頁面本身是無法修改這個用戶名字段的。這是一個靜態的條目,即總是保持不變。出于好奇,我決定再次嘗試通過Tamper Data來對這個變量進行修改。讓我驚訝的是,它居然奏效了。實際上,該網站不僅接受了我改過的用戶名,而且連該帳戶的用戶名也改過來了。當然,我們無法將用戶名更新為業已存在的那些用戶名,但是我們可以更新為隨機選擇的、不常用的用戶名,這是很容易成功的。
為了利用這一點,我們只需更新自己的惡意頁面中的表單,讓其username字段包含一個新的、當前未曾使用的值即可。現在,當受害者點擊了該提交按鈕之后,不僅他們的電子郵件地址會被更新,而且就連他們的用戶名也會變成我們之前設定的值。這就意味著,這個鏈接可以發送給任何已經登錄該網站的人,他們的賬戶都可能會失竊。再進一步,我們甚至可以將這個過程腳本化,這樣就可以成批地把不同的表單發送給不同的人員。只要他們輕輕點擊一下,就會全部中招。
0×07 如何保護自己
那么,我們如何保護自己免受此類攻擊的威脅呢? 實際上,這種攻擊之所以會發生,主要是由于兩個核心漏洞導致的,下面我們就介紹如何處理這個問題。
在這里,最嚴重的問題就是CSRF攻擊,如果沒有它,就不會發生后面的事情了。為了防御CSRF攻擊,OWASP組織已經提供了多種不同的方法,不過最為安全的方法就是在隱藏字段中使用一個唯一的令牌。這也是我們這個示例網站所試圖去做的。如果能夠徹底實施的話,CSRF攻擊的得手機會就會大大降低。但是,只要有一個頁面沒有徹查該令牌的話,攻擊者仍然有機可乘。這是因為對于他們來說,只要有一個漏洞就足夠了,并且不要忘了,那些心懷不軌的家伙們可有的是時間。
另一個問題與用戶輸入驗證有關。盡管這個系統看上去會利用某種白名單來檢查電子郵件地址,但是這種方法是無效的。它只是檢查電子郵件地址中是否存在白名單中的域名而已,只要有就行,不管出現在什么地方。實際上,它應該重點檢查電子郵件地址的最后部分,或者對整個電子郵件地址進行更加嚴格的檢查。此外,更為安全的做法是,當用戶更新個人簡介信息時,應該要求他們重新輸入自己當前的密碼。當然,在重新輸入密碼的時候,還可以加入CAPTCHA驗證,因為惡意Web表單是無法自動識別CAPTCHA的。
最終的漏洞還涉及到用戶輸入內容的驗證。根據個人信息更新頁面的設計來看,這個頁面好像并沒有打算讓用戶更新他們的用戶名。在原始的表單中,用戶名字段是不允許編輯的。如果果真如此的話,那么這個表單根本就不應該將用戶名作為一個字段進行提交。即使由于某種原因,必須提交這個字段的話,那么該頁面也應該進行重寫,以禁止編輯用戶名。
0×08 小結
CSRF攻擊是攻擊者用來訪問你的數據的方法之一。不過,只掃描站點漏洞是遠遠不夠的。這需要仔細審查該網站運作方式,以檢測更細微的瑕疵。本文通過實例說明,我們不僅能夠找出這種缺陷的,并且還能幫助客戶防御此類攻擊,從而避免出現災難性的數據破壞。