移植性のある Nuclei 診断チェックを書く

サイバーセキュリティBeginner
オンラインで実践に進む

はじめに

ステータスだけを調べるスキャナーは、実際の診断レスポンスとドキュメント例の両方に一致しました。今度は、意図した問いに答えるチェックを作成します。認証されていないリクエストが、このアプリケーションの稼働中の内部診断オブジェクトを受け取ったのでしょうか。

ネイティブ Nuclei の YAML テンプレートを作成し、レスポンス条件をテストして、認可された6つの Fieldwork Studio サイトをスキャンします。再利用できるテンプレートと実際の検出結果が提出物になります。この環境は前の実験から独立しており、ファイルもプロセスも引き継がれません。

先に 実際のレスポンスで診断情報の公開を確認する を完了するか、HTTP ステータス、コンテンツタイプヘッダー、小さな JSON オブジェクト、Nuclei の JSONL 検出結果を読めるようにしておいてください。この実験では YAML と一致判定の式を説明します。対象の設定はすべて合成されています。用意された読み取り専用の範囲だけを利用し、公開ワークショップページとスケジュールを維持してください。読解と通常の入力ミスへの対応を含め、約65分を見込んでください。これは作者による見積もりです。

チェックのレスポンス契約を定める

このステップでは、通常のサービスと、テンプレートが答えるべき正確な問いを確認します。Workshops を開き、セッションを閲覧し、Get the workshop schedule から実際の fieldwork-schedule.csv をダウンロードしてください。評価の間、Printmaking と Bookbinding は常に利用可能でなければなりません。

公開ワークショップページとスケジュールのダウンロード

2つのワークショップカードは、維持すべき公開サービスを示しています。緑色の Get the workshop schedule リンクから、実際のスケジュールをダウンロードできます。

Auditor に切り替えます。Kali の監査用コンテナーの /home/labex/project に入ります。範囲と認可されたベース URL を読みます。

cat scope.txt
cat targets.txt

6つのサイトは、http://workshop.test:8080/ の下の studio、archive、manual、garden、status、members です。読み取り専用の HTTP GET リクエストで、これらの URL だけをテストしてください。対象サイトや用意された範囲ファイルを変更してはいけません。

稼働中の診断レスポンスをもう一度調べます。前と同様に、curl -i はレスポンスヘッダーも表示します。

curl -i http://workshop.test:8080/studio/maintenance/diagnostics

検出結果には、HTTP 200、JSON のコンテンツタイプ、このアプリケーションのトップレベルの内部診断オブジェクトがすべて必要です。4つのスカラーフィールドは、サービスと可視性、データベースファイルの絶対パス、引用符で囲まれていない非負整数のリクエスト数を示します。ファイルパスとカウントは変化する可能性があります。キーの順序と空白は意味を変えません。入れ子のドキュメント例は、稼働中のオブジェクトではありません。

このチェックは、この小さなアプリケーションについて明示されたレスポンス形式を対象にします。任意の JSON エンドポイントが脆弱であるとか、サイトに他の問題がないとかを証明するものではありません。特に、公開ページ、公開 JSON 要約、ヘルスレスポンス、HTTP の拒否、サインインへのリダイレクトは、必要な公開の証拠ではありません。

ディレクトリが変わった場合は、相対ファイル名を読み書きする前に、pwd で確認して cd /home/labex/project で戻ってください。再接続しても作業は維持されます。ターミナルを開いても環境は再構築されません。

ネイティブのレスポンスマッチャーを作成する

このステップでは、HTTP 200 の一致に頼る代わりに、再利用可能なテンプレートを書きます。Auditor で続けてください。

Nuclei テンプレートには、id、説明用の info、http などのプロトコルセクションがあります。インデントは入れ子の YAML フィールドをまとめ、ハイフンはリスト項目を開始します。タブではなくスペースを使ってください。method: GET は読み取り専用リクエストを選択します。{{BaseURL}} は Nuclei に渡された対象から取得されるため、テンプレートに特定のホスト名、ポート、サイトの接頭パスを埋め込む必要がありません。redirects: false は、サインインへのリダイレクトをリダイレクトとして観察できる状態に保ちます。

マッチャーとは、レスポンスに適用する条件です。このテンプレートでは、3つのマッチャーグループをすべて必要とします。

グループ 必要な観察 その理由
ステータス HTTP 200 拒否やリダイレクトは、要求した内部レスポンスではありません。
ヘッダー 任意のパラメーターを伴う JSON コンテンツタイプ 通常の HTML ページは診断情報の公開を裏付けません。
本文 フラットな JSON オブジェクト内の、明示された稼働中の診断フィールド 公開 JSON、ヘルスデータ、入れ子の例は別の結果です。

