【ファイル共有アクセス権の動的検証】WMI (Win32_LogicalShareSecuritySetting) によるフォルダ共有パーミッション調査
レガシーシステムの深部、あるいは堅牢なオンプレミスインフラの現場において、VBScript(Visual Basic Scripting Edition)とWSH(Windows Script Host)はいまだにインフラストラクチャの自動化と監査を担う「静かなる特攻隊長」である。
PowerShell全盛の現在であっても、追加のランタイムを一切許容しない閉域網のサーバ群や、ドメインコントローラー直下の過酷な環境において、ネイティブで動作するVBScriptの価値が揺らぐことはない。
今回は、組織のセキュリティポリシーにおける最大の脅威の一つである「不適切なファイル共有パーミッション」を標的とする。
WMI(Windows Management Instrumentation)の `Win32_LogicalShareSecuritySecuritySetting` および関連するセキュリティ記述子(DACL)をVBScriptから直叩きし、ネットワーク共有フォルダのアクセス権を極限まで効率的に抽出・監査するスクリプトの実装知見を公開する。
—
1. アーキテクチャの核心:なぜ WMI のセキュリティ記述子(DACL)なのか?
ファイルサーバーの監査において、NTFSパーミッションの調査には `FileSystemObject` や `icacls` コマンドが使われがちだ。しかし、これらは「ローカルパス」を前提としており、真にネットワーク経由で「誰がどの共有(Share)に対してアクセスできるか」という共有レベルのアクセス権(Share Permissions)を見落とす原因になる。
ネットワーク共有のパーミッションは、NTFS権限とAND条件で評価される。真のセキュリティ担保には、共有名(Share Name)に紐づくDACL(Discretionary Access Control List)を直接解析しなければならない。
ここで登場するのが、WMIの以下のクラス群だ:
1. `Win32_Share`: 共有名と物理パスの紐付け。
2. `Win32_LogicalShareSecuritySetting`: 特定の共有に対するセキュリティ設定へのブリッジ。
3. `GetSecurityDescriptor` メソッド: セキュリティ記述子(Win32_SecurityDescriptor インスタンス)の取得。
しかし、このWMIアプローチには、VBScriptエンジニアが恐れる「深い罠」が潜んでいる。オブジェクトのライフサイクル管理を誤れば、メモリリーク(COMコンポーネントの解放漏れ)を引き起こし、大規模ファイルサーバーの監査時に無限のメモリ食い潰しが発生する。
—
2. 実装:WMI セキュリティ解析エンジンの全貌
以下のコードは、指定されたサーバー上のすべての共有フォルダを列挙し、それぞれのアクセス権(Trustee名とMask値)を完全にパースしてコンソール(またはログ)に出力するプロダクションクオリティのVBScriptである。
‘ ==============================================================================
‘ 共有フォルダ アクセス権動的監査スクリプト
‘ Architecture: WMI (Win32_LogicalShareSecuritySetting) + DACL Parsing
‘ Target OS: Windows Server 2012 R2 / 2016 / 2019 / 2022 (Legacy Compatible)
‘ ==============================================================================
Option Explicit
Const wbspFullControl = 2032127
Const wbspChange = 1245631
Const wbspRead = 1179785
AuditingSharePermissions “.”
Sub AuditingSharePermissions(strComputer)
Dim objWMIService, colShares, objShare
Dim colSecuritySettings, objSecuritySetting
Dim intRetVal, objSD, arrDACL, objACE
Dim objTrustee, strAccount, strDomain, strAccessType
WScript.Echo “=== ネットワーク共有パーミッション監査開始: [” & strComputer & “] ===”
‘ 1. WMIサービスへの接続 (接続プールの最適化とエラーハンドリング)
On Error Resume Next
Set objWMIService = GetObject(“winmgmts:{impersonationLevel=impersonate,authenticationLevel=pktPrivacy}!\\” & strComputer & “\root\cimv2”)
If Err.Number <> 0 Then
WScript.Echo “[FATAL ERROR] WMI接続失敗: ” & Err.Description
Exit Sub
End If
On Error GoTo 0
‘ 2. 有効な共有の取得(IPC$やプリンタ共有などの特殊共有を除外)
Set colShares = objWMIService.ExecQuery(“SELECT FROM Win32_Share WHERE Type = 0”)
For Each objShare in colShares
WScript.Echo vbCrLf & “—————————————-”
WScript.Echo “共有名 (ShareName): ” & objShare.Name
WScript.Echo “物理パス (Path) : ” & objShare.Path
WScript.Echo “説明 (Description): ” & objShare.Description
‘ 3. 該当共有のセキュリティ設定インスタンスを取得
Set colSecuritySettings = objWMIService.ExecQuery( _
“ASSOCIATORS OF {Win32_Share.Name='” & objShare.Name & “‘} ” & _
“WHERE ResultClass = Win32_LogicalShareSecuritySetting”)
For Each objSecuritySetting in colSecuritySettings
‘ 4. GetSecurityDescriptorメソッドの呼び出し
intRetVal = objSecuritySetting.GetSecurityDescriptor(objSD)
If intRetVal = 0 And Not IsNull(objSD) Then
arrDACL = objSD.DACL
If IsArray(arrDACL) Then
WScript.Echo ” [ACL 監査結果]”
For Each objACE in arrDACL
Set objTrustee = objACE.Trustee
strAccount = objTrustee.Name
strDomain = objTrustee.Domain
‘ ACE型 (Allowed = 0, Denied = 1 等)
strAccessType = IIf(objACE.AceType = 0, “許可 (Allow)”, “拒否 (Deny)”)
WScript.Echo ” – アカウント: ” & strDomain & “\” & strAccount & _
” [” & strAccessType & “]” & _
” 権限マスク: &H” & Hex(objACE.AccessMask)
Next
Else
WScript.Echo ” [WARNING] DACLが空か、取得できません。”
End If
Else
WScript.Echo ” [ERROR] セキュリティ記述子の取得に失敗しました。ReturnCode = ” & intRetVal
End If
‘ ループ内でのCOMオブジェクトの明示的破棄(メモリ最適化の極意)
Set objSecuritySetting = Nothing
Set objSD = Nothing
Next
Set colSecuritySettings = Nothing
Set objShare = Nothing
Next
‘ 5. ルートオブジェクトの解放
Set colShares = Nothing
Set objWMIService = Nothing
WScript.Echo vbCrLf & “=== 監査完了 ===”
End Sub
‘ 簡易三項演算子エミュレーション
Function IIf(expr, truePart, falsePart)
If expr Then
IIf = truePart
Else
IIf = falsePart
End If
End Function
—
3. チーフアーキテクトが指摘する「メモリ最適化とライフサイクルの罠」
VBScriptによるWMIクエリの乱用は、多くのシニアプログラマが陥る「COMメモリリーク」の温床となる。
上記のコードで、なぜ `For Each` ループの内部で細かく `Set … = Nothing` を実行しているのか。その理由をアーキテクチャの観点から解説する。
WMIプロバイダホスト (WmiPrvSE.exe) との通信コスト
WMI経由で `GetSecurityDescriptor` を叩くたびに、裏ではCOMインターフェイスを介したポインタのやり取りと、OS側でのセキュリティ記述子(SECURITY_DESCRIPTOR)の構造体変換が発生する。
VBScriptのランタイムは、スクリプトスコープが終了するまで参照されたCOMオブジェクトのメモリ解放を「遅延」させる傾向がある。数十個、数百個の共有フォルダを持つファイルサーバーに対してこのスクリプトを走らせた場合、`Set objSecuritySetting = Nothing` を怠ると、クライアント側(あるいはリモートサーバー側)の `WmiPrvSE.exe` が肥大化し、メモリを食いつぶすクラッシュ(あるいはパフォーマンス急低下)を引き起こす。
「使ったら即座に捨てる(`Nothing`を代入する)」。これが、レガシー環境で数万回のイテレーションを安定稼働させるための絶対の鉄則である。
—
4. セキュリティ監査上の重要知見:アクセス権マスクの解読
スクリプトが出力する `AccessMask`(例: `&H12019F` や `&H1F01FF`)は、Windowsのアクセス制御における基本権利(Access Mask)のビットフラグだ。
実務上の監査では、以下の特権的マスクにフラグが立っていないかを動的に検知するロジックを追加することが望ましい。
- `FILE_GENERIC_ALL` (&H1F01FF): フルコントロール(変更、削除、権限変更すべて含む)
- `FILE_GENERIC_READ` (&H120089): 読み取りおよび実行
- `FILE_GENERIC_WRITE` (&H120116): 書き込み、追加
特に、共有パーミッション側で `Everyone` や `Authenticated Users` グループに対して `FILE_GENERIC_ALL` が付与されている環境は、NTFS側でどれほど厳格にアクセス制御を施していても「共有の窓口で完全崩壊している」状態とみなすべきである。
本スクリプトの `AccessMask` 出力をSIEM基板やCSVログへパイプライン出力することで、組織全体の共有フォルダの脆弱性を一網打尽に可視化することが可能となる。
—
5. 結びにかえて
VBScriptは「過去の遺物」ではない。それは、OSの深いレイヤー(WMI、COM、Win32 API)へとダイレクトに、かつ依存関係なしにアクセスできる「最も純度の高い軽量スクリプト言語」である。
フレームワークの流行り廃りに惑わされることなく、背後で動くAPIのライフサイクルとメモリモデルを完璧に理解したコードこそが、ミッションクリティカルな現場を長年にわたり支え続ける。この知見をあなたの武器とし、レガシーとモダンが混在するインフラストラクチャの砦を堅守せよ。
