通殺絕⼤多數交易平臺的Tradingview Dom XSS漏洞分析
*文章作者:Arrowzzzzzz,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載
本文主要是分析慢霧安全團隊 ofollow,noindex">《⼀個通殺絕⼤多數交易平臺的 XSS 0day 漏洞》 ,根據慢霧區匿名情報,通用 K 線展示 JS 庫 TradingView 存在 XSS 0day 漏洞,可繞過 Cloudflare 等防禦機制。該漏洞被利用會導致使用者帳號許可權被盜、惡意操作等造成資產損失。
下面進入正題。
Tradingview 圖表庫,下載下來是一個charting_library資料夾,資料夾裡面有:
其中charting_library.min.js 包含外部圖表庫widget介面。此檔案不應該被修改。static資料夾中儲存圖表庫內部資源,不適用於其他目的。
問題是出現在tv-chart.630b704a2b9d0eaf1593.html(tv-chart*.html),當我下載好TradingView外掛後會自動生成tv-chart*.html這個檔案,中間的*是隨機值
我們檢視該html :

因為他是dom型xss,那麼就檢視是否有script標籤(一般獲取dom值使用javascript獲取的)。
檢視後你會發現沒有獲取disabledFeatures ;enabledFeatures ;indicatorsFile這3個引數的地方,那麼第二種可能就是html裡遠端載入的js了(這些遠端載入js就像我們寫python種匯入的庫一樣,當我們需要某些功能的結果時,就匯入對應的庫,執行裡面的函式,獲取結果值,在將值放到我們需要操作的地方)
在這裡我們發現他遠端載入了3個js:
我們去一一檢視對應的js檔案。
spin.min.js:
vendors.fd8604c09abed9f6643a.js:

我們對上面2個js未能查詢到存在xss的引數。
library.19c99ed5d0307c67f071.js:
我們分析下存在漏洞的引數indicatorsFile。
D ? $.getScript(urlParams.indicatorsFile).done(function()
這個是當時程式碼,我們看看?號,在js中問號是運算子,語法如下:
test ? expression1 : expression2
引數:
test-任何 Boolean 表示式。
expression1-如果 test 為 true,則返回表示式,可能是逗號表示式。
expression2-如果 test 為 false,則返回表示式,可以使用逗號表示式連結多個表示式。
所以D是test,要是他是真就執行expression1($.getScript(urlParams.indicatorsFile).done(function())如果是假就執行:(冒號)後面的程式碼。
我們再看。
$.getScript(),在js中代表通過 HTTP GET 請求載入並執行 JavaScript 檔案。
$查詢了下資料,這裡面可能代表我們匯入的jquery,就像python中匯入time庫,要用到sleep函式的時候需要time.sleep()這樣寫。

urlParams.indicatorsFile:
後面的indicatorsFile我們知道是dom的引數,那麼urlParams是什麼了,我們檢視之前的html檔案。
location.href;(設定或返回完整的 URL)

p.indexOf("#");
p是前面location.href;的返回值;indexOf()可返回某個指定的字串值在字串中首次出現的位置。
語法:
stringObject.indexOf(searchvalue,fromindex)
引數:
searchvalue-必需。規定需檢索的字串值。
fromindex-可選的整數引數。規定在字串中開始檢索的位置。它的合法取值是 0 到 stringObject.length – 1。如省略該引數,則將從字串的首字元開始檢索。
註釋:
如果要檢索的字串值沒有出現,則該方法返回 -1。
後面if判斷是否有#,如果有函式k的返回值是p.substring(o + 1) 。

substring()用於提取字串中介於兩個指定下標之間的字元。
語法:
stringObject.substring(start,stop)
引數:
start-必需。一個非負的整數,規定要提取的子串的第一個字元在 stringObject 中的位置。
stop -可選。一個非負的整數,比要提取的子串的最後一個字元在 stringObject 中的位置多 1。如果省略該引數,那麼返回的子串會一直到字串的結尾。
後面就是正則匹配出我們#後的引數和值了 :

/([^&=]+)=?([^&]*)/g中g的意思是執行全域性匹配(查詢所有匹配而非在找到第一個匹配後停止)。



已經把傳參和值都匹配出來了。
j是個列表,他的值是[disabledFeatures=[321],disabledFeatures,[321]],所以我取後面的2個值。
n[e(j[1])] = e(j[2])這個就是在n這個object中新增屬性和值。
建立物件:
var obj = {}; //或者 var obj=new Object();
新增屬性和值:
var key = "name"; var value = "張三丰" obj[key] = value;
e中replace是返回一個由替換值替換一些或所有匹配的模式後的新字串。模式可以是一個字串或者一個正則表示式,替換值可以是一個字串或者一個每次匹配都要呼叫的函式。
語法如下:
str.replace(regexp|substr, newSubStr|function) regexp (pattern)
一個RegExp 物件或者其字面量。該正則所匹配的內容會被第二個引數的返回值替換掉。
substr (pattern)
一個要被 newSubStr 替換的字串。其被視為一整個字串,而不是一個正則表示式。僅僅是第一個匹配會被替換。
newSubStr (replacement)
用於替換掉第一個引數在原字串中的匹配部分的字串。該字串中可以內插一些特殊的變數名。參考下面的使用字串作為引數。
function (replacement)
一個用來建立新子字串的函式,該函式的返回值將替換掉第一個引數匹配到的結果。參考下面的指定一個函式作為引數。
將我disabledFeatures引數值1+2+3裡+號替換成空格。

匹配</等開始到>結束,將匹配到的值替換成空。
urlParams.indicatorsFile就是indicatorsFile引數的值:
所以,library.19c99ed5d0307c67f071.js.中$.getScript(urlParams.indicatorsFile)就是獲取indicatorsFile的值,並且用getScript載入該值 。
*文章作者:Arrowzzzzzz,本文屬於FreeBuf原創獎勵計劃,未經許可禁止轉載