How to use scripting to secure your Win2K network--part 2

From: Tony Chow (tchow@BLUETENTACLE.COM)
Date: 10/12/01


Message-ID:  <50B30C640EC48648ABAA34F00D737A96CDB0@leto.bluetentacle.local>
Date:         Fri, 12 Oct 2001 13:47:51 -0700
From: Tony Chow <tchow@BLUETENTACLE.COM>
Subject:      How to use scripting to secure your Win2K network--part 2
To: NTBUGTRAQ@LISTSERV.NTBUGTRAQ.COM


======================================
Delivery co-sponsored by GFI Software
======================================
LANguard Security Event Log Monitor offer!

Catch hackers red-handed with LANguard S.E.L.M.! Provides intrusion
detection through centralized NT/2000 security event log monitoring.
Extensive reporting identifies all machines being targeted & local users
trying to hack. Download your FREE starter pack today:
http://www.gfisoftware.com/stats/adentry.asp?adv=107&loc
======================================
List message follows...

Since posting the first article I've requested quite a few requests for
some actual code samples. Here it is. I've derived from my production
script a self-contained script that automatically installs hotfixes on a
Windows 2000 system. Please note the following:

-You will need to put hotfix files on a network share.

-You'll need to separate those hotfixes that are for server features
only from those that are applicable for both Win2K Server and Win2K
Professional, and put them in different folders.

-You'll need shutdown.exe, from the resource kit, and put it also in a
network share.

-You'll need blat.exe, a free command-line e-mail program, and put it,
again, in a network share. Blat can be found at
http://www.interlog.com/~tcharron/blat.html.

-You'll need qchain.exe, available free on Microsoft's web site. This
should also be put in a network share.

-You will need to modify some constants in the script to reflect the
paths of above resources as well as other resources

All network shares should be accessible to the computer account if the
script is to be run as a startup script.

The script is written in JScript. Just copy the code below and paste it
into a text file and name the file installhotfixes.js.

The script accepts the following parameters:

/debug Normally if an exception is encountered in the script, an e-mail
notification is sent to the administrator and the script quits
gracefully. If this switch is turned on, the exception is raised.

/s When this switch is specified, server-specific hotfixes are
installed. Otherwise, only hotfixes that apply to both Professional and
Server are installed.

/noreboot When this switch is specified, the script sends an e-mail
request to the administrator if a reboot is needed. Otherwise, the
script reboots automatically if necessary.

The script is provided as-is and liability-free. I will not respond to
any technical support requests. Nor will I take any responsibility for
the damage it may cause to your computers. Use it at your own risk. Do
not try to run it until you have a fairly good idea of exactly what it
does.

Tony Chow
Systems Administrator, Job Seeker
Resume: http://www.bol.ucla.edu/~everblue/resume.html

---------------------------------------

/*
        Initialize Global Constants
        There is no notion of constants in JScript, so we use var in its
place.
*/

/*
        replace the strings below with information specific to your
network.
        ensure that the computer account is assigned rights to the
network resources.
        Authenticated Users special group covers computer accounts.
*/
//1. Hotfix folder for hotfixes that apply to both Windows 2000
Professional and Server
var GeneralHotfixLoc="\\\\someserver\\someshare";
//2. Hotfix folder for Windows 2000 Server-only hotfixes.
var ServerHotfixLoc="\\\\someserver\\someshare\\server";
//3. blat.exe, the command-line e-mail program. It can be downloaded
from:
// http://www.interlog.com/~tcharron/blat.html
var BlatLoc="\\\\someserver\\someshare\\shutdown.exe";
//4. Location of shutdown.exe, from the resource kit.
var ShutdownLoc="\\\\someserver\\someshare\\shutdown.exe";
//5. Location of qchain.exe, from Microsoft web site
var QchainLoc="\\\\someserver\\someshare\\qchain.exe";
//6. Administrator E-mail address
var AdminEmail="someone@somewhere.com";
//7. SMTP server address and port
var SMTPServer="mail.somedomain.com:25";
//8. SMTP account address
var SMTPAccountAddress="someone@somewhere.com";

//Event log types
var INFORMATION=4, SUCCESS=0, ERROR=1, WARNING=2;

/*
        Initialize global objects.
*/
var shell = new ActiveXObject("Wscript.Shell");
var net = new ActiveXObject("WScript.Network");
var fso = new ActiveXObject("Scripting.FileSystemObject");

/*
        Error handling routine
        if an exception is encountered in the script,
        it is reported to the administrator via e-mail;
        however, if a /debug switch is specified, the exception is
raised
        for debugging purposes.
*/
if (IsArgumentPresent("/debug"))
{
        main();
}
else
{
        try
        {
                main();
        }
        catch(e)
        {
                var i;
                var ErrTxt = "Script [" + WScript.ScriptName + "]
encountered a fatal error. Please diagnose.\n\n";
                ErrTxt += e.number + "\n\n" + e.description;
                shell.LogEvent(ERROR, ErrTxt);
                SendNotificationEmail("Script Error on [" +
net.ComputerName + "] under user [" + net.UserName + "]", ErrTxt);
        }
}

