毒をもって毒を制す:スクリプトで構築するWindowsセキュリティ要塞
毒をもって毒を制す:スクリプトで構築するWindowsセキュリティ要塞
「スクリプトが非合法なら、非合法な者だけがスクリプトを持つことになる。」この言葉の裏には、スクリプトの安全性に関する長年の議論があります。新しいウイルスやサイバー攻撃が出現するたびに、スクリプトはしばしば矢面に立たされ、非難の対象となります。しかし、スクリプトを全面的に禁止するのは、問題を解決するために本質的な価値を捨てるようなものです。スクリプトは潜在的な攻撃経路であるだけでなく、システム管理者の手にかかれば、自動化とセキュリティ強化のための強力で柔軟なツールとなります。本稿では、Windowsスクリプト(WMIとADSIを組み合わせる)を利用して、Microsoft Baseline Security Analyzer (MBSA) の中核機能を再現し、さらに強化する方法を深く探求し、受け身の姿勢から攻めの姿勢へと転換し、スクリプトを「セキュリティ上の懸念」から「セキュリティの守護神」へと変貌させます。
知識は力なり:スクリプトとMBSAの相補的な関係
Microsoft Baseline Security Analyzer (MBSA) は、セキュリティパッチの欠如や脆弱なパスワードポリシーなど、コンピュータの潜在的なセキュリティリスクを分析する優れた無料ツールです。しかし、MBSAの主な機能は「報告」にあります。何が問題であるかを教えてくれますが、その問題を自動的に「解決」することはできません。例えば、MBSAはネットワーク上の2,967台のコンピュータでゲストアカウントが有効になっていることを報告できますが、それらを一つ一つ手動で無効にする作業は依然として残ります。
まさにここに、スクリプトの活躍の場があります。スクリプトの強力さは、セキュリティ情報を「取得」する能力だけでなく、その情報に基づいて自動的に「アクションを実行」する能力にあります。スクリプトを作成して、発見されたセキュリティ問題をチェックし、自動的に修正することが可能です。以下では、具体的なセキュリティタスクを通じて、スクリプトを使用してMBSAのチェック機能を実装し、可能であればさらに問題を自動的に修正するスクリプトを作成する方法を示します。
注:例を簡潔にするため、以下のスクリプトはすべてローカルコンピュータを対象としています。わずかな変更で、ネットワーク上の数百、数千のコンピュータを管理するように簡単に拡張できます。
14のコアセキュリティタスクのスクリプト化実装
タスク1:コンピュータ名とIPアドレスの取得
セキュリティ監査を実施する際、最初のステップは対象のマシンを特定することです。以下のWMIスクリプトは、コンピュータ名と有効なIPアドレスを取得します。
' コンピュータ名の取得
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colComputers = objWMIService.ExecQuery("Select * from Win32_ComputerSystem")
For Each objComputer in colComputers
Wscript.Echo "Computer Name: " & objComputer.Name
Next
' IPアドレスの取得
Set IPConfigSet = objWMIService.ExecQuery("Select IPAddress from Win32_NetworkAdapterConfiguration Where IPEnabled=TRUE")
For Each IPConfig in IPConfigSet
If Not IsNull(IPConfig.IPAddress) Then
For Each strAddress in IPConfig.IPAddress
WScript.Echo "IP Address: " & strAddress
Next
End If
Next
タスク2:OSのService Packバージョンの確認
システムに最新のサービスパックがインストールされていることを確認するのは、既知の脆弱性から保護するための基本です。このスクリプトはWMIを介して最新のService Packバージョンを照会します。
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colOS = objWMIService.ExecQuery("Select * from Win32_OperatingSystem")
For Each objOS in colOS
Wscript.Echo "Service Pack: " & objOS.ServicePackMajorVersion & "." & objOS.ServicePackMinorVersion
Next
タスク3:インストール済みのセキュリティパッチ(ホットフィックス)の確認
多くの攻撃が成功する原因は、リリース済みのセキュリティパッチを適時にインストールしなかったことにあります。このスクリプトは、システムにインストールされているすべてのQuick Fix Engineering (QFE) 更新プログラムを一覧表示します。
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colQuickFixes = objWMIService.ExecQuery("Select * from Win32_QuickFixEngineering")
For Each objQuickFix in colQuickFixes
Wscript.Echo "HotFix ID: " & objQuickFix.HotFixID & " - Description: " & objQuickFix.Description
Next
タスク4:ローカル管理者数の確認
管理者が多すぎると、攻撃対象領域が広がります。このスクリプトは、ローカルのAdministratorsグループのすべてのメンバーを一覧表示するだけでなく、ポリシーに準拠していないアカウントを自動的に削除することもできます。
' 管理者の一覧表示
Set objGroup = GetObject("WinNT://./Administrators,group")
For Each objUser in objGroup.Members
Wscript.Echo objUser.Name
Next
' ポリシーに準拠しない管理者の自動削除(例:ローカルのAdministratorとドメイン管理者fabrikam\Administratorのみを許可)
Set objGroup = GetObject("WinNT://./Administrators,group")
For Each objUser in objGroup.Members
If LCase(objUser.Name) <> "administrator" and LCase(objUser.Name) <> "fabrikam\administrator" Then
objGroup.Remove(objUser.ADsPath)
End If
Next
タスク5:パスワードの無期限設定の確認
パスワードの定期的な変更を強制することは、セキュリティを向上させる効果的な手段です。以下のスクリプトは、「パスワードを無期限にする」と設定されているすべてのアカウントを検出し、その設定を自動的に変更することができます。
Const ADS_UF_DONT_EXPIRE_PASSWD = &H10000
' チェックと報告
Set colAccounts = GetObject("WinNT://.")
colAccounts.Filter = Array("user")
For Each objUser In colAccounts
flag = objUser.Get("UserFlags")
If flag AND ADS_UF_DONT_EXPIRE_PASSWD Then
Wscript.Echo objUser.Name & ": パスワードは無期限です。"
End If
Next
' 自動修復:すべてのユーザーパスワードを定期的に期限切れにする
For Each objUser In colAccounts
flag = objUser.Get("UserFlags")
If flag AND ADS_UF_DONT_EXPIRE_PASSWD Then
objUser.UserFlags = flag XOR ADS_UF_DONT_EXPIRE_PASSWD
objUser.SetInfo
End If
Next
タスク6:脆弱なパスワードのテスト
MBSAは、空のパスワードやユーザー名と同じパスワードなど、脆弱なパスワードを検出できます。スクリプトも同様のチェックをシミュレートできます。以下の例では、パスワードが「password」であるかどうかを検出します。
On Error Resume Next
strPassword = "password"
Set colAccounts = GetObject("WinNT://.")
colAccounts.Filter = Array("user")
For Each objUser In colAccounts
objUser.ChangePassword strPassword, strPassword
If Err = 0 or Err = -2147023569 Then
Wscript.Echo objUser.Name & " は脆弱なパスワード '" & strPassword & "' を使用しています。"
End If
Err.Clear
Next
タスク7:ファイルシステムタイプの確認
NTFSファイルシステムを使用することは、アクセス制御を実現するための基本です。このスクリプトは、すべてのローカルハードドライブパーティションのファイルシステムがNTFSであるかどうかを確認します。
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk Where DriveType = 3")
For Each objDisk in colDisks
If UCase(objDisk.FileSystem) <> "NTFS" Then
Wscript.Echo "警告: ドライブ " & objDisk.DeviceID & " はNTFSではなく " & objDisk.FileSystem & " を使用しています。"
End If
Next
タスク8:自動ログオン設定の確認
自動ログオンは便利ですが、深刻なセキュリティリスクをもたらします。このスクリプトは、レジストリを介して自動ログオン機能をチェックし、無効にします。
Const HKEY_LOCAL_MACHINE = &H80000002
strKeyPath = "Software\Microsoft\Windows NT\CurrentVersion\WinLogon"
strValueName = "AutoAdminLogon"
Set objReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
' 状態の確認
objReg.GetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, dwValue
If dwValue = 1 Then
Wscript.Echo "自動ログオンが有効です。"
' 自動ログオンを無効にする
dwValue = 0
objReg.SetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, dwValue
Wscript.Echo "自動ログオンが無効になりました。"
End If
タスク9:ゲストアカウントの状態の確認
ゲストアカウントは常に無効にしておくべきです。このスクリプトはその状態を確認し、有効になっている場合は自動的に無効にします。
Set objUser = GetObject("WinNT://./Guest")
If objUser.AccountDisabled Then
Wscript.Echo "ゲストアカウントは無効です。"
Else
Wscript.Echo "警告: ゲストアカウントは現在有効です。"
objUser.AccountDisabled = True
objUser.SetInfo
Wscript.Echo "ゲストアカウントは自動的に無効になりました。"
End If
タスク10:匿名ログオンの確認 (RestrictAnonymous)
匿名アクセスを制限すると、未承認のユーザーがドメインのユーザー名や共有リストを列挙するのを防ぐことができます。このスクリプトは、匿名アクセスを制限するために正しいレジストリキーをチェックし、設定します。
Const HKEY_LOCAL_MACHINE = &H80000002
strKeyPath = "System\CurrentControlSet\Control\Lsa"
strValueName = "RestrictAnonymous"
Set objReg = GetObject("winmgmts:\\.\root\default:StdRegProv")
' チェックと修復
objReg.GetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, dwValue
If dwValue = 0 Then
Wscript.Echo "匿名アクセスが有効になっており、リスクがあります。"
dwValue = 1
objReg.SetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, dwValue
Wscript.Echo "匿名アクセスが制限されました。"
End If
タスク11:実行中のサービスのリスト表示
システムで実行されているサービスを把握することは、マルウェアや不正なアプリケーション(個人で立てたFTPサーバーなど)を発見するのに役立ちます。このスクリプトはサービスをリスト表示するだけでなく、指定したサービスを停止することもできます。
' すべてのサービスとその状態をリスト表示
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colServices = objWMIService.ExecQuery("Select * from Win32_Service")
For Each objService in colServices
Wscript.Echo objService.DisplayName, objService.State
Next
' 指定したサービスを停止(例:UPnPHost)
Set colServices = objWMIService.ExecQuery("Select * from Win32_Service Where Name = 'upnphost'")
For Each objService in colServices
objService.StopService()
Next
タスク12:ファイル共有とアクセス許可の確認
安全でない共有は、データ漏洩や横方向の攻撃の一般的な侵入口です。このスクリプトは、すべての共有とそのパスを一覧表示できます。
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colShares = objWMIService.ExecQuery("Select * from Win32_Share")
For Each objShare in colShares
Wscript.Echo "Share Name: " & objShare.Name & ", Path: " & objShare.Path
Next
結論
スクリプトはセキュリティの世界の「悪役」どころか、システム管理者が自動化され、スケーラブルなセキュリティ管理を実現するための強力な武器です。MBSAのチェックロジックとスクリプトの実行能力を組み合わせることで、問題を発見するだけでなく、効率的かつ一貫して問題を解決し、企業ネットワーク全体に堅牢でインテリジェントなセキュリティ防衛線を構築することができます。