任务4:检查升级包
好的,我们承认:从来就没有谁发布过完美的操作系统。(嗯,如果任何来自 Microsoft 的人这么问,我们可是不会这么说的。明白吗?)在操作系统发布后,总是会找到各种缺陷。不幸的是,我们同时也会找到各种安全漏洞和易遭受攻击的薄弱处。没有人喜欢发现缺陷这个事实,但是生活就是这个样子。没有人愿意切到自己的手指,但是万一你真的切了自己的手指,你不会坐以待毙,你肯定会翻出急救包然后给伤口上绷带。同样,如果一个操作系统需要类似的措施时,你应该翻出急救箱给系统装好升级包。
换句话说,你最好保证你的计算机安装了最新的系统补丁。Security Analyzer 就能够向你提供有关这方面的信息,而通过下面的脚本也同样能够做到:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\" & strComputer &
"\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery _
("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colOperatingSystems
Wscript.Echo objOperatingSystem.ServicePackMajorVersion _
& "." & objOperatingSystem.ServicePackMinorVersion
Next
在你开始提问之前,首先申明一点:WMI不会显示你所安装的所有升级包的信息,它只会告诉你安装的最新的升级包情况。但是这个应该不成问题,因为最新的升级包都包含了以前版本中的修补程序和升级程序。如果你在 WINDOWS 2000 中安装了 Service Pack 4,那么你安装的不仅仅是 Service Pack 4 中所增加的内容,它也包含了 Service Pack 1、2 和 3 中的所有内容。或者说,只要你知道自己的计算机已经安装了 Service Pack 4,你就没有必要担心你的计算机是否已经安装了Service Pack 1、2 或 3。事实上,它们肯定已经全部安装了。

任务五:检查 Hot Fix
你知道有关最近几次病毒以及蠕虫攻击的真正悲哀的地方在哪里吗?其实很多补丁已经提供很久了,如果用户能够使用的话,就能够避免计算机遭受攻击。但是出于多种原因,人们就是没有使用这些补丁,直到病毒找上门来,后果我不说大家也知道了。我说这些是什么意思?确保自己有最新的补丁和热修补程序(有关如何操作的更多信息请点击此处),并且确保自己的计算机安装了必需的修补程序。
但是你怎么知道机器上装了哪些热修补程序呢?你问的真是好笑:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\" & strComputer &
"\root\cimv2")
Set colQuickFixes = objWMIService.ExecQuery _
("Select * from Win32_QuickFixEngineering")
For Each objQuickFix in colQuickFixes
Wscript.Echo "Description: " & objQuickFix.Description
Wscript.Echo "Hot Fix ID: " & objQuickFix.HotFixID
Next

注意: 如果你使用的是 WINDOWS 2000 操作系统,有些计算机在进入Win32_QuickEngineering 类的时候可能会发生死机现象。有关此已知问题的更多信息以及如何修复,请点击这里。
这段脚本可以很好的工作,但是老实说,和 Security Analyzer 比起来还是要逊色一点。因为 Security Analyzer的确有些不俗的表现:它不仅仅告诉你哪些热修补已经安装了,还显示哪些没有安装。换句话说,如果没有安装某些必须的补丁的话,它会提示你。
那么 Security Analyzer 如何分辨哪些必须安装哪些不必呢?当你运行 Security Analyzer 时,它会参考一个文件(Mssecure.xml),该文件包含一个重要补丁列表以及哪些计算机(不同版本的 WINDOWS)需要哪些补丁的信息。
编写 Mssecure.xml 文件超出了本专栏的范围(虽然对于掌握 XML 知识的人来说这并非不可能)。但是,在以后的专栏中,我们将展示一些你也可以执行针对特定热修补 和补丁的类似方法。