/*
        Destroy global objects.
*/
var shell = null;
var net = null;
var fso = null;

//Main code
function main()
{
        //if the switch /s is specified, server hotfixes are installed
as well.
        if (IsArgumentPresent("/s"))
        {
                InstallHotfixesInFolder(new Array(GeneralHotfixLoc,
ServerHotfixLoc));
        }
        else
        {
                InstallHotfixesInFolder(GeneralHotfixLoc);
        }

        //reboot if pendingfilerenameoperations exist, except when the
-hfonly switch is specified,
        //in which case only a notification message is sent, reminding
the admin to reboot the server when opportune.
        
        //simplest way of knowing if a registry value exists is to just
read it.
        //If an exception is encountered when reading, then the value
doesn't exist.
        //This method requires no additional scripting components to be
installed.
        try
        {
        
shell.RegRead("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session
Manager\\PendingFileRenameOperations");
        }
        catch(e)
        {
                return;
        }

        shell.Run("\"" + QchainLoc + "\"", 0, true);

        //if an argument /noreboot was passed to the script, then
instead of rebooting,
        //send an e-mail notification asking the administrator to reboot
when convenient.
        if (IsArgumentPresent("/noreboot"))
        {
                SendNotificationEmail("[" + net.ComputerName + "]:
Reboot Request", "Automatic hotfix update concludes that this system
needs to be restarted to complete the hotfix installation. Please
reboot the system when convenient.");
        }
        else
        {
                Reboot();
        }
}

//This function accepts either a string specifying the location of
hotfixes
//or an array specifying multiple locations.
//if multiple locations are specified
function InstallHotfixesInFolder(HotfixFolders)
{
        var spver;
        var ds;
        var rx;
        var hotfixid;
        var unconditional = false;
        var temp;
        var i;

        //This script handles only Windows 2000 hotfixes. Therefore
check the
        //OS version first.
        if (!IsWin2K())
        {
                return;
        }

        //if a string was passed as a parameter, turn it into a
single-element array
        //for convenience's sake.
        if (typeof(HotfixFolders) == "string")
        {
                temp = HotfixFolders;
                HotfixFolders = new Array();
                HotfixFolders[0] = temp;
        }

        spver = ServicePackVersion();

        //Now, get a sorted list of the files by date
        //if there are multiple hotfix folders, they are merged into a
single list.
        ds = GetSortedFileList(HotfixFolders, 2);

        if (ds.RecordCount > 0)
        {
                //going down the list...
                for (ds.MoveFirst();!ds.EoF;ds.MoveNext())
                {
                        //Regular expression identifies hotfixes by
their unique naming convention.
                        //also extracts the hotfix id and the associated
service pack number.
                        //only hotfixes later than the current service
pack will be installed.
                        rx = /^(q\d+)_w2k_sp(\d+)_x86_en\.exe/gi;
                        if (rx.test(ds.Fields("Name").Value) &&
(parseInt(RegExp.$2) >= (spver + 1)))
                        {
                                hotfixid = RegExp.$1;
                                if (unconditional ||
!IsHotfixInstalled(hotfixid, ds.Fields("DateModified").Value))
                                {
                                        shell.LogEvent(INFORMATION,
"Install script has started to install Hotfix " + hotfixid);
                                        shell.Run("\"" +
ds.Fields("Path").Value + "\" -m -z -n", 0, true);
                                        //record the modified date of
this hotfix, so that if the same hotfix is
                                        //later re-released, it will be
reapplied.
        
shell.RegWrite("HKLM\\SOFTWARE\\Custom Automated Installations\\Hotfix
Date Check\\" + hotfixid + "\\Date",
Date.parse(ds.Fields("DateModified").Value), "REG_SZ")
                                        //once one hotfix is installed,
all subsequent hotfixes (ordered by date)
                                        //will be applied regardless of
conditions. Hence unconditional = true.
                                        //This is to avoid an earlier
hotfix from overwriting files replaced by later
                                        //ones.
                                        unconditional = true;
                                }
                        }
                }
        }
}

/*
        Checks if a hotfix has been installed or not
        it takes into account the scenario in which Microsoft
re-releases an updated version
        of an old hotfix, in which case the hotfix will need to be
reapplied.
*/
function IsHotfixInstalled(ID, HotfixDate)
{
        try
        {
                if ((shell.RegRead("HKLM\\SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion\\Hotfix\\" + ID + "\\Installed") == 1) &&
(shell.RegRead("HKLM\\SOFTWARE\\Custom Automated Installations\\Hotfix
Date Check\\" + ID + "\\Date") == Date.parse(HotfixDate)))
                {
                        return true;
                }
        }
        catch(e)
        {
        }
        
        return false;
}

//Utility functions

/*
        This function returns the current NT service pack version in an
integer value.
        0 indicates no service pack has been installed.
*/
function ServicePackVersion()
{
        var rx;
        
        //if an exception is encountered, then no service pack is
assumed to be installed
        try
        {
                rx = /service\spack\s(\d+)/gi;
        
rx.exec(shell.RegRead("HKLM\\SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion\\CSDVersion"));
                return parseInt(RegExp.$1);
        }
        catch(e)
        {
        }
        
        //otherwise, return service pack 0
        return 0;
}

/*
This simple function reboots the computer.
*/
function Reboot()
{
        shell.LogEvent(INFORMATION, "The script is rebooting the
system.");
        shell.Run("\"" + ShutdownLoc + "\" /c /l /r /t:0",0,true);
}

/*
This function determines whether the operating system is Windows 2000
*/
function IsWin2K()
{
        if (shell.RegRead("HKLM\\SOFTWARE\\Microsoft\\Windows
NT\\CurrentVersion\\CurrentVersion").toLowerCase() == "5.0")
        {
                return true;
        }
        else
        {
                return false;
        }
}

/*
        This function returns a sorted file list from a directory or a
list of directories.
        If multiple directories are specified, files are merged into a
single list.
        The list is a standalone ADO recordset.
*/
function GetSortedFileList(FolderPaths)
{
        var f;
        var e;
        var rs;
        var i;
        var o;
        var sortstr;
        var temp;
        
        rs = new ActiveXObject("ADODB.Recordset");
        rs.CursorLocation = 3;
        rs.Fields.Append("Name", 200, 255);
        rs.Fields.Append("Path", 200, 255);
        rs.Fields.Append("DateModified", 7);
        
        rs.Open();
        

        if (typeof(FolderPaths) == "string")
        {
                temp = FolderPaths;
                FolderPaths = new Array();
                FolderPaths[0] = temp;
        }
        
        for (i in FolderPaths)
        {
                f = fso.GetFolder(FolderPaths[i]);
                if (!fso.FolderExists(FolderPaths[i]))
                {
                        break;
                }

                if (f.Files.Count < 1)
                {
                        break;
                }

                e = new Enumerator(f.Files);
                for (;!e.atEnd();e.moveNext())
                {
                        o = e.item();
                        rs.AddNew();
                        rs.Fields("Name").Value = o.Name;
                        rs.Fields("Path").Value = o.Path;
                        rs.Fields("DateModified").Value =
o.DateLastModified;
                        rs.Update();
                }
        }
        
        rs.Sort = "DateModified";
        rs.MoveFirst();
        
        return rs;
}

/*
Sends an e-mail message to the administrator for notification purposes.
*/
function SendNotificationEmail(Subject, Message)
{
        var fn =
shell.ExpandEnvironmentStrings("%temp%\\_notifytemp.txt");
        var a = fso.CreateTextFile(fn);
        a.Write(Message);
        a.Close();
        shell.Run("\"" + BlatLoc + "\" \"" + fn + "\" -t " + AdminEmail
+ " -server " + SMTPServer + " -f " + SMTPAccountAddress + " -s \"" +
Subject + "\"", 0, true);
        fso.DeleteFile(fn);
}

/*
This simple function determines whether a command line argument
specified
in the arg parameter has been passed to the script. Returns true or
false.
Case-insensitive.
*/
function IsArgumentPresent(arg)
{
        var e = new Enumerator(WScript.Arguments);
        for (;!e.atEnd();e.moveNext())
        {
                if (e.item().toLowerCase() == arg.toLowerCase())
                {
                        return true;
                }
        }
        return false;
}



Relevant Pages

  • Newbie Question - Hotfix / Patching script
    ... create a script that will install multiple hotfixes one after the other ... The problem is that when I run the script it ... therefore some dont install because there is another hotfix being installed ...
    (microsoft.public.scripting.vbscript)
  • Re: Login Script - Installing HotFix/Patches
    ... >> When I install the hotfix's patches via login script they work fine. ... > You could let the script make a flag file (e.g. in the ... > Check the version number of a dll/exe file that the hotfix includes to see ...
    (microsoft.public.security)
  • Re: Using VBS to scan NT machines for KB828028 (MS04-007)
    ... you could try my script which will do much more than ... just one hotfix... ... How are you getting this to work with NT4 machines? ... WMI never returns a ...
    (microsoft.public.scripting.vbscript)
  • RE: Problem with auto unpacking Hotfixes (from 1 machine only)
    ... However the problem is actually extracting the files in the first place. ... I have noticed on my local machine when unpacking a hotfix it uses the ... can call via the script that will do the job appropriatley? ...
    (Focus-Microsoft)
  • Re: Hotfix Deployment via Login Script
    ... I need to deploy a hotfix via a login script. ... > a script to install the hotfix is no problem, the problem is that my users ... administrative logon after it has been applied? ...
    (microsoft.public.scripting.wsh)