コードからカバレッジへ(パート2):ホワイトスペースの悪夢
Detection stack
- AIDR
- Alert
- ETL
- Query
概要
この記事では、Impacket のツールによって生成された LDAP フィルターが、Active Directory によってどのように正規化され、不一致な空白スペースを導入するかを説明しています。 イベント ID 1644 ログにおけるこれらのフォーマット変更は、基礎的なフィルターロジックが同一であっても、正確な文字列一致に依存する検出を壊す可能性があります。著者は基本的な「含む」ロジックが脆弱である理由を示し、スペースの違いや大文字小文字の変更、演算子の変化を許容する正規表現を使用して、強靭な Sigma 検出を構築する過程を紹介します。
調査
問題を検証するために、著者は実際の Event 1644 の記録をレビューし、生産環境から同じビット演算の LDAP フィルターの複数の空白パターンを文書化しました(例:userAccountControl&524288 の異なるスペースやカッコの配置、フォーマットのバリエーション)。いくつかの検出方法が試行され、静的な文字列チェックから始まり、ますます柔軟な正規表現パターンへと進化し、ルールがすべての観察された表現に確実に一致するまで試行錯誤されました。
緩和策
オプションの空白を許容し、大文字小文字を区別せずに一致をサポートし、両方の AND/OR 演算子スタイルに対応する正規表現ベースの検出を採用します。パフォーマンスを合理的に保つために、より高価な正規表現ロジックを適用する前にターゲット属性名で事前フィルターを行います。最後に、フィールドで見られるあらゆる空白とフォーマットのバリエーションを捉えた curated “wall of shame” コーパスに対して検出の妥当性を継続的に検証します。
対応
疑わしい LDAP フィルターマッチがトリガーされた際には、SOC に通知して、特権の探索や昇格に関連する列挙活動の可能性を評価します。イベントをソースホスト/IP、要求ユーザー、その他の LDAP 属性と関連付けて、意図と範囲を決定します。誤検知が発生した場合は、既知のフォーマットバリエーションへのカバレッジを維持しながら、しきい値やルール条件を調整します。
攻撃の流れ
この部分はまだ更新中です。通知を受けるためにサインアップしてください。
通知するシミュレーションの実行
前提条件: テレメトリー & ベースラインの事前フライトチェックに合格している必要があります。
根拠: このセクションでは、検出ルールを引き起こすように設計された敵の手法 (TTP) の正確な実行を詳述しています。コマンドと説明は、特定された TTP を直接反映し、検出ロジックによって期待される正確なテレメトリーを生成することを目的としています。
-
攻撃の説明とコマンド:
低特権ドメイン資格情報を持つ敵が、横移動するための特権アカウントを見つけようとしています。彼らは LDAP フィルターを作成し、bitwise AND を userAccountControl に対して実行してアカウントを分離します。フラグ(値 524288)があります。単純な文字列一致を回避するために、Sigma ルールが期待するように余分な空白とカッコを追加します。フラグ(値 524288)があります。単純な文字列一致を回避するために、Sigma ルールが期待するように余分な空白とカッコを追加します。flag (value 524288). To evade simple string matching, they add extra whitespace and parentheses exactly as the Sigma rule expects.- 空白バリエーションを持つ LDAP フィルター文字列を構築します。
- LDAP 検索を実行して
ldapsearch(PowerShell 経由)でドメインコントローラーに対して行います。 - クラフトされたフィルターを含む Event 1644 の記録がセキュリティイベントログにあることを確認します。
-
回帰テストスクリプト:
# -------------------------------------------------------------- # シグマルールをトリガーすべき LDAP ビット操作フィルターをシミュレート # -------------------------------------------------------------- # パラメータ $DomainController = "dc01.example.com" $BaseDN = "DC=example,DC=com" $Filter = "( userAccountControl & 524288 )" $Attributes = "distinguishedName,samAccountName,userAccountControl" # LDAP クエリを実行 try { $result = [ADSI]"LDAP://$DomainController/$BaseDN" $searcher = New-Object System.DirectoryServices.DirectorySearcher($result) $searcher.Filter = $Filter $searcher.PropertiesToLoad.AddRange($Attributes.Split(',')) $searcher.PageSize = 1000 $entries = $searcher.FindAll() foreach ($entry in $entries) { $dn = $entry.Properties["distinguishedname"][0] $sam = $entry.Properties["samaccountname"][0] $uac = $entry.Properties["useraccountcontrol"][0] Write-Output "Found: $sam ($dn) – UAC=$uac" } } catch { Write-Error "LDAP query failed: $_" } -
クリーンアップコマンド:
# 永続的な変更は行われませんでした; ADSI 接続を閉じるだけです。 Write-Output "クリーンアップ完了。"