任务六:检查本地管理员数目
人多手脚乱,太多的管理员同样也会坏了好事。更加重要的是,有更多的管理员意味着某人(管理员)犯某种错误从而将计算机暴露与他人攻击之下的可能性也大大增加。你也许会给你的邻居一把备用钥匙,好让他在你不在的时候更好的照看你的东西。但是你不会将你的备用钥匙分发给公共汽车上遇到的每个人。(我们暂且假设你不会这么做)同理,所以你会限制本地管理员的数量,因为从定义上来说,他们可以做许多超出你期望范围以外的事。
同 Security Analyzer 一样,脚本也能够返回计算机上本地管理员组的所有
成员。事实上,下面这段代码就是完成这项功能的:
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
Set objGroup = GetObject("WinNT://" & strComputer &
"/Administrators,group")
For Each objUser in objGroup.Members
Wscript.Echo objUser.Name
Next
这些材料是非常有用的;无任什么时候,只要你有时间(假设你能够抽出时间),你可以仔细研究这份名单,必要的话,删除那些完全没有必要成为管理员的人。
但是如果你所在机构已经制定出与管理员资格有关的政策,应该如何实施这项功
能?例如,假设已经规定普通用户不能成为管理员,而合格的管理员只能是本地管理员用户帐号和域管理员帐号(我们在此例中假设你工作于 fabrikam.com)。这种情况下,你所要求脚本做的不是返回多少个外部帐号,而是希望它能够删除那些外部帐号,只剩下 Administrator和fabrikam\Administrator 帐号。这才算够酷,不是吗?
好吧,如果这真的是你所想的,为什么不承认呢?这段脚本列举出管理员组的所有成员。如果它发现某个既不是 Administrator 也不是 fabrikam\Administrator 帐号的话,就会使用删除方法从该组中删除此帐号。
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
Set objGroup = GetObject("WinNT://" & strComputer &
"/Administrators,group")
For Each objUser in objGroup.Members
If objUser.Name <> "Administrator" and objUser.Name <> _
"fabrikam\Administrator" Then
objGroup.Remove(objUser.ADsPath)
End If
Next
对了:这就是如何使用脚本来增强 Security Analyzer 功能的典型例子。

任务7:检查未过期密码
如果说有什么东西用户会不喜欢的话,那么定时过期的密码肯定名列其中。毕竟,你还没有记住它时,它就已经需要更换了。那么,既然用户们这么憎恨定时过期密码的话,那么干嘛要用他们呢?(我们并不是指为了那些在想密码的时候倒在键盘和鼠标上的无数杯咖啡)。
但是,事实是黑客们和用户们一样憎恨定时过期密码。设想一名黑客想要进行字典穷举攻击。他或者是她也许已经排除了aardvark是密码的可能,而这时候用户恰恰在攻击进行一半的时候将 aardvark更改为系统密码。即使黑客真的猜中了密码,那么这种胜利也是暂时的。也许正当黑客猜中密码的时候,用户马上就将其更改为其它的了。经常更改系统密码是有效提高安全性的途径之一,而经常更改密码最好的办法就是让密码经常过期。
因为用不过期的密码会造成某种安全弱点,所以如果 Security Analyzer 发现任何帐户使用不过期密码的时候,它都会报告这一情况。也许你已经猜到了,我们恰巧也有一段相应的脚本来完成这个相同功能:
Const ADS_UF_DONT_EXPIRE_PASSWD = &H10000
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
Set colAccounts = GetObject("WinNT://" & strComputer & "")
colAccounts.Filter = Array("user")
For Each objUser In colAccounts
Set usr = GetObject("WinNT://" & strComputer & "/" & _
objUser.Name & ", user")
flag = usr.Get("UserFlags")
If flag AND ADS_UF_DONT_EXPIRE_PASSWD Then
Wscript.Echo usr.Name & ": Password does not expire."
Else
Wscript.Echo usr.Name & ": Password expires."
End If
Next
而且你还以通过以下脚本将密码全部设置为过期密码:
Const ADS_UF_DONT_EXPIRE_PASSWD = &H10000
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
Set colAccounts = GetObject("WinNT://" & strComputer & "")
colAccounts.Filter = Array("user")
For Each objUser In colAccounts
Set usr = GetObject("WinNT://" & strComputer & "/" & _
objUser.Name & ", user")
flag = usr.Get("UserFlags")
If flag AND ADS_UF_DONT_EXPIRE_PASSWD Then
usr.UserFlags = flag XOR ADS_UF_DONT_EXPIRE_PASSWD
usr.SetInfo
End If
Next