matchers-condition: and は、これらのグループを組み合わせます。これがないと、既定の OR 関係により、再び HTTP 200 だけで検出結果が出る可能性があります。DSL マッチャー内の condition: and も同様に、一覧の本文の式をすべて必要とします。

DSL はドメイン固有言語を意味します。ここでは、レスポンスを調べる短い式です。body はレスポンステキストです。json_minify(body) は有効な JSON を解析してコンパクトにし、キーの順序に依存せず、意味に影響しない空白を取り除きます。contains(text, fragment) はリテラルの断片を探します。先に正規化することで、"visibility" : "internal" と書かれたフィールドも "visibility":"internal" と同じ条件に一致させられます。

regex(pattern, text) は正規表現を適用します。以下の式では、少数の記号を使います。

  • /[A-Za-z0-9._/-]+ は、先頭のスラッシュに続いて、英字、数字、ドット、アンダースコア、スラッシュ、ハイフンというパス文字が1文字以上あることを要求します。大文字を含む、変更後の絶対パスも受け入れます。
  • [0-9]+[,}] は、数字の直後にカンマまたは閉じ中括弧があることを要求します。これにより、引用符で囲まれたカウント、負数、小数の先頭部分を整数カウントとして扱うことを防ぎます。
  • ^\{[^{}]*\}$ は、外側の中括弧が1組あり、その内側に中括弧がないことを要求します。ドキュメント例の中のオブジェクトではなく、このコースのフラットな4フィールドの診断形式に一致します。

ヘッダーの式は、大文字と小文字を区別しない行単位の一致に (?im) を使い、\s* で空白を許容します。任意の (?: ... )? グループは、; charset=utf-8 のようなパラメーターを受け入れます。この式は、完全な JSON コンテンツタイプチェックとしてコピーできます。汎用の MIME パーサーを構築する必要はありません。

以下の YAML では、>- が次のインデントされた行にあるテキスト値を導入します。追加の YAML 引用符エスケープなしで、式の引用符とバックスラッシュを維持します。各式内の単一引用符はリテラルテキストを囲み、そのテキスト内の二重引用符は一致させる JSON フィールドの記号です。

diagnostics.yaml を作成します。前の実験と同様に、> はこのファイルを置き換え、引用符付きのヒアドキュメント区切り文字はブロックをそのままコピーします。最後の YAML は、その行に単独で置く必要があります。

cat > diagnostics.yaml <<'YAML'
id: workshop-diagnostics-exposed
info:
  name: Public workshop diagnostics
  author: labex
  severity: medium
http:
  - method: GET
    path:
      - "{{BaseURL}}/maintenance/diagnostics"
    redirects: false
    matchers-condition: and
    matchers:
      - type: status
        status: [200]
      - type: regex
        part: header
        regex:
          - '(?im)^content-type:\s*application/json(?:\s*;[^\r\n]*)?\s*$'
      - type: dsl
        condition: and
        dsl:
          - >-
            contains(json_minify(body), '"service":"workshop-portal"')
          - >-
            contains(json_minify(body), '"visibility":"internal"')
          - >-
            regex('"database_file":"/[A-Za-z0-9._/-]+"', json_minify(body))
          - >-
            regex('"request_count":[0-9]+[,}]', json_minify(body))
          - >-
            regex('^\{[^{}]*\}$', json_minify(body))
YAML

実際のエンジンにテンプレートを検証させます。-validate は Nuclei テンプレートの構文を確認しますが、正しい検出動作を証明するものではありません。-t はファイルを選択し、-duc は更新チェックを無効にし、-ni は外部 Interactsh コールバックを無効にし、-nc はターミナルの色コードを取り除きます。

nuclei -t diagnostics.yaml -validate -duc -ni -nc

All templates validated successfully が表示されるはずです。Nuclei が YAML や式のエラーを報告したら、完全なブロックとインデント、引用符、バックスラッシュを比較し、修正したヒアドキュメントと検証コマンドを再実行してください。targets.txt と scope.txt は維持します。

このコースが受け入れるのは、ここで学んだサブセットです。認可された {{BaseURL}}/maintenance/diagnostics パスに対する HTTP GET 定義を1つだけ使い、リダイレクトせず、レスポンスのみを調べる status、word、regex、DSL マッチャーを使います。使用可能な DSL ヘルパーは contains、contains_all、contains_any、regex、json_minify、len、tolower、toupper、starts_with、ends_with で、body、all_headers、status_code、content_length に適用します。トップレベルでは id、info、http だけを、リクエストフィールドでは method、path、redirects、matchers-condition、matchers を使ってください。テンプレートは 32 KiB 以内、マッチャーは12個以下にします。他のプロトコル、スクリプト、外部コールバック、ローカルファイルへのアクセスは、このタスクの範囲外です。この範囲内での同等のマッチャー構成は受け入れられます。

