- CTF實戰:技術、解題與進階
- ChaMd5安全團隊
- 2111字
- 2023-08-28 18:13:20
1.2.3 XSS進階
1.CSP
內容安全策略(Content Security Policy, CSP)是一種計算機安全標準,由Robert Hansen于2004年提出,首先在Firefox 4中實現,并很快被其他瀏覽器采用。CSP用于防止XSS、點擊劫持和其他由于在受信任的網頁上下文中執行惡意代碼而導致的代碼注入攻擊。CSP為網站所有者提供了一種標準方法來聲明允許瀏覽器在該網站上加載資源,如JavaScript、CSS、HTML網頁等。
CSP可以通過兩種方式進行設置,在HTTP的消息頭中設置,或者在HTML的Meta標簽中設置。正常的CSP配置由多組策略組成,每組策略包含一個策略指令和一個內容源列表,如表1-3、表1-4所示,每組策略之間由分號分隔。CSP主要是通過限制JavaScript的執行、限制跨域請求來防御XSS的。
表1-3 CSP指令

表1-4 CSP指令值表

具體配置參見https://developers.google.com/web/fundamentals/security/csp/。
舉個例子來看看CSP的效果。未設置CSP時,代碼如圖1-68所示。

圖1-68 未設置CSP的代碼
成功加載圖片后的效果如圖1-69所示。

圖1-69 成功加載圖片
我們配置一個常見的CSP,代碼如下。允許執行內聯JavaScript代碼,但不允許加載外部資源。

可以看到,配置完CSP之后,獲取圖片失敗了,如圖1-70所示。

圖1-70 CSP策略生效
2.繞過CSP
下面介紹如何利用CSP的配置缺陷。
在真實的開發環境中,常常不得已需要執行內聯,我們可以借此執行內聯JavaScript,當然也可以利用location跳轉帶外數據,代碼如下。

利用unsafe-eval錯誤配置的代碼如下。

不允許加載外部資源,并且不允許加載內聯JavaScript代碼,但是配置了unsafe-eval指令值,使用了data配置,可通過Base64編碼Payload,代碼如下,結果如圖1-71所示。


圖1-71 成功繞過CSP
如果網站設置了script nonce,在無法猜測nonce值且base-uri沒有被設置的情況下,可以使用base標簽設置默認地址為我們構造的惡意服務器地址。如果頁面中的合法script標簽采用了相對路徑,那么最終加載的JavaScript代碼就是針對base標簽中設置的默認地址的相對路徑,如圖1-72所示。

圖1-72 成功繞過
這樣就會默認加載我們構造的惡意服務器上的main.js。
我們也可以通過link標簽進行預加載。如下代碼是一個簡單的CSP規則,不允許加載外部資源,我們用img標簽引入baidu.com的圖片時就會被阻止,如圖1-73所示。


圖1-73 不允許加載外部資源
可以通過link標簽的預加載來繞過,代碼如下。大部分瀏覽器都約束了該標簽,如圖1-74所示。


圖1-74 通過link標簽的預加載繞過
外帶數據可以使用如下代碼繞過。

在瀏覽器的機制上,跳轉也算是一種跨域行為,并且不受CSP約束,可以通過跳轉繞過CSP,帶出我們要的數據。部分Payload可參考以下代碼。

CSP中原本有sandbox和child-src限制iframe的行為,但是當一個同源站點存在A頁面和B頁面,且A頁面有CSP保護,而B頁面沒有CSP保護時,我們可以通過B頁面新建iframe嵌套A頁面,這樣就可以繞過A頁面的CSP獲取A頁面的數據,如圖1-75所示。

圖1-75 通過iframe標簽繞過
通過站點允許訪問的資源來構造XSS,最經典的案例就是利用www.google.analytics.com中提供自定義JavaScript代碼的功能(因為Google會封裝自定義的JavaScript,所以還需要unsafe-eval字段),繞過CSP,代碼如下,如圖1-76所示。


圖1-76 利用站點可控靜態資源繞過
瀏覽器解析html標簽的規則為遇到左尖括號標簽開始解析,直到遇到右尖括號結束,兩者之間的數據都會被當成標簽名或者屬性。
在某些場景下,利用這個規則我們可以劫持別的標簽的屬性,代碼如下。

CSP為不允許請求外部資源,僅允許屬性nonce為test的標簽執行JavaScript代碼。

我們構造Payload:?xss=%3Cscript+src=data:text/plain,alert(1),結果如圖1-77所示。

圖1-77 繞過不完整的script標簽
如圖1-77所示,Payload拼接到頁面上就是成功劫持了nonce='test'屬性,并且執行了我們構造的alert函數。

JSONP其實就是一個跨域解決方案,JavaScript是不可以跨域請求JSON數據的,但是可以跨域請求JavaScript腳本。我們可以把數據封裝成一個JavaScript語句,做一個方法的調用,跨域請求JavaScript腳本可以得到此腳本。因為程序得到JavaScript腳本之后會立即執行,所以把數據作為參數傳遞到方法中就可以獲得數據,從而解決跨域問題。為了便于客戶端使用數據,我們發明了一種非正式傳輸協議,JSONP。該協議的一個要點就是允許用戶傳遞一個callback參數給服務端,服務端返回數據時會將這個callback參數作為函數名來包裹JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了。
JSONP是用來解決跨域問題的,能夠繞過CSP,構造如下Payload。如果返回的數據為JSON格式,此方法就無法利用了。

3.XSS升級為RCE
瀏覽器一般對JavaScript調用系統命令設置限制,但是在一些客戶端上往往會忽略這個細節。當XSS在瀏覽器以外的客戶端被觸發時,攻擊者可以利用XSS構造JavaScript去調用系統命令,這樣一個簡單的XSS漏洞可能就會升級為一個RCE漏洞。
舉個例子,Electron是GitHub發布的跨平臺桌面應用開發工具,支持Web技術開發桌面應用,其本身是基于C++開發的,圖形用戶界面核心來自Chrome。Electron相當于精簡版的Chromium瀏覽器。Xmind、Slack、Atom、Visual Studio Code、Wordpress Desktop、GitHub Desktop、蟻劍和Mattermost等應用程序都是采用Electron框架構建的。
簡單來說,Electron可以將一個Web應用轉為桌面應用,在Web應用中可能會出現的XSS漏洞,在Electron開發的桌面應用中也會出現,并且這種XSS漏洞很容易升級成為一個RCE漏洞。Xmind、GitHub Desktop等都曾被爆出過XSS漏洞導致的任意命令執行。
我們先來看看JavaScript如何調用系統命令。
exec()是child_process模塊里面最簡單的函數,作用是執行一個固定的系統命令。

例如執行命令whoami。

構造執行命令的XSS Payload調用計算器,代碼如下。

execSync()的作用同exec(),不同之處在于該函數在子進程完全關閉之前不會返回,代碼如下。當遇到超時并發送killSignal時,該函數在進程完全退出之前不會返回。

execFile()函數的作用是運行可執行文件,函數參數信息如下。

execFile()函數用法如下。

execFileSync()的作用與execFile()相同,不同之處在于該函數在子進程完全關閉之前不會返回。當遇到超時并發送killSignal時,該方法在進程完全退出之前不會返回,代碼如下。