任务8:进行本地帐户密码测试
蠕虫和病毒得到大家的广泛关注,这个可以理解,但是某些时候真正造成计算机安全威胁的往往是那些最最简单的东西,比如说微不足道的留言便条。在办公室里,职员们经常在留言便条上记录密码,并且将它们贴在显示器上面。现在,你要禁止这种做法了,或者可以严惩那些乱贴这类留言条的家伙这种做法会导致另外一个问题(我们不是指使用走私的便条)。为什么人们会将他们的密码记录下来?因为这些密码太难记了。如果用户不可以记录这些密码,那么他们会倾向于使用他们无需记录就可以记下来的更加简单的密码——例如password。
即使是一个业余黑客或者发泄不满的人使用密码password也可以侵入计算机。虽然 Security Analyzer 不能检测在显示器上是否有记录密码的便条,但是它却可以检查是否有本地用户帐号:
&#8226; 使用空密码
&#8226; 使用与用户名相同的密码
&#8226; 使用与计算机名相同的密码
&#8226; 使用password作为密码
&#8226; 使用单词admin或administrator作为密码
Security Analyzer 是如何进行这么酷的测试的?毕竟,它不可能检索用户的
密码。不然的话这也成了一个安全突破口。好了,告诉你答案,事实上我们也不知道。但是我们怀疑它使用 ADSI ChangePassword 方法(或者某种相当的API)来测试并且修改用户密码。为什么是 ChangePassword ?因为使用这种方法时,如果你不知道当前用户的密码,就不可能顺利实现此项功能,你需要一起提供当前密码以及新密码来完成。因为 Security Analyzer 并没有真正修改密码,所以我们怀疑它只是简单的提供相同的密码(例如password)来同时作为当前密码和新密码,或者与之类似的情况:
objUser.ChangePassword "password", "password"
如果当前密码不是password,这个方法就行不通,系统就会报错。如果当前密码是password,这种方法就会成功,不会产生任何错误,然后密码会被改成password。当然,这一过程的净效果就是密码没有改变,但是系统现在知道当前的密码就是password。这听起来似乎有点混乱,但是先看看这段脚本,看看它到底做些什么。思路其实非常简单。
而且,是的,这也是黑客可能进行的测试。我们现在是借用黑客方法来做正事。
以下就是一段用来检测帐户是否使用的密码是否是password的脚本。这段脚本也可以做简单的修改来检测帐户使用的密码是不是空白的,或者使用登录名作为密码,或者其它。
On Error Resume Next
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
strPassword = "password"
Set colAccounts = GetObject("WinNT://" & strComputer & "")
colAccounts.Filter = Array("user")
For Each objUser In colAccounts
objUser.ChangePassword strPassword, strPassword
If Err = 0 or Err = -2147023569 Then
Wscript.Echo objUser.Name & " is using the password " & _
strpassword & "."
End If
Err.Clear
Next
注意: 你可能注意到了,我们的脚本检测是否出现错误(Err=0);如果没有错误,就说明密码已经被改变,这就说明我们知道这个帐号的密码就是password。但是我们同样检测是否存在错误-2147023569。如果当前的密码是password但是这个密码不能被更改,因为自从上次密码更改以后还没有经过足够长的时间,那么就会产生上述这个错误。(根据默认设置,密码只有每14天才能被更改一次)。

任务9:检测文件系统
如果计算机运行的是 Windows NT、Windows 2000、Windows XP 或者 Windows 2003操作系统,那么将硬盘各个分区格式成NTFS文件系统是非常重要的。(或许会有很少的用户会例外,我们这里暂且不管了。)除非你喜欢将你的硬盘暴露给外界,否则你需要将硬盘格式成为 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
Wscript.Echo "Disk drive: "& objDisk.DeviceID & " -- " & objDisk.FileSystem
Next
在前述 WQL 查询中,你可能已经发现我们只是查询 DriveType 等于 3 的逻辑盘。为什么这么做?因为 DriveType 等于 3 代表的是硬盘,通过将返回数据限定在硬盘上,可以省去我们在软盘、CD-ROM 驱动器以及其它我们知道不会(也不可
能)安装 NTFS 文件系统的驱动器上花费的时间。

