脚本的故事
摘要:
这是一篇介绍用脚本实现MBSA功能的文章。
======================以下为原文==================================
脚本故事 - 2003年11月
发布者 The Scripting Guys
如果脚本是非法的,那么只有非法者才使用脚本
如需获得脚本专栏所有脚本故事的列表以及其他信息,请点击此处。
Microsoft 的员工生活在虚幻的世界中,这是针对 Microsoft 员工的批评之一。意思是说我们这些 Microsoft 员工只是关注各种理想情况而对于系统管理员真正需要面对的实际问题却从不考虑。这可能适用与某些 Microsoft 员工,但是至于我们对系统管理员所面临的问题一无所知这种说法,Scripting Guys是有意见的。而且也有许多其他同仁同意我们的看法。例如,就在今天早上坐车来上班的路上,我们对司机说:“Bentley,你认为Scripting Guys是生活在虚幻的世界中的吗?是不是在系统管理员有需要的时候总是不能提供帮助?” “我不这样认为,”Bentley说,“绝对不是这样的。”
毕竟我们认为他是这么说的。说实在话,由于坐在后排的 Jacuzzi 的说话声音过大,我们听得并不是很清楚。
不过,问题是,Scripting Guys也只不过是些平常人物罢了,和其他人并没有什么区别。把我们归为那类不食人间烟火的“杰出精英”有点夸张了。我们觉得我们在一个重要的领域可能的确不够尽职:也许——注意,仅仅是也许——当系统管理员遇到安全问题时,我们没有出色地完成自己的工作。
我们承认,让Scripting Guys谈论安全问题可能会让你们听起来有点好笑,因为有好多人认为如果没有脚本程序那么就不会有那么多的安全问题。每当出现黑客攻击或者破坏攻击以及新的病毒或者蠕虫病毒的时候,人们就会自然而然地将责备对号入座到脚本,然后扩大到我们这些从事与脚本有关的工作的人。他们会说,也许我们应该全面禁止脚本。也许, Microsoft 应该从操作系统中删除 Windows Script Host,也应该永久禁止宏功能。而且,把Scripting Guys都投入监狱并扔掉钥匙,让他们再没机会重见天日。(有趣的是,那些并不支持禁止脚本的人却也极力支持后面这个处罚措施。)
此外,我们也不否认脚本编写的确是某些上述攻击中的途径。是的,我们已经注意到了Kak.hta,我们也已经看到了 ILoveYou.vbs 病毒。(事实上,我们是最先遭受他们攻击的人之一。就权当真的有那么多人爱我们吧。)但是设想一下,如果我们真的放弃脚本编写,情况会变成怎么样?这些疯狂的举动就会停止了吗?
好吧,这的确是一个不错的想法,可是通过买魔力球彩票赢取十亿美元也是一个很不错的想法,而且两者发生的几率也是十分的相似(同为0)。当然,蠕虫和其他病毒的确可以写成脚本,但是它们也同样被写成批处理文件,难道我们也要全面禁止批处理文件吗?它们还被编写成可执行文件,难道我们也要禁止在电脑中使用可执行文件吗?将电脑中的可执行文件全部删除,那么你的电脑除了看起来有点酷之外,就毫无其它用处了。
嘿嘿,那样我们岂不是发明了一台 Macintosh!(致无处不在的 Macintosh 的爱好者:开玩笑而已。)
事实上,脚本编写只不过是使蠕虫以及其他病毒的编写变得更加简便,即使是这点还是值得商榷的(某些病毒是十分复杂的)。但是如果我们禁止使用脚本的话,病毒编写者只需回复从前的样子用 C 或者 C++语言编写即可。但是请记住,在病毒当道的今天,即使我们通过某种方法消除了所有病毒,计算机在处理的时候也并不是百分之百的安全。设想,如果一个连接到 Internet 的计算机用户共享了他(她)的整个硬盘。在这种情况下,对计算机进行肆意破坏时甚至不需要任何脚本的帮助。设想将计算机设置成无论是谁打开机器都能登录到操作系统的情况;某些人因为贪图方便将密码设置过于简单的情况以及其他你能想象的到的一些不安全设置。
这个问题的实质在于:只要我们使用电脑,那么一些不好的事情就有可能发生。这和生活中的所有事情都是一样的。比如让人们开车,就会有人会闯红灯或者倒车时撞上路旁的信箱。对于这些意外我们无能为力。我们所能做的就是教会人们怎样安全行车,并且提供安全带、安全气囊以及其他一些能够降低人们出事故时造成损害的安全设施。
这就是我们栏目今天所要传递给大家的信息。我们是否能够让人们停止传播病毒?我们是否能够让人们再不要将自己的计算机暴露于他人的潜在攻击下?我们是否能够保证计算机进行计算时百分之百的安全,消除一切出错的机会?不,我们做不到。至少在没有拔掉所有网络适配器、断开所有 Internet 连接以及彻底隔离各个计算机用户之前,我们是无法做到的。(致所有系统管理员:我们知道这听起来很诱人,但是这并不是Scripting Guy的正式建议。)不过,我们还是能够做些事情来帮助改善系统脆弱性的,防患于未然。脚本能够让我们的计算机变得更加安全,虽然这听起来似乎有点疯狂。(是的,我们说的的确是更加安全。请相信我们。)
本页内容
知识就是力量
最后的思考
知识就是力量
好了,好了:站在走廊上往下看,看那些手中掌握实权的家伙们。我们有时很难理解,他们的权利到底和他们所掌握的知识有什么联系。虽然事实如此,但是尽量多地掌握你所能得到的知识和信息毕竟不是一件坏事,这对于计算机安全这个问题尤其正确。你对于你的计算机以及它的设置知道得越清楚,那么你也就越有信心。这就是为什么计算机安全专家们建议大家对自己的计算机进行阶段性安全检查的原因。那么现在就剩一个问题:如何正确地进行这项阶段性安全检查?
事实上, Microsoft 已经发布了一款小巧实用的工具——Microsoft Baseline Security Analyzer——它能够帮助你完成这项安全检查。(我希望你已经下载了这个免费的小工具,如果还没有,请点击此处下载。)这款小工具的功能如其名称所示:它能够对一台计算机(如果你愿意,也可以是多台)进行分析,指出潜在的安全隐患,例如没有安装的安全补丁或者密码设置过于简单(甚至完全没有密码)的本地帐户。此软件个头虽小但却非常有用,无论是在家里还是办公室它都是必备软件。
当然,现在你也许在想:“好吧,既然 Security Analyzer 这么好用,我决定将它送给老妈作为圣诞礼物算了。但是这和脚本有什么关系呢?如果 Security Analyzer 能够提供我需要的一切,那我还要脚本干什么?Scripting Guys,我说的对吗?”
事实上,你说的很对:如果 Security Analyzer 能够做你所需要的一切,那么完全不必再使用脚本了。(或许你也并不是很需要那些救生圈,为什么不寄给 Scripting Guys,让我们来照顾它好了。)但是如果 Security Analyzer 并不能做你所需要的所有事情的时候怎么办?虽然 Security Analyzer 在收集信息方面做得很不错,但是毕竟它不能够对所收集到的信息做出任何反应。例如,它能够检测所有 5000 台工作站计算机的情况,并针对每台机子上的来宾帐户是否已经关闭做出详细报告(绝大多数机构都选择关闭来宾帐户以避免匿名或者没有授权的用户登录网络)。假设你的网络中有 2967 台计算机的来宾帐户没有关闭,那么 Security Analyzer 会将这一情况报告给你,但是最后还是需要你自己对每台机器进行处理,手动关闭这些机器上的来宾帐户。
后者就是脚本的用武之地。假如你想知道计算机上的来宾帐户是否已经关闭,你可以写一段脚本来完成上述过程,但是如果你只是想要检查一下这个帐户的状态的话,有什么意义?毕竟 Security Analyzer 就能够帮你检查帐户的状态,你完全没有必要写脚本。但是如果你想要做的不仅仅是检查一下帐户的状态而是想把打开的帐户全部关闭呢?单单靠 Security Analyzer 是不能完成上述两步的,但是你可以通过编写脚本来完成。
这也就是我们想在这里指出的。(如果你读过ADSI Scriptomatic Readme也许觉得很难相信,但是 Scripting Guys 实际上做的还是有点道理的)。脚本是一个很有用的工具,因为它可以帮助你得到与安全性相关的信息,但是它之所以成为必不可少的工具,其原因在于它可以对收集到的信息进行处理。这就是我们所要展示的:我们将向你展示 Security Analyzer 检索的信息,我们也想你展示如何通过编写能够检索同样信息的脚本。然后,在可能的时候,我们向你展示如何通过修改脚本来使它能够对检索的信息进行处理。
完全公布。好了,我们可能说的稍微有点夸张了:考虑到篇幅的原因,我们并不打算在此介绍 Security Analyzer 的所有功能。所以,我们会跳过中间的某些步骤,比如校验 SQL 服务器的帐户密码。希望你没有什么意见;Bentley 说他没问题。
顺便说一句,我们可能会在以后的专栏中更加深入地探讨与安全有关的话题。例如,我们会解释(或者至少试着解释)安全标识符奇怪而神秘的世界以及如何通过脚本来控制它。我们也会向你展示脚本在管理补丁程序和安全补丁方面发挥的作用。但是现在我们暂且先把这些内容搁置一旁。现在,就让我们不受任何打扰,开始讲解 Security Analyzer 能做什么以及如何通过脚本来实现相同的功能。
注意: 再打扰一下。为了使我们的脚本片段尽可能短,你在这里看到的脚本都是针对单机(本地单机)的。Security Analyzer 的实力在于它可以针对多机使用。脚本是否也能够做到呢?你猜猜看;有关更多详细信息请参阅前一期脚本故事专栏。擦亮你的眼睛,看看 Runomatic 是如何贴到脚本中心的。
什么是 Runomatic?你不想我们来破坏你自己找到答案时的那种惊奇感,是吧?
任务1:检索计算机名
老实告诉你吧,通过脚本来检索计算机名的途径数不胜数。但是因为 WMI 将要成为检索信息的主流技术,所以我们所展示的是一段返回目标计算机名称的 WMI 脚本。
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colComputers = objWMIService.ExecQuery _
("Select * from Win32_ComputerSystem")
For Each objComputer in colComputers
Wscript.Echo objComputer.Name
Next
这看起来似乎有点可笑,尤其是这段脚本只是针对单机运行的时候。但是当它的目标计算机有 100 台的时候,它能够帮助我们将各台计算机区分开来。
任务2:检索 IP 地址
通过脚本实现 IP 地址检索同样非常简单:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
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 strAddress
Next
End If
Next
在判定一台计算机的 IP 地址时,只要注意两点。第一,WMI 将包括 VPN 和 RAS 连接在内的所有东西都认为是计算机网络适配器设置的一部分,为此,我们需要使用 WHERE 语句Where IPEnabled = TRUE来将返回的信息限定为实际网络适配器的信息。
第二,IP 地址通常以数组的形式返回,因此我们需要用一个 For-Each 循环来获得真正的 IP 地址。如果我们直接回应 IP 地址属性值的话,将会发生“类匹配”错误。
任务3:报告安全检查进行的日期和时间
实现这个功能也是非常的容易,但是鉴于我们复制绝大多数 Security Analyzer 的功能,下面是一行返回日期和时间的代码:
Wscript.Echo Now
你们有人不是觉得脚本是非常高深的吗!其实就这么简单。
任务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 不能检测在显示器上是否有记录密码的便条,但是它却可以检查是否有本地用户帐号:
• 使用空密码
• 使用与用户名相同的密码
• 使用与计算机名相同的密码
• 使用password作为密码
• 使用单词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")
注意: 那么共享权限呢?如上文说到的,我们将会在以后的专栏中讨论。
任务15:检查 Windows 版本
安装了 Service Pack 1 意味着什么?如果你运行的是 Windows XP,那么说明你紧跟时代步伐,但是如果你使用的是 Windows 2000 的话,就说明你已经落伍了。(Windows 2000现在已经发展到 Service Pack 4 了)。这说明,如果你不知道计算机运行的是什么 Windows 版本的话,脚本返回的某些信息是没有意义的。这里就有一段脚本说明这一点:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer &
"\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery _
("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colOperatingSystems
Wscript.Echo objOperatingSystem.Caption, objOperatingSystem.Version
Next
任务16:检查 Office 宏的安全性
Microsoft Office 让用户自己决定如何运行嵌入文件中的宏。你可以选择:
• 高。只允许运行来自受信任来源的宏。所有其它经过签署的和未经签署的宏都将被禁用。
• 中。你可以选择是否运行可能不安全的宏。
• 低。允许运行所有宏。
在将宏安全性设置为高的情况下,你可以在其它时间中阻止自动运行的宏(当你打开一个文档或者运行某个程序的时候自动运行的那些宏)对你的计算机做些不好的事情。毫无疑问,一开始就将 Office 设置成为高安全性是个不错的主意,如果能够经常检查高安全性是否还在运行将会更好。
检查宏安全性级别可能会有点灵活,因为指向你所要更改的注册值可能会因为你安装的 Office 版本的不同而相差很大。我们没有精力来讨论如何判断 Office 的版本(提示:使用 WMI 组的Win32_Product)然后决定合适的注册表路径,但是至少这里有一段能够从 Outlook 2000 中检索出宏安全级别的脚本。你应该能够轻易地根据你的需要和所安装的 Office 版本来改进这段脚本:
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objReg=GetObject("winmgmts:\\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Office\10.0\Outlook\Security"
strValueName = "Level"
objReg.GetDWORDValue HKEY_CURRENT_USER, strKeyPath, strValueName,
dwValue
If dwValue = 3 Then
Wscript.Echo "Outlook macro security is set to high."
ElseIf dwValue = 2 Then
Wscript.Echo "Outlook macro security is set to medium."
Else
Wscript.Echo "Outlook macro security is set to low."
End If
上述这段脚本是用来检测 Outlook 的宏安全性级别的。那么,如果你想要检测 Word、Excel、PowerPoint 或者 Access的宏安全级别的时候该怎么办?好吧,你只需要根据需要更改注册表路径即可:
strKeyPath = "Software\Microsoft\Office\10.0\Word\Security"
strKeyPath = "Software\Microsoft\Office\10.0\Excel\Security"
strKeyPath = "Software\Microsoft\Office\10.0\PowerPoint\Security"
strKeyPath = "Software\Microsoft\Office\10.0\Access\Security"
任务17:检查 IE 安全区域
为了保护用户不受恶意网站的攻击,Internet Explorer 使用安全区域来管理在你访问某个网站时可能完成或无法完成的行为(比如运行脚本、安装 ActiveX 控件等等)。在默认设置下,这些安全区域提供不同的安全级别:你可以在本地内部网络区域执行在受限网站区域中不能执行的程序(比如运行脚本)。
如果你想要了解更多有关安全区域的情况,我们建议你阅读Internet Explorer 增强安全性设置白皮书(此白皮书是针对 Windows 2003 编写的,但是其中的内容对于其它 Windows 版本同样适用。)在这里,我们有一段能够报告本地内部网络区域(区域1)安全级别的脚本:
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objReg=GetObject("winmgmts:\\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Internet
Settings\Zones\1"
strValueName = "CurrentLevel"
objReg.GetDWORDValue HKEY_CURRENT_USER,strKeyPath,strValueName,dwValue
Select Case dwValue
Case 73728
Wscript.Echo "The security zone is set to high security."
Case 69632
Wscript.Echo "The security zone is set to medium security."
Case 66816
Wscript.Echo "The security zone is set to medium-low security."
Case 65536
Wscript.Echo "The security zone is set to low security."
Case Else
Wscript.Echo "The security zone is set to custom security."
End Select
你也可以通过此段脚本检索有关受信任站点(区域2)、Internet(区域3)以及受限制站点(区域4)的安全信息。只需要相应更改注册表路径:
Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2
Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\3
Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\4
在绝大多数情况下,这个方法能够向你提供所需的信息。唯一会碰到的问题是用户是否已经更改了安全区域的一项或者多项设置。在那样的情况下,安全级别将会被报告为"用户自定义",但是你就无从知道这个新的用户自定义的安全级别是比原来的默认级别更加严格还是更加宽松。这也就是脚本再次派上用场的地方:你可以使用一段脚本来决定(或者配置)各个安全区域的安全设置。有关这方面的更多信息,请参阅Internet Explorer 增强安全性设置白皮书或者擦亮你的眼睛看看 Tweakomatic。(不,这不是拼写错误。我们真的将要发布一款名叫Tweakomatic的东西。你一定要相信我们迟早会这么做的。)
任务18:检查 Outlook 安全区域
Microsoft Outlook 同样使用 Internet Explorer 的安全区域来决定是否运行来自于 HTML 格式信息的脚本和活动内容。要避免在计算机上运行嵌入了 HTML 信息的脚本,你需要做两件事情:
• 确保在 Internet Explorer 安全区域内运行脚本的功能已经被禁用(特别是在受限制的站点区域内)。
• 确保 Outlook 使用那个安全区域
Outlook 安全区域记录在注册表中;同样,指向那个注册表值的确切路径取决于你在计算机上安装的 Office 版本。如果你使用 Office 2000,你可以使用以下脚本来决定 Outlook 的安全区域:
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objReg=GetObject("winmgmts:\\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Office\10.0\Outlook\Options\General"
strValueName = "Security Zone"
objReg.GetDWORDValue HKEY_CURRENT_USER,strKeyPath,strValueName,dwValue
Select Case dwValue
Case 4
Wscript.Echo _
"Outlook is using settings from the Restricted Sites zone."
Case 3
Wscript.Echo "Outlook is using settings from the Internet
zone."
Case 2
Wscript.Echo "Outlook is using settings from the Trusted Sites
zone."
Case 1
Wscript.Echo "Outlook is using settings from the Local Intranet
zone."
Case Else
Wscript.Echo "The Outlook security zone could not be
determined."
End Select
在绝大多数计算机上,受限制的站点区域有最严格的安全限制。正因为如此,你可能会更进一步来为所有用户配置 Outlook 设置,以保证 Outlook 在 HTML 安全上使用受限制的站点。这里正好是一段实现以上功能的脚本:
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objReg=GetObject("winmgmts:\\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Office\10.0\Outlook\Options\General"
strValueName = "Security Zone"
dwValue = 4
objReg.SetDWORDValue HKEY_CURRENT_USER, strKeyPath, strValueName, dwValue
返回页首
最后的思考
这里我们想再次强调一点:如果 Security Analyzer 能够符合你的需求,那么你就没有必要再写那些和 Security Analyzer 实现同样功能的脚本。(Scripting Guys 绝对坚持认为你不必去做你完全没有必要做的事情。)但是从另外一方面来说,Security Analyzer 只能够实现它编写设计的功能是它的一个局限性。之所以会如此是因为你觉得还有其他一些很重要的安全检查,而这些检查是 Security Analyzer 所不能做到的。例如,也许你想确定一下所有用户是否都使用受密码保护的屏保程序。Security Analyzer 能够做到吗?不能,但是你却可以通过编写一段很简单的脚本来实现:
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objReg =GetObject("winmgmts:\\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Control Panel\Desktop"
ValueName = "ScreenSaverIsSecure"
objReg.GetStringValue HKEY_CURRENT_USER, strKeyPath, ValueName,
strValue
If strValue = "1" Then
Wscript.Echo "The screen saver is password protected."
Else
Wscript.Echo "The screen saver is not password protected."
End If
然后,当然了,你也可以再进一步对屏保程序进行密码保护:
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objReg =GetObject("winmgmts:\\" & strComputer &
"\root\default:StdRegProv")
strKeyPath = "Control Panel\Desktop"
ValueName = "ScreenSaverIsSecure"
strValue = "1"
objReg.SetStringValue HKEY_CURRENT_USER, strKeyPath, ValueName,
strValue
所以,也许你使用 Security Analyzer 来完成某些任务同时使用脚本来完成其它一些任务。问题的重点在于,你不应该用非此即彼的主张来看待安全问题:不是使用 Security Analyzer 就是通过编写脚本。相反,无论使用哪个都能起到很好的效果。毕竟,脚本通常用来辅助已经使用的工具而不是完全取代这些工具。选择你应该干的工作。
或者,至少随便做一些能够让你的管家去做的事情。
你对栏目有什么问题或意见吗?请写信到 scripter@microsoft.com。找 Bentley。