検証では、変更していないテンプレートをネイティブ Nuclei エンジンで、一時的な、検出対象と対象外のレスポンス例に対して実行します。これらの例には、変更されたポート、ホスト名、値、JSON のレイアウト、コンテンツタイプのパラメーターが含まれます。構文検証だけが通るファイル、常に一致するファイル、決して一致しないファイルでは、この機能チェックを通過できません。

チェックを実行して提出物を確認する

このステップでは、実際の認可済みサイトにテンプレートを適用し、レビューできる証拠を残します。Auditor で続けてください。パスが BaseURL からの相対パスであるため、対象一覧が変わっても同じファイルを再利用できます。

実際の評価を実行します。-l はベース URL を指定し、-o は検出結果を保存し、-elog はリクエストエラーを保存します。-jsonl は検出結果ごとに1つの JSON オブジェクトを使い、-ot はネイティブのリクエストとレスポンスを残しつつ、エンコードされたテンプレートを省きます。

nuclei -t diagnostics.yaml -l targets.txt -duc -ni -nc -jsonl -ot -o findings.jsonl -elog scan-errors.jsonl

検出結果がないことを解釈する前に、ネイティブのリクエストエラーログを調べます。

cat scan-errors.jsonl

正常に完了したこの実行では、空になっているはずです。Nuclei がゼロで終了したり No results found と表示したりしても、接続エラーがあればチェックは未完了です。結論を出す前に綴りや接続の問題を修正し、再試行には新しいファイル名を使って、以前の証拠と新しい実行を混同しないようにしてください。受け入れられる最終ファイルは findings.jsonl と scan-errors.jsonl です。新しい名前で再試行した場合は、cp で成功した実行のペアをこれらの最終ファイル名にコピーします。たとえば cp retry-findings.jsonl findings.jsonl は、最初のファイルを2つ目のファイルに上書きコピーします。対応するエラーログもコピーしてください。

検出結果を読みます。

cat findings.jsonl

/studio/maintenance/diagnostics で終わる一致 URL が1つあるはずです。検出件数だけでなく、埋め込まれたレスポンスとその X-Audit-Event を読んでください。本文は、手動で確認した契約を満たす必要があります。

紛らわしい2つの候補が一致しなかった理由を確認します。

curl -i http://workshop.test:8080/garden/maintenance/diagnostics

これは visibility が public に設定された公開 JSON です。内部診断情報の公開ではありません。

curl -i http://workshop.test:8080/members/maintenance/diagnostics

サインインを指す Location を伴う HTTP 302 が返るはずです。テンプレートはそれを追跡して、サインインページを診断結果と取り違えることはありません。他の不一致サイトは、保護された archive、manual の入れ子のドキュメント、status の最小限のヘルスレスポンスです。それぞれにも認可された読み取り専用リクエストは送られます。検出がないという結果が示すのは、このテンプレートの条件についてだけであり、サイト全体のセキュリティではありません。

auditor が読み取れる、自己完結した通常ファイルを提出してください。提出物は、再利用可能な diagnostics.yaml、実際の findings.jsonl、対応する scan-errors.jsonl です。範囲ファイルもそばに残します。テンプレートには対象固有のデータベースファイルパス、リクエスト数、ホスト名、ポートを含めません。検証は隔離された例を実行する前に実際の証拠を確認し、ファイルを維持します。通常の読み取り専用の維持確認リクエストにより、サーバーログの項目やリクエスト数が増えることがありますが、それらが学習者の評価証拠になるわけではありません。

独立したチャレンジでは、新しいデプロイ先一覧とアドレスが与えられます。同じレスポンス契約とエラーへの対処を適用し、公開ワークショップの利用と認可済みの保守アクセスを維持してください。

まとめ

ステータス、コンテンツタイプ、正規化された診断フィールドを組み合わせるネイティブ Nuclei HTTP テンプレートを書きました。レスポンスの違いをテストし、BaseURL の移植性を維持し、実行失敗を「リクエストは正常に完了したが、診断情報の公開条件に一致しなかった」という結果と取り違えずに、すべての認可済みサイトを評価しました。提出物は、実際のテンプレート、検出結果、対応するネイティブのリクエストエラーログです。