0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

XSS学習振り返り

0
Posted at

Level 1: Hello, world of XSS

image.png

query = self.request.get('query', '[empty]')
message = "Sorry, no results were found for <b>" + query + "</b>."

ユーザーが送信した query パラメータの値を HTMLエスケープせずに message にそのまま埋め込んでいます。

ページ上に表示されるHTML:

Sorry, no results were found for <b><script>alert('XSS')</script></b>.

なぜブラウザが防がないのか?

self.response.headers.add_header("X-XSS-Protection", "0")

XSSフィルタを明示的に無効化しています(IEや旧Chrome向け)

修正例:最も簡単な防止方法は、query の出力をHTMLエスケープすることです。

import cgi
safe_query = cgi.escape(query)  # Python 2.x
# もしくは Python 3.x では html.escape(query)
message = "Sorry, no results were found for <b>" + safe_query + "</b>."

Level 2: Persistence is key

image.png
ユーザーが入力した投稿メッセージを「投稿」として保存・表示します。
しかし、保存された内容(HTML文字列)を次のように直接HTMLに挿入しています:

html += "<blockquote>" + posts[i].message + "</blockquote";

HTMLの一部として タグはよく使われますが、そこに onerror イベントを仕込むことで JavaScript が実行されます。

ページ上に表示されるHTML:

<blockquote><script>alert('XSS')</script></blockquote>

画像読み込みに失敗したとき onerror が実行され、アラートが表示される。

修正例:textContent を使って安全に描画

const blockquote = document.createElement('blockquote');
blockquote.textContent = posts[i].message;
parentElement.appendChild(blockquote);

JSの textContent プロパティは HTML としてではなく テキストとして表示 するため安全です。

Level 3: That sinking feeling...

image.png

var html = "Image " + parseInt(num) + "<br>";
html += "<img src='/static/level3/cloud" + num + ".jpg' />";
$('#tabContent').html(html);  // ← ✴️ここでXSS成立の可能性

parseInt(num) は整数値に変換するが、cloud" + num + ".jpg は 文字列として連結されており、num に文字列が入ると HTMLが壊れる可能性がある。
ここに ' onerror=alert(1) x=' などを入れると:

<img src='/static/level3/cloud1' onerror=alert(1) x='.jpg' />

これで onerror 属性が挿入され、画像が読み込めない → onerror が発火してXSS成功 となります。

Level 4: Context matters

image.png

<img src="/static/loading.gif" onload="startTimer('{{ timer }}');" />

ここで {{ timer }} にユーザーの入力が埋め込まれていると仮定すると、その入力がクオートされていない状態でスクリプトに注入されることになります。
攻撃者が ');alert(1);// のような値を {{ timer }} に入れると以下のようになる:

onload="startTimer('');alert(1);//');"

☠️ JavaScriptとして正しく構文解析され、alert(1) が実行される。

Level 5: Breaking protocol

image.png

<a href="{{ next }}">Next >></a>

この行がすべてです。ここで {{ next }} は、ユーザー入力(クエリパラメータなど)を直接 <a href="..."> に埋め込んでいます。

出力HTML:"javascript:alert(1)" を入力して「Next >>」をクリックすると、alert(1) が即時実行されます。

<a href="javascript:alert(1)">Next >></a>

Level 6: Follow the

image.png

var hash = location.hash.substr(1); // 例: #http://example.com/evil.js
if (hash.startsWith("http://trusted.com/")) {
  var script = document.createElement("script");
  script.src = hash;
  document.body.appendChild(script);
}

このようなコードは 安全そうに見えて、巧妙にバイパスできます。
Hint4 外部からJSを読み込む手段として Google の JSONP API(callback付き)を利用せよ!から //www.google.com/jsapi?callback=alert をURLに埋め込む

if (url.match(/^https?:\/\//)) {
  // 拒否メッセージを表示して終了
}

この行は、http://https:// で始まるURLをブロックする目的ですが…
"http" を含むURLを弾いてるわけではなく、先頭にある場合だけ拒否している
"http" を含んでいても、先頭以外なら通ってしまう!

👉 JavaScriptでこのように評価されます:

includeGadget("//www.google.com/jsapi?callback=alert");

そしてこれは src = "//www.google.com/jsapi?callback=alert" となり、url.match(/^https?:\/\//) はマッチしない(先頭が // なので)

<script src="https://www.google.com/jsapi?callback=alert"></script>

結果: alert() が実行される!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?