任务10:检测自动登录
可以将 Windows 设置成每当开机就自动使用注册的用户名和密码登录。这听起来很方便,但是的确不适用于那些始终与 Internet 保持连接或者在信息亭中使用的计算机。但是,从另一方面来说,这却有点安全漏洞的嫌疑,至少可以这样说。毕竟它允许任何打开计算机的人以储存在注册表中的用户名和密码登录。正因为如此,Security Analyzer 所执行的一项检查就是查看计算机的自动登录功能是否打开。同样,你也可以使用脚本来完成这项检查:
Const HKEY_LOCAL_MACHINE = &H80000002
strComputer = "."
Set objReg=GetObject("winmgmts:\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows NT\CurrentVersion\WinLogon"
strValueName = "AutoAdminLogon"
objReg.GetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName,
dwValue
If dwValue = 1 Then
Wscript.Echo "Auto logon is enabled."
Else
Wscript.Echo "Auto logon is disabled."
End If
同样,你肯定不希望计算机上的自动登录打开。所以,为什么不用一段脚本来关闭自动登录功能而不是像 Security Analyzer 那样仅仅只是提示自动登录功能的状态呢。可以使用类似以下的脚本:
Const HKEY_LOCAL_MACHINE = &H80000002
strComputer = "."
Set objReg=GetObject("winmgmts:\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows NT\CurrentVersion\WinLogon"
strValueName = "AutoAdminLogon"
dwValue = 0
oReg.SetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName,
dwValue

任务11:检查来宾帐户状态
这个问题取决于你如何看待,来宾帐户在不需要证实身份的情况下向他人提供了一种登录你的计算机的途径(我们假设唯一的例外就是,此 Guest 不是指 Christopher Guest)。正因为如此,在 Windows XP 和 Windows 2003 中来宾帐户在默认条件下是关闭的,而且在 Windows 2000 和 Windows NT 4.0 中同样也应该是关闭的。这是一段报告本地计算机上来宾帐户状态的脚本:
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
Set objUser = GetObject("WinNT://" & strComputer & "/Guest")
If objUser.AccountDisabled Then
Wscript.Echo "The Guest account is disabled."
Else
Wscript.Echo "The Guest account is enabled."
End If
看,酷吧。但是假设你决定所有计算机上的来宾帐户都应该关闭。可以修改这段脚本来让它在检查来宾帐户以外多做一两件其它的事。如果来宾帐户已经关闭,那么它只需要报告状态事实;但是如果来宾帐户开启的话,它进一步关闭此帐户:
Set objNetwork = CreateObject("Wscript.Network")
strComputer = objNetwork.ComputerName
Set objUser = GetObject("WinNT://" & strComputer & "/Guest")
If objUser.AccountDisabled Then
Wscript.Echo "The Guest account is already disabled."
Else
objUser.AccountDisabled = True
objUser.SetInfo
Wscript.Echo "The Guest account has been disabled."
End If

任务12:检查匿名登录
在默认设置下,非授权用户是无法连接到任何 Windows 计算机以获得域用户名和共享名列表的。这是什么意思?好的,黑客以你的身份登录需要两条信息:你的用户名和你的密码。他可以简单地通过将便携式电脑接驳到你所在的网络然后向最近的计算机查询就可以获取你的用户名,这样就获得了攻击帐户所需信息的一半。(谢天谢地,还没有人发明无线的 Internet 便条,虽然我们知道有人正在钻研这一技术。)
那么你如何知道自己的计算机是否正在象分发万圣节糖果那样发送信息呢?你可以用 Security Analyzer 或者你也可以使用下面这段脚本:
Const HKEY_LOCAL_MACHINE = &H80000002
strComputer = "."
Set objReg=GetObject("winmgmts:\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "System\CurrentControlSet\Control\Lsa"
strValueName = "RestrictAnonymous"
objReg.GetDWORDValue HKEY_LOCAL_MACHINE,strKeyPath,strValueName,dwValue
If dwValue = 0 Then
Wscript.Echo "Anonymous access is enabled."
Else
Wscript.Echo "Anonymous access is disabled."
End If
你这样问很有趣;通过脚本有一种方法可以关闭计算机上的匿名登录:
Const HKEY_LOCAL_MACHINE = &H80000002
strComputer = "."
Set objReg=GetObject("winmgmts:\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "System\CurrentControlSet\Control\Lsa"
strValueName = "RestrictAnonymous"
dwValue = 1
oReg.SetDWORDValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName,
dwValue

任务13:列出安装的服务
知道计算机上正在运行什么服务很有用。毕竟,有些阴险的病毒会将自己伪装成服务的形式,所以你必须知道是否有这种病毒正在你的计算机上运行。但是查看合法服务是否在一台计算机上运行也很有用处。对了,我们知道你告诉过你的用户不要在他们的机子上运行网站服务器或者 FTP 服务器,但是你如何知道他们是否听话呢?以下这段脚本会告诉你:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\" & strComputer &
"\root\cimv2")
Set colRunningServices = objWMIService.ExecQuery _
("Select * from Win32_Service")
For Each objService in colRunningServices
Wscript.Echo objService.DisplayName, objService.State
Next
那么假如你发现有人运行了你不希望运行的服务时怎么办呢?我们可以从停止通用即插即用设备主机服务的脚本上得到启发(停止它们):
strComputer = "."
Set objWMIService = GetObject("winmgmts:\" & strComputer &
"\root\cimv2")
Set colRunningServices = objWMIService.ExecQuery _
("Select * from Win32_Service Where Name = 'upnhost'")
For Each objService in colRunningServices
objService.StopService()
Next

任务14:检查共享文件以及共享权限
难道你不知道吗:在你整个生命过程中,你都被告知和他人进行分享是多么的重
要,但是现在我们发现共享(至少从计算机角度来说)事实上并不一定是件好事。
共享文件的功能绝对是利弊兼备的。文件共享能够让用户们在不用管理员帮忙以及不占用文件服务器储存空间的情况下就某个工作开展协作。但是,不幸的是,它同样允许人们将一些不应该共享的文件共享(例如侵权的 MP3 文件)。另外,网络共享提供了进入计算机的便捷通道。如果你共享了一个文件夹而没有正确地设置共享权限,那么......。
那么脚本是否能提示你计算机上是否有共享文件呢?你猜猜看:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\" &
strComputer &"\root\cimv2")
Set colShares = objWMIService.ExecQuery("Select * from Win32_Share")
For each objShare in colShares
Wscript.Echo "Name: " & objShare.Name
Wscript.Echo "Path: " & objShare.Path
Wscript.Echo "Type: " & objShare.Type
Next
当然,许多机构都会选择彻底不共享文件,至少在客户端机器上是这样的。如果你也做了同样的决定,那么你就会发现脚本在这个问题上会很有用。Security Analyzer 会告诉你是否在计算机上找到了共享文件;但是,最后还是需要你连接到各台计算机上取消各个共享文件。而脚本不仅仅会识别出共享的文件夹,它还能够同时取消这些文件夹的共享。例如,这里有一段能够取消所有类型为 0 的文件共享(我们稍后就会解释这一点):
strComputer = "."
Set objWMIService = GetObject("winmgmts:\" & strComputer &
"\root\cimv2")
Set colShares = objWMIService.ExecQuery _
("Select * from Win32_Share Where Type = 0")
For each objShare in colShares
objShare.Delete
Next
注意: 别慌,虽然这个方法叫做删除,但是它只是删除共享功能(也就是说,它只是停止在网络上共享这个文件夹而已)。虽然我们认为完全删除它们也是禁止在网络上共享它们的一条途径,
但是它不会正真删除文件夹或者文件夹中的任何内容。
那么为什么是类型为 0 的文件呢?在 WMI 中,一个类型为 0 的共享文件代表的是一个普通的旧的共享文件。当然还有其它的共享文件,最常见的就是管理级共享文件(如C$)。前述脚本能够在分别保留其它共享文件(如管理级共享文件)的同时,停止共享普通文件夹。如果你要停止所有的共享文件,你只要不将 Where 语句包含在查询语句里即可:
Set colShares = objWMIService.ExecQuery("Select * from Win32_Share")
注意: 那么共享权限呢?如上文说到的,我们将会在以后的专栏中讨论。 (未完待序)