Saturday, February 19, 2011

Powershell: Connect to Active Directory

Here is a Powershell script to determine domain membership and then connect to the domain in order to run further commands. Fortunately there is an easier way in Windows Server 2008 R2 by launching Powershell with the Active Directory module. You can then use commands such as New-ADGroup.

Here it is though - a script that does not require the AD module....


#Determine Domain Name
$ObjReg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', ‘.’)
$ObjRegKey = $ObjReg.OpenSubKey("SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters")
$DomName = $ObjRegKey.GetValue("Domain")
$domaincnformat = “”
$fqdncomponents=$DomName.split(‘.’)
foreach ($comp in $fqdncomponents) {$domaincnformat = $domaincnformat + ‘dc=’ + $comp + ‘,’}
$domaincnformat = $domaincnformat.substring(0,$domaincnformat.length-1)
#Connect to Active Directory
$domain = [ADSI] “LDAP://dc=$domaincnformat”

$OU = "TestOU"

$CreateOU = $domain.Create(“OrganizationalUnit”,”ou=” + $object.OU)
$CreateOU.SetInfo()

Thursday, February 17, 2011

Extension to Windows Audit: Expand lists of groups against domain

This script takes input from the Windows auditing script on this site (the one that extracts from SYDI-Server XML into CSV, or the Powershell script on this site that audits servers using ADSI interface rather than WMI)

The input must be CSV format and have the following headers: -

"Server","User","DisplayName","Memberof","HPA","AccountCode","AccessGroups","Status"

Server: The name of the server where the users/groups were extracted from. This is not used in the script however it is written back to the output as is.

User: Either a user or group name. Script will check determine if a local user/group or a domain user/group. If it is a domain group then it will be recursed to determine all members of this domain group including all nested groups. The aim is to expand out all groups and so this column should only contain users after running the script.

Displayname: The display name of all domain users will be returned in this column.

Memberof: The trail through which each user is a member of the group originally specified in the "User" column.

HPA: not used in this script - can be used to note whether each user is HPA highly privileged access).

AccountCode: not used in this script. Can be used to put a code against each user account.

AccessGroups: Name of group that was expanded out. This column may or may not be useful.

Status: If "Local" then the user/group is a local user/group. If "Direct" then user is a domain user that is directly a member of the local group on the server. If "Expanded User" then user is a member of domain groups that are a member of the local group on the server. If "ForeignPrincipal" then an expanded security object is a place holder - an object from another domain and cannot be further expanded by this script. If "Non AD" then the domain could not findan object with the specified name. You may need to run the script against the output in another domain in order to expand out this object.

The script can be run against its own output as many times as required. This allows the script to be run in multiple domains and thus fully expanding out all group memberships within a list.





$grouphash = @{}
function Get-RecursiveGroupMembership {
param (
[string] $groupname,
[string] $lastname
)
$members = @()

if($grouphash[$groupname] -eq $null)
{
#$grouphash[$line.User] = Get-QADGroupMember $line.User -Indirect | Select-Object NTAccountName | foreach {$_.NTAccountName}
$this = (Get-QADGroup $groupname).member | Get-QADObject | Select-Object NTAccountName, type, displayName
}
$this = $grouphash[$groupname]
$this = (Get-QADGroup $groupname).member | Get-QADObject | Select-Object NTAccountName, type, displayName
$this | foreach {
if ($_.type -eq 'user') {
$mem ="" | Select-Object name, grp, type, displayname;
$mem.name = $_.NTAccountName
$mem.grp = $lastname
$mem.type = "Expanded User"
$mem.displayname = $_.displayName
$members = $members + $mem
#$members += @('"'+$_.NTAccountName+'"' , '"'+$lastname+'"')
}
elseif ($_.type -eq 'computer') {
$mem ="" | Select-Object name, grp, type, displayname;
$mem.name = $_.NTAccountName
$mem.grp = $lastname
$mem.type = "Expanded User"
$mem.displayname = ""
$members = $members + $mem
#$members += @('"'+$_.NTAccountName+'"' , '"'+$lastname+'"')
}
elseif ($_.type -eq 'group') {
$newlastname = $lastname + ':' + $_.NTAccountName
#Write-Host "Adding sub group $_" + $newlastname

$members += Get-RecursiveGroupMembership $_.NTAccountName $newlastname
}
else {#Probably Foreign Security Principal - should note as such
$mem ="" | Select-Object name, grp, type, displayName;
$mem.name = $_.NTAccountName
$mem.grp = $lastname
$mem.type = "ForeignSecurityPrincipal"
$mem.displayname = ""
$members = $members + $mem

}
}
return $members
}
cls

Add-PSSnapin Quest.ActiveRoles.ADManagement –ErrorAction SilentlyContinue

$csvin = Import-Csv info.csv | where-Object {$_.HPA -eq "Yes"}

connect-QADService



try{
'"Server","User","DisplayName","Memberof","HPA","AccountCode","AccessGroups","Status"' | Out-File -filepath "infoOut.csv"
} catch [System.Management.Automation.Host.PromptingException]
{
return
} catch [Exception]
{
Write-Host "Error!!!"
return
}

#$myobj = "" | select "Server","User","DisplayName","Memberof","HPA","AccountCode","AccessGroups","Status"

foreach($line in $csvin)
{
if($line.HPA -ne "Yes")
{
continue
}

$user = $line.User.Split('\')

if($user[0] -eq $line.Server)
{
"Local User: " + $line.User
'"'+$line.Server +'","'+ $line.User + '","' + $line.DisplayName + '","Administrators","Yes","","","Local"' | Out-File -filepath "infoOut.csv" -Append
continue
}

$ret = Get-QADObject $line.User
if($ret -ne $null)
{
if($ret.ClassName -eq "group")
{
# "Good User: " + $line.User
# if($grouphash[$line.User] -eq $null)
# {
# "Getting Group Users"
# $grouphash[$line.User] = Get-QADGroupMember $line.User -Indirect | Select-Object NTAccountName | foreach {$_.NTAccountName}
# }
$membersout = Get-RecursiveGroupMembership $line.User $line.User

foreach($mem in $membersout )
{
#'"Server","User","Member of","HPA","Account Code","Access Groups","Status"' | Out-File -filepath "infoOut.csv"
'"'+$line.Server +'","'+ $mem.name + '","' + $mem.displayName + '","'+$mem.grp +'","Yes","","'+ $line.User +'","' + $mem.type + '"' | Out-File -filepath "infoOut.csv" -Append
}

} elseif($ret.ClassName -eq "user") {
'"'+$line.Server +'","'+ $line.User + '","' + $ret.displayName + '","Administrators","Yes","","","Direct"' | Out-File -filepath "infoOut.csv" -Append

} else
{
'"'+$line.Server +'","'+ $line.User + '","","Administrators","Yes","","","Direct-Foreign"' | Out-File -filepath "infoOut.csv" -Append
}
} elseif ($line.status -eq "Expanded User") {
"Previously expanded user: " + $line.User
'"'+$line.Server +'","'+ $line.User + '","' + $line.Displayname + '","'+ $line.Memberof + '","Yes"," ","' + $line.AccessGroups + '" , "' + $line.status + '"' | Out-File -filepath "infoOut.csv" -Append

} elseif ($line.status -eq "Direct") {
"Previously Direct User: " + $line.User
'"'+$line.Server +'","'+ $line.User +'","' + $line.Displayname + '","' + $line.Memberof + '","Yes"," ","' + $line.AccessGroups + '","' + $line.status + '"' | Out-File -filepath "infoOut.csv" -Append

} else
{
"Non Ad User: " + $line.User
'"'+$line.Server +'" , "'+ $line.User + '","' + $line.Displayname + '","Administrators" , "Yes" ,"","","NON AD"' | Out-File -filepath "infoOut.csv" -Append
}
$ret = $null
}
Disconnect-QADService
$kv = @()
foreach($hk in $grouphash.Keys)
{
foreach($st in $grouphash[$hk])
{
$obnew = New-Object System.Object
$obnew | Add-Member -MemberType NoteProperty -Name Group -Value $hk
$obnew | Add-Member -MemberType NoteProperty -Name Member -Value $st
$kv += $obnew
}
}

Tuesday, February 15, 2011

VBScript: Remote Auditing of Linux servers

The only real way to audit Linux servers remotely is via SSH connections. Unfortunately this is not built into Windows (although apparently a .NET library can be called through PowerShell). The solution may be the Chilkat SSH ActiveX control. Unfortunately it does cost - $250US for a site license at last check.

Here is a script that uses that SSH ActiveX control to connect to Linux servers and extract HPA (Highly Privileged Access) accounts - i.e. all accounts that have root access. The script can be modified to run almost any command however. It then exports the information to CSV files. The two CSVs exported are the user - group memberships, and the SUDoers file which says what each user and group can do.

Use Excel to create formulas to determine which users and groups are HPA.

Here is the script that has been tested against RHEL 4 and 5 as well as Solaris 8 and 10. Not guaranteed to work against all servers however as it depends considerably on the setup of each server:

'************************************************************
'************************************************************
'******* Script to connect to UNIX and LINUX type ***********
'******* servers to run remote commands ***********
'******* ***********
'******* Includes function to elevate privileges ***********
'******* ***********
'******* Requires Chilkat SSH ActiveX component ***********
'************************************************************
'************************************************************


'************************************************************
'DEFINE TEXT FILES AND CREATE OBJECTS
'************************************************************

Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

Dim fso, logFile, dataFile, serverFile, UIDFile, groupFile, sudoersFile, x, y

' verboseLogging = 0 ' Minimal logging
' verboseLogging = 1 ' Verbose logging
verboseLogging = 2 ' Full logging

Set fso = CreateObject("Scripting.FileSystemObject")
Set dataFile = fso.CreateTextFile("RawData.txt", True) 'Output information from commands
Set logFile = fso.CreateTextFile("SessionLog.txt", True) 'Verbose Logging
Set groupFile = fso.CreateTextFile("GroupMemberships.txt", True) 'User group memberships
Set sudoersFile = fso.CreateTextFile("SUDOERS.txt", True) 'Sudoers output
Set serverFile = fso.OpenTextFile("Servers.txt", ForReading) 'Input information about servers

If Err.Number <> 0 Then
wscript.echo "CRASH: Cannot open file for reading: Servers.txt. " & Err.Number & " " & Err.Description
wscript.quit
End If

' Important: It is helpful to send the contents of the
' ssh.LastErrorText property when requesting support.
set ssh = CreateObject("Chilkat.Ssh") 'Create connection to Chilkat ActiveX Control
success = ssh.UnlockComponent("30-day trial") 'Unlock 30 day trial of ActiveX Control
If (success <> 1) Then
MsgBox ssh.LastErrorText
WScript.Quit
End If
if verboseLogging > 0 Then logFile.WriteLine("The result of creating an object should be 1. The result is: " & success)
if verboseLogging > 0 Then logFile.WriteLine("The last error message is: " & ssh.LastErrorText)

'************************************************************
'THESE ARE THE COMMANDS WE WILL RUN AGAINST EACH SERVER
'************************************************************

' Grab the user with UID of 0 as this is the root user
command2 = "cut -d: -f1,3 /etc/passwd |grep :0$"

' Grab all users (listed in passwd) and return their group memberships. If Solaris (SunOS) then it also prints the username. RHEL does this automatically.
command3 = "for i in `cat /etc/passwd |cut -d: -f1`; do if [ ""`uname -s`"" = ""SunOS"" ]; then printf ""$i : ""; fi; groups $i; done"

' Find sudo and parse its binary for the location of sudoers. We then grep out lines starting with # (^#), and print to screen
' ^# is escaped with backslash as it can be a special character on Solaris.
command5a = "which sudo | xargs strings |grep sudoers |grep \/"
command5 = "which sudo | xargs strings |grep sudoers |grep \/ |xargs grep -v \^# |grep ALL |awk '{print$1, $2, $3}'"

' If unable to find sudoers location in the binary of sudo then fall back to searching for sudoers in one of the following locations.
command6 = "find /opt/sfw/etc /usr/local/etc /etc -name sudoers -print 2>/dev/null |xargs grep -v \^# |grep ALL |awk '{print$1,$2,$3}'"

'************************************************************
'MAIN ROUTINE (CONNECT TO SERVERS IN SERVERS.TXT)
'************************************************************

' Servers.txt MUST contain 5 comma separated items on each line:
' Hostname or IP Address,SSH Port,Username,Password,SU Password
' myhost,22,username,password,supassword

Do Until serverFile.AtEndOfStream
blFoundErr = "False"

' Parse servers.txt in CSV format and read the arguments
arrFileLine = CSVParser(serverFile.ReadLine)
On Error Resume Next
hostname = trim(arrFileLine(0))
port = trim(arrFileLine(1))
authuser = trim(arrFileLine(2))
authpwd = trim(arrFileLine(3))
supwd = trim(arrFileLine(4))
If Err.Number <> 0 Then
logFile.WriteLine("Error reported: "&Err.Number)
logFile.WriteLine("ERROR: A line in the server.txt was an error and was not processed.")
blFoundErr = True
End If


On Error Goto 0


' If no errors then connect to server and collect information
If blFoundErr = "False" Then
lineToWrite = "INFO: Working with hostname """ & hostname &_
""" on port: """ & port & """ with username """ & authuser & """." & vbCRLF &_
vbCRLF & "This script will connect SSH, elevate SU and run the commands: "&vbCRLF&_
command2&vbCRLF&command3&vbCRLF&command5&vbCRLF&command6
wscript.echo lineToWrite
logFile.WriteLine(lineToWrite)

logFile.WriteLine("Servername: "&hostname)
dataFile.WriteLine("Servername: "&hostname)
groupFile.WriteLine("Servername: "&hostname)
sudoersFile.WriteLine("Servername: "&hostname)

Result=""

'************************************************************
'CONNECT TO THE SERVER USING SSH
'************************************************************

' Keep a session log, which is available via the SessionLog property:
ssh.KeepSessionLog = 1

' Connect to the server using SSH
success = ssh.Connect(hostname,port)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Connecting to host "&hostname&" on port "&port&".")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If

'wscript.echo "Success code when connecting to host: " & success

' When reading, if no additional data arrives for more than 20 seconds, then abort:
ssh.IdleTimeoutMs = 20000

' SSH Server Authentication
' If there is no login/password required, you must still call AuthenticatePw and use any values for login/password.
If Result <> "ERROR" Then
success = ssh.AuthenticatePw(authuser,authpwd)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Was able to connect to "&hostname&" but unable to authorize with the provided credentials.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If

'wscript.echo "Success code when authenticating to server: " & success

' Open a session channel.
If Result <> "ERROR" Then
channelNum = ssh.OpenSessionChannel()
If (channelNum < 0) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Opening Session on host "&hostname&". I may have previously authenticated successfully however. Please check the log.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If

' Request a pseudo-terminal
termType = "dumb"
widthInChars = 120
heightInChars = 40
pixWidth = 0
pixHeight = 0

If Result <> "ERROR" Then
success = ssh.SendReqPty(channelNum,termType,widthInChars,heightInChars,pixWidth,pixHeight)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Requesting Pseudo-Terminal on host "&hostname&". I may have previously authenticated successfully however. Please check the log.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If

'wscript.echo "Success code when requesting pseudo-terminal on host: " & success


' Start a shell on the channel:
If Result <> "ERROR" Then
success = ssh.SendReqShell(channelNum)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Error Requesting Pseudo-Terminal on host "&hostname&". I may have previously authenticated successfully however. Please check the log.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If
End If

'wscript.echo "Success code trying to start shell on the channel: " & success

If Result <> "ERROR" Then logFile.WriteLine("INFO: This server appears to have been connected to successfully.")
logFile.WriteLine("INFO: Server "&hostname&" was connected if this is 1: " & ssh.IsConnected & vbCRLF)


'wscript.sleep(5000)

if verboseLogging > 0 Then logFile.WriteLine("INFO: VERBOSE: Completed a sleep interval and starting to run commands.")

'************************************************************
'RUN OUR COMMANDS HERE
'************************************************************

if verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Begin function to elevete SU.")

If Result <> "ERROR" Then Result=ElevateSU(supwd)
if verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Exited function to elevete SU.")

If Result <> "ERROR" Then
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Result is not set to ERROR.")
'Get users with root ID
CommandResult=ExecuteCommand(command2)
If CommandResult <> "ERROR" Then parseRootUID(CommandResult)

'Get users and group memberships
CommandResult=ExecuteCommand(command3)
If CommandResult <> "ERROR" Then parseGroupMemberships(CommandResult)

'Get Sudoers contents using either 'strings' or 'find' commands
commandForSudoers = command6
CommandResult=ExecuteCommand(command5a)
If CommandResult <> "ERROR" Then
If (TestStringContainsSudoers(CommandResult)) = "TRUE" Then
commandForSudoers = command5
End If
End if
logFile.WriteLine("INFO: Sudoers Command to execute: & """ & commandForSudoers & """.")
CommandResult=ExecuteCommand(commandForSudoers)
If CommandResult <> "ERROR" Then parseSUDoers(CommandResult)

End If

'************************************************************
'CLOSE THE SSH SESSION AND DISCONNECT
'************************************************************
Result=""

If verboseLogging > 0 Then logFile.WriteLine("INFO: VERBOSE: Now attempting to close down the SSH session and disconnect.")

success = ssh.ChannelSendEof(channelNum)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": was unable to successfully shutdown the session.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If

success = ssh.ChannelSendClose(channelNum)
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": was unable to successfully shutdown the session.")
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
Result = "ERROR"
End If

' Disconnect
ssh.Disconnect
If Result <> "ERROR" Then logFile.WriteLine("INFO: The session appears to be shut down successfully.")
lineToWrite = "Server " & hostname & " was disconnected successfully if response is 0: " & ssh.IsConnected
logFile.WriteLine (lineToWrite)
wscript.echo lineToWrite
wscript.sleep(2000)
logFile.WriteLine ("INFO: " & hostname & ": Completed sleep interval.")
logFile.WriteLine ("INFO: " & hostname & ": End of server processing." & vbCRLF)
End If

Loop

'************************************************************
'FINALLY CLOSE OFF OUR LOG FILES
'************************************************************
logFile.WriteLine("INFO: Finished processing all servers in file and closing off files.")
logFile.Close
dataFile.Close
groupFile.Close
sudoersFile.close


'************************************************************
'************************************************************
'Here come the functions!!!
'************************************************************
'************************************************************

'************************************************************
'PARSE OUTPUT OF COMMAND: ROOT UID (ROOT UID IS ALWAYS 0)
'EXPORT TO CSV
'************************************************************

Function parseRootUID(Result)
logFile.WriteLine("INFO: " & hostname & ": Now parsing Root UID.")

splitString=Split(Result,vbLF)
For each x in splitString
If instr(x,LEFT(command2,20)) > 0 Then
' Do not parse lines containing the original command
logFile.WriteLine("WRN: " & hostname & ": Returned line is the command that was executed and will not be parsed.")
ElseIf RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,":") > 0 Then
if verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter "":"" found: " & x)
'Lets Trim the line
x=Trim(x)
'Lets replace : with ,
x=replace(x,":",",")
'Lets write it to a file
groupFile.WriteLine(hostname&","&x)
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If

Next

logFile.WriteLine("INFO: " & hostname & ": Finished parsing Root UID." & vbCRLF)
End Function


'************************************************************
'PARSE OUTPUT OF COMMAND: GROUP MEMBERSHIPS
'EXPORT TO CSV
'************************************************************

Function parseGroupMemberships(Result)
logFile.WriteLine("INFO: " & hostname & ": Now parsing Group Memberships.")

splitString=Split(Result,vbLF)
For each x in splitString
If instr(x,LEFT(command3,20)) > 0 Then
' Do not parse lines containing the original command
logFile.WriteLine("WRN: " & hostname & ": Returned line is the command that was executed and will not be parsed.")
ElseIf RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,":") > 1 Then
' Delimiter : was found so parsing line - this is common for Red Hat so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter "":"" found: " & x)
userComponent = trim(left(x,inStr(x,":")-1))
groupComponent = trim(right(x,len(x)-inStr(x,":")))
splitGroupComponent=Split(groupComponent," ")
groupFile.WriteLine(hostname&","&userComponent) ' Addition 7/12/2011
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
ElseIf instr(x," ") > 1 Then
'Space Delimiter was found with no colon - this is common for Solaris so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [SPACE] found: " & x)
userComponent = trim(left(x,inStr(x," ")-1))
groupComponent = trim(right(x,len(x)-inStr(x," ")))
splitGroupComponent=Split(groupComponent," ")
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
Next

logFile.WriteLine("INFO: " & hostname & ": Finished parsing Group Memberships." & vbCRLF)
End Function


'************************************************************
'PARSE OUTPUT OF COMMAND: SU DOERS (PERMISSIONS ASSIGNMENT)
'EXPORT TO CSV
'************************************************************

Function parseSUDoers(Result)
logFile.WriteLine("INFO: " & hostname & ": Now parsing SUDOERS file.")
specialDelimiter = " "

splitString=Split(Result,vbLF)
For each x in splitString
If instr(x,LEFT(commandForSudoers,20)) > 0 Then
' Do not parse lines containing the original command
logFile.WriteLine("WRN: " & hostname & ": Returned line is the command that was executed and will not be parsed.")
ElseIf RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,specialDelimiter) > 0 Then
' Special delimiter has been found - parsing with special delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter """ & specialDelimiter & """ found: " & x)
rewriteLine = Replace(Trim(x),specialDelimiter,",",1,1)
sudoersFile.WriteLine(hostname&","&rewriteLine)
ElseIf instr(x,vbtab) > 0 Then
' Tab delimiter has been found - parsing with tab delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [TAB] found: " & x)
rewriteLine = Replace(Trim(x),vbtab,",",1,1)
' userComponent = trim(left(x,inStr(x,vbtab)-1))
' roleComponent = trim(right(x,len(x)-inStr(x,vbtab)))
sudoersFile.WriteLine(hostname&","&rewriteLine)
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If
Next

logFile.WriteLine("INFO: " & hostname & ": Finished parsing SUDOERS file." & vbCRLF)
End Function


'************************************************************
'ELEVATE TO SUPER USER (SU)
'************************************************************

Function ElevateSU(supwd)

'wscript.echo "Now running within the elevate SU function."

' Send the 'sudo su -' command.
' (With Linux Ubuntu system running OpenSSH, it is important to send a bare-LF and not a CRLF.)

cmd = "sudo su -" & vbLf
success = ssh.ChannelSendString(channelNum,cmd,"ansi")
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while sending the string 'su')")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi")) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ElevateSU = "ERROR"
exit function
End If

' Write out the received text to the log file (this empties the received text buffer)
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))

'wscript.echo "Success code for running the sudo su command is: " & success
'wscript.echo " If ElevateSU is set to ERROR then there has been an error: " & ElevateSU

'wscript.echo "We will now check the channel for a line in the received text containing *password*"

' Read until we get the prompt for the password
success = ssh.ChannelReceiveUntilMatch(channelNum,"*password*","ansi",0) 'Set to 0 for case-insensitive
'wscript.echo "Success code for finding *password* was: " & success
' Handle error if a password prompt was not received and check whether it has automatically elevated the prompt w/o asking for password
If (success <> 1) Then

sshReturnedText = ssh.GetReceivedText(channelNum,"ansi")

' If # was returned then the prompt has elevated automatically
if instr(sshReturnedText,"#") > 0 Then
logFile.WriteLine("INFO: Prompt appears to have automatically elevated. Received #. No need to send the password")
logFile.WriteLine("INFO: Following text will provide information regarding an attempt to elevate the prompt. However it does not necessarily mean there is an issue.")
logFile.WriteLine(ssh.LastErrorText)
exit function

' For all other returned prompts, it was probably not elevated so we should not continue processing this server.
else
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while waiting for prompt for password) - this server will not be processed")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(sshReturnedText) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ElevateSU = "ERROR"
exit function
end if
End If

' Display what we've received so far:
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))

' Send the password. Again, make sure it uses a bare-LF and not a CRLF.
password = supwd & vbLf
success = ssh.ChannelSendString(channelNum,password,"ansi")
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while trying to send the password)")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ElevateSU = "ERROR"
exit function
End If

' Read the response until we get the shell prompt (assuming it's successful)
' In my case, the shell prompt is: "root@ubuntu:/home/chilkat# "
' It will be different in your case.
success = ssh.ChannelReceiveUntilMatch(channelNum,"*#*","ansi",0) 'Set to 0 for case-insensitive
If (success <> 1) Then
' Check the last-error information and the session log...
logFile.WriteLine("ERROR: " & hostname & ": Elevating Privileges (while waiting for the shell prompt following running 'su') - this can indicate an incorrect password")
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi")) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
' Check to see what was received.
ElevateSU = "ERROR"
exit function
End If

' Display what we've received so far. This clears the internal receive buffer, which is important.
' After we send the command, we'll be reading until the next command prompt. If the command prompt
' is already in the internal receive buffer, we'll think we're already finished...
logFile.WriteLine("INFO: No errors appear to have been encountered running SU")
logFile.WriteLine("INFO: Now writing ssh connection info...")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi") & vbCRLF)
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
logFile.WriteLine("INFO: Completed writing ssh connection info...")
End Function


'************************************************************
'EXECUTE COMMAND
'************************************************************

Function ExecuteCommand(mycommand)
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Now entering the command function.")
' Send the command.
cmd = mycommand & vbLf
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Now sending command: " & cmd)
success = ssh.ChannelSendString(channelNum,cmd,"ansi")
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Finished running command.")
If (success <> 1) Then
logFile.WriteLine("ERROR: " & hostname & ": was unable to run the command: "&mycommand)
logFile.WriteLine("INFO: Error on or after the following prompts:")
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi")) ' error testing
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ExecuteCommand = "ERROR"
exit function
End If

If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: About to read channel for output.")

' Start reading the data.
' If it pauses for more than 6000ms then we can assume there is no more output.
' Initial timeout is still controlled by IdleTimeoutMs
timeoutOnceReceivingData = 6000
success = ssh.ChannelReadAndPoll(channelNum,timeoutOnceReceivingData)
If (success < 0) Then
if (success = -1) Then logFile.WriteLine("ERROR: " & hostname & ": was unable to process the command: "&mycommand)
if (success = -1) Then logFile.WriteLine("INFO: This may have been caused by a server disconnect or other reason.")
if (success = -2) Then logFile.WriteLine("ERROR: " & hostname & ": The following command was processed and no data was returned within the configured timeout: "&mycommand)
logFile.WriteLine(ssh.GetReceivedText(channelNum,"ansi"))
logFile.WriteLine(ssh.LastErrorText)
logFile.WriteLine(ssh.SessionLog)
ExecuteCommand = "ERROR"
exit function
end if
logFile.WriteLine("INFO: we appear to have run this command successfully: "&mycommand)

' Write the data to log files:
storeCmdOutput=(ssh.GetReceivedText(channelNum,"ansi"))
ExecuteCommand = storeCmdOutput
dataFile.WriteLine(storeCmdOutput)
logFile.WriteLine(storeCmdOutput)

End Function

'************************************************************
'Check if a string contains a valid sudoers file
'************************************************************
Function TestStringContainsSudoers(teststring)

TestStringContainsSudoers = "TRUE"

splitstring=split(teststring,vbLF)
teststring=trim(splitstring(1))
'wscript.echo "This is the test string: " & teststring

'Does trimmed string begin with '/'
If (Left(teststring,1) <> "/") Then TestStringContainsSudoers = "FALSE"
If (instr(teststring,"/sudoers")) <1 Then TestStringContainsSudoers = "FALSE"

If TestStringContainsSudoers = "TRUE" Then logFile.WriteLine("INFO: SUDOERS file has been detected in " & teststring & ".")
If TestStringContainsSudoers = "FALSE" Then logFile.WriteLine("INFO: SUDoers file has not been detected in " & teststring & ".")

End Function

'************************************************************
'PARSE A GIVEN CSV FILE INTO ARGUMENTS
'************************************************************

Function CSVParser (CSVDataToProcess)

'Declaring variables for text delimiter and text qualifyer
Dim TextDelimiter, TextQualifyer

'Declaring the variables used in determining action to be taken
Dim ProcessQualifyer, NewRecordCreate

'Declaring variables dealing with input string
Dim CharMaxNumber, CharLocation, CharCurrentVal, CharCounter, CharStorage

'Declaring variables that handle array duties
Dim CSVArray(), CSVArrayCount

'-------------------

'Setting default values for various variables

'<- Text delimiter is a comma
TextDelimiter = ","

'<- Chr(34) is the ascii code for "
TextQualifyer = Chr(34)

'<- Determining how record should be processed
ProcessQualifyer = False

'<- Calculating no. of characters in variable
CharMaxNumber = Len(CSVDataToProcess)

'<- Determining how to handle record at different
' stages of operation
' 0 = Don't create new record
' 1 = Write data to existing record
' 2 = Close record and open new one
NewRecordCreate = 0

'<- Priming the array counter
CSVArrayCount = 0

'<- Initializing the array
Redim Preserve CSVArray(CSVArrayCount)

'<- Record character counter
CharCounter = 0

'-------------------
'Starting the main loop

For CharLocation = 1 to CharMaxNumber

'Retrieving the next character in sequence from CSVDataToProcess
CharCurrentVal = Mid(CSVDataToProcess, CharLocation, 1)

'This will figure out if the record uses a text qualifyer or not
If CharCurrentVal = TextQualifyer And CharCounter = 0 Then
ProcessQualifyer = True
CharCurrentVal = ""
End If

'Advancing the record 'letter count' counter
CharCounter = CharCounter + 1


'Choosing data extraction method (text qualifyer or no text qualifyer)
If ProcessQualifyer = True Then

'This section handles records with a text qualifyer and text delimiter
'It is also handles the special case scenario, where the qualifyer is
'part of the data. In the CSV file, a double quote represents a single
'one ie. "" = "
If Len(CharStorage) <> 0 Then
If CharCurrentVal = TextDelimiter Then
CharStorage = ""
ProcessQualifyer = False
NewRecordCreate = 2
Else
CharStorage = ""
NewRecordCreate = 1
End If
Else
If CharCurrentVal = TextQualifyer Then
CharStorage = CharStorage & CharCurrentVal
NewRecordCreate = 0
Else
NewRecordCreate = 1
End If
End If

'This section handles a regular CSV record.. without the text qualifyer
Else
If CharCurrentVal = TextDelimiter Then
NewRecordCreate = 2
Else
NewRecordCreate = 1
End If

End If

'Writing the data to the array
Select Case NewRecordCreate

'This section just writes the info to the array
Case 1
CSVArray(CSVArrayCount) = CSVArray(CSVArrayCount) & CharCurrentVal

'This section closes the current record and creates a new one
Case 2
CharCounter = 0
CSVArrayCount = CSVArrayCount + 1
Redim Preserve CSVArray(CSVArrayCount)

End Select
Next
'------------------

'Finishing Up
CSVParser = CSVArray

End Function








==========================
And here is a script that can take a manual cut and paste of the commands in the above code, and convert to a CSV format:



Dim xmlDoc, objNodeList, plot, nodeBook


' =======================================================
' Set up files to be written and read
' =======================================================

Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

Dim fso, logFile, dataFile, x, y

Set fso = CreateObject("Scripting.FileSystemObject")
Set dataFile = fso.CreateTextFile("Windows_Groups.txt", True) 'Output information from commands
Set logFile = fso.CreateTextFile("SessionLog.txt", True) 'Verbose Logging
Set groupFile = fso.CreateTextFile("GroupMemberships.txt", True) 'User group memberships
Set sudoersFile = fso.CreateTextFile("SUDOERS.txt", True) 'Sudoers output


If Err.Number <> 0 Then
wscript.echo "CRASH: Cannot open file for reading. " & Err.Number & " " & Err.Description
wscript.quit
End If

wscript.echo "Please have text files in the specified directory that are named [servername].TXT. Put a line at the beginning of the SUDOERS section" &_
" containing 'SUDOERSSECTION' which will be used to determine when to start processing as SUDOERS"

' =======================================================
' Browse for folder containing XML files
' =======================================================


Const WINDOW_HANDLE = 0
Const BIF_EDITBOX = &H10
Const BIF_NONEWFOLDER = &H0200
Const BIF_RETURNONLYFSDIRS = &H1

Set objShell = CreateObject("Shell.Application")
Set wshShell = CreateObject("WScript.Shell")

'**Browse For Folder To Be Processed
strPrompt = "Please select the folder to process."
intOptions = BIF_RETURNONLYFSDIRS + BIF_NONEWFOLDER + BIF_EDITBOX
strTargetPath = "C:\"
strFolderPath = Browse4Folder(strPrompt, intOptions, strTargetPath)

Set objFolder = fso.GetFolder(strFolderPath)
Set objColFiles = objFolder.Files

For Each file In objColFiles
' Check if we are looking at an XML file
If UCASE(RIGHT(file.Name,3)) <> "TXT" Then
logFile.WriteLine("WRN: Filename " & file.Name & " does not appear to be a TXT file.")
Else
'perform the work
Set serverFile = fso.OpenTextFile(file, ForReading) 'Input information about servers

' =======================================================
' Browse each TXT file for groups and memberships
' =======================================================

serverSource=file.Name
fullServerSource=objFolder&"\"&serverSource
msgbox "Processing " & file.Name
' msgbox fullServerSource
hostname=LEFT(serverSource,INSTR(serverSource,".")-1)

logFile.WriteLine("INFO: Now processing server: " & serverSource)
reachedSudoers = 0
Do Until serverFile.AtEndOfStream
' Parse servers.txt in CSV format and read the arguments
arrFileLine = serverFile.ReadLine
If INSTR(arrFileLine,"SUDOERSSECTION") Then reachedSudoers=1

if reachedSudoers = 0 Then
parseGroupMemberships(arrFileLine)
else
parseSudoers(arrFileLine)
End If

Loop
serverFile.close
End If
Next






'************************************************************
'PARSE OUTPUT OF COMMAND: GROUP MEMBERSHIPS
'EXPORT TO CSV
'************************************************************

Function parseGroupMemberships(x)
logFile.WriteLine("INFO: " & hostname & ": Now parsing Group Membership")

If RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,":") > 1 Then
' Delimiter : was found so parsing line - this is common for Red Hat so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter "":"" found: " & x)
userComponent = trim(left(x,inStr(x,":")-1))
groupComponent = trim(right(x,len(x)-inStr(x,":")))
splitGroupComponent=Split(groupComponent," ")
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
ElseIf instr(x," ") > 1 Then
'Space Delimiter was found with no colon - this is common for Solaris so parsing line
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [SPACE] found: " & x)
userComponent = trim(left(x,inStr(x," ")-1))
groupComponent = trim(right(x,len(x)-inStr(x," ")))
splitGroupComponent=Split(groupComponent," ")
For each v in splitGroupComponent
groupFile.WriteLine(hostname&","&userComponent&","&v)
Next
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If

logFile.WriteLine("INFO: " & hostname & ": Finished parsing Group Memberships." & vbCRLF)
End Function


'************************************************************
'PARSE OUTPUT OF COMMAND: SU DOERS (PERMISSIONS ASSIGNMENT)
'EXPORT TO CSV
'************************************************************

Function parseSUDoers(x)
logFile.WriteLine("INFO: " & hostname & ": Now parsing SUDOERS file.")
specialDelimiter = " "

If RIGHT(x,2) = "# " Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf INSTR(x,":/root") > 0 Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf LEFT(x,1) = "#" Then
' Do not parse the root prompt
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" was not parsed as it is the root prompt.")
ElseIf instr(x,specialDelimiter) > 0 Then
' Special delimiter has been found - parsing with special delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter """ & specialDelimiter & """ found: " & x)
rewriteLine = Replace(Trim(x),specialDelimiter,",",1,1)
sudoersFile.WriteLine(hostname&","&rewriteLine)
ElseIf instr(x,vbtab) > 0 Then
' Tab delimiter has been found - parsing with tab delimiter.
If verboseLogging > 1 Then logFile.WriteLine("INFO: VERBOSE: Delimiter [TAB] found: " & x)
rewriteLine = Replace(Trim(x),vbtab,",",1,1)
' userComponent = trim(left(x,inStr(x,vbtab)-1))
' roleComponent = trim(right(x,len(x)-inStr(x,vbtab)))
sudoersFile.WriteLine(hostname&","&rewriteLine)
Else
' No delimiter found - not parsing line.
logFile.WriteLine("WRN: " & hostname & ": The line " & chr(34) & x & chr(34) &_
" could not be parsed as no delimiter was found.")
End If

logFile.WriteLine("INFO: " & hostname & ": Finished parsing SUDOERS file." & vbCRLF)
End Function








' =======================================================
' Function for browsing for folders
' =======================================================


'**Browse4Folder Function
Function Browse4Folder(strPrompt, intOptions, strRoot)
Dim objFolder, objFolderItem

On Error Resume Next

Set objFolder = objShell.BrowseForFolder(0, strPrompt, intOptions, strRoot)
If (objFolder Is Nothing) Then
Wscript.Quit
End If
Set objFolderItem = objFolder.Self
Browse4Folder = objFolderItem.Path
Set objFolderItem = Nothing
Set objFolder = Nothing
End Function

VBScript: Extract members of local Administrators group from SYDI-Server output

Sydi-Server is a great VBS tool that can extract local group memberships into an XML output. Unfortunately this is not always the greatest format to work with.

This VBScript can be pointed to a directory containing the SYDI-Server XML files and it will extract all members of the local Administrators groups into a CSV format.

Here is the script:


Dim xmlDoc, objNodeList, plot, nodeBook
Set objRegEx = CreateObject("VBScript.RegExp")



' =======================================================
' Set up files to be written and read
' =======================================================

Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

Dim fso, logFile, dataFile, x, y

Set fso = CreateObject("Scripting.FileSystemObject")
Set dataFile = fso.CreateTextFile("Windows_Groups.txt", True) 'Output information from commands
Set logFile = fso.CreateTextFile("SessionLog.txt", True) 'Verbose Logging

If Err.Number <> 0 Then
wscript.echo "CRASH: Cannot open file for reading: Servers.txt. " & Err.Number & " " & Err.Description
wscript.quit
End If

' =======================================================
' Browse for folder containing XML files
' =======================================================


Const WINDOW_HANDLE = 0
Const BIF_EDITBOX = &H10
Const BIF_NONEWFOLDER = &H0200
Const BIF_RETURNONLYFSDIRS = &H1

Set objShell = CreateObject("Shell.Application")
Set wshShell = CreateObject("WScript.Shell")

'**Browse For Folder To Be Processed
strPrompt = "Please select the folder to process."
intOptions = BIF_RETURNONLYFSDIRS + BIF_NONEWFOLDER + BIF_EDITBOX
strTargetPath = "C:\"
strFolderPath = Browse4Folder(strPrompt, intOptions, strTargetPath)

Set objFolder = fso.GetFolder(strFolderPath)
Set objColFiles = objFolder.Files

For Each file In objColFiles
' Check if we are looking at an XML file
If UCASE(RIGHT(file.Name,3)) <> "XML" Then
logFile.WriteLine("WRN: Filename " & file.Name & " does not appear to be an XML file.")
Else
'perform the work

' =======================================================
' Browse each XML file for groups and memberships
' =======================================================

serverXMLSource=file.Name
fullServerXMLSource=objFolder&"\"&serverXMLSource
' msgbox fullServerXMLSource
serverName=LEFT(serverXMLSource,INSTR(serverXMLSource,".")-1)

logFile.WriteLine("INFO: Now processing server: " & serverXMLSource)

Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.async=false
'xmlDoc.validateOnParse = false
xmlDoc.load(fullServerXMLSource)

if (xmlDoc.parseError.errorCode <> 0) Then
Dim myErr
Set myErr = xmlDoc.parseError
logFile.WriteLine("ERR: " & myErr.reason & ", " & myErr.srcText & ", " & myErr.line & ".")
'Msgbox ("Error: " & myErr.reason)
'Msgbox ("Error text: " & myErr.srcText)
'Msgbox ("Error line: " & myErr.line)
Else

' msgbox xmlDoc.xml
' Set elemList = xmlDoc.DocumentElement
' Set nodeList = elemList.SelectNodes("localgroups")
' for each item in nodeList
' msgbox "This is: " & item.xml
' Next

Set objChildNodes = xmlDoc.documentElement.childNodes
i = 0
childNodeNumber=0
For EAch strNode in objChildnodes
If strNode.NodeName = "localgroups" Then
childNodeNumber = i
'msgbox "Node : " & strNode.xml
End if
i=i+1
Next

If childNodeNumber = 0 Then
logFile.WriteLine("WRN: " & serverName & ": Unable to find a 'localgroups' element in " & serverXMLSource & ".")
End If
Set objLowerChildNodes = xmlDoc.DocumentElement.childNodes.item(childNodeNumber).ChildNodes
j = 0

For each strNode in objLowerChildNodes
If strNode.NodeName = "group" Then
groupName = objLowerChildNodes.item(j).GetAttribute("name")
if isNull(groupname) Then
'No point looking for members as we cannot even find a name of a group
logFile.WriteLine("WRN: " & serverName &_
": Unable to find a name tag for the returned group")
Else
'We can look for the next childnodes to find the members
'We will echo the group name to the output so that it appears even if empty.
if GroupName <> "Administrators" Then
dataFile.WriteLine(serverName & ",," & GroupName & ",No")
End If
Set objEvenLowerChildNodes = xmlDoc.DocumentElement.childNodes.item(childNodeNumber).ChildNodes.item(j).ChildNodes
k = 0
For each strLowerNode in objEvenLowerChildNodes
If strLowerNode.Nodename = "member" Then
memberName = objEvenLowerChildNodes.item(k).GetAttribute("name")
if isNull(memberName) Then
'Unable to find the name of a member
logFile.WriteLine("WRN: " & serverName &_
": Unable to find a name tag for the returned group member")
else
'We have the name of the group member
' ===========================================
' This is where we do our data manipulation
' And write it out to a file in the format:
' servername, membername, groupname, hpa, accountcode
' ============================================


' If HPA then test for Account Code
If GroupName = "Administrators" Then
hpa = "Yes"

' Allow to search for 9 consecutive digits
objRegEx.Global = false
objRegEx.Pattern = "\d{9}"
Set checkMatches = objRegEx.Execute(memberName)

'if checkMatches.Count> 0 Then
' For Each banana in checkMatches
' wscript.echo checkMatches.Count & " " & banana.Value
' Next
'End if

' - Test for JPicard or GISAdmin or 'break fix' accounts(5)
if INSTR(UCASE(memberName),"JPICARD") > 0 Then
accountCode = "5"
Elseif INSTR(UCASE(memberName),"GISADMIN") > 0 Then
accountCode = "5"
' Test for shared accounts (2)
Elseif INSTR(UCASE(memberName),"ADMIN") > 0 Then
accountCode = "2"
Elseif INSTR(UCASE(memberName),"\SSO") > 0 Then
accountCode = "2"
Elseif INSTR(UCASE(memberName),"\REDPLANET") > 0 Then
accountCode = "2"
' Test for adm accounts (?)
Elseif INSTR(UCASE(memberName),"\ADM-") > 0 Then
accountCode = "1"
' Test for \[1...9][0...9] (1)
Elseif checkMatches.Count > 0 Then
accountCode = "1"
' Test for \CEF_ \CMF_ _G_ Domain Admins and other groups (G)
Elseif INSTR(UCASE(memberName),"\CEF") > 0 Then
accountCode = "G"
Elseif INSTR(UCASE(memberName),"\CMF_") > 0 Then
accountCode = "G"
Elseif INSTR(UCASE(memberName),"_G_") > 0 Then
accountCode = "G"
Elseif INSTR(UCASE(memberName),"DOMAIN ADMINS") > 0 Then
accountCode = "G"
Elseif INSTR(UCASE(memberName),"\HP-OVE-GROUP") > 0 Then
accountCode = "G"
' Test for RRabling and personal SAM accounts (4)
Elseif INSTR(UCASE(memberName),"RRABLING") > 0 Then
accountCode = "1"
' Test for \IUSR ftpuser perfmon taskschedule and other service/app accounts(6)
Elseif INSTR(UCASE(memberName),"\IUSR") > 0 Then
accountCode = "6"
Elseif INSTR(UCASE(memberName),"FTPUSER") > 0 Then
accountCode = "6"
Elseif INSTR(UCASE(memberName),"PERFMON") > 0 Then
accountCode = "6"
Elseif INSTR(UCASE(memberName),"TASKSCHEDULE") > 0 Then
accountCode = "6"
Else
accountCode = "3"
End If

Else
hpa = "No"
End If

dataFile.WriteLine(serverName & "," & memberName & "," & GroupName & "," & hpa & "," & accountCode)

end if
else
' It was not an element named 'member' that was returned
logFile.WriteLine("WRN: " & serverName &_
": An element named 'member' should have been returned however the element" &_
"returned was: " & memberName)

end if

k = k + 1
Next
End If

Else
'It was not a element named 'group' that was returned
logFile.WriteLine("WRN: " & serverName &_
": An element named 'group' should have been returned hpwever the element" &_
"returned was: " & groupName)
End If


j = j + 1
Next






end if









End If
Next











' =======================================================
' Function for browsing for folders
' =======================================================


'**Browse4Folder Function
Function Browse4Folder(strPrompt, intOptions, strRoot)
Dim objFolder, objFolderItem

On Error Resume Next

Set objFolder = objShell.BrowseForFolder(0, strPrompt, intOptions, strRoot)
If (objFolder Is Nothing) Then
Wscript.Quit
End If
Set objFolderItem = objFolder.Self
Browse4Folder = objFolderItem.Path
Set objFolderItem = Nothing
Set objFolder = Nothing
End Function

Powershell: Write out local Administrators group membership to CSV

This powershell script will query a CSV containing a single list of servers to determine the membership of the local Administrators group. This script could be used to assist in determining HPA (Highly Privileged Access) accounts in a company.

While SYDI-Server can perform this task, some servers do not allow remote WMI connections. This script uses the same remote management interface as Computer Management to connect to the server. For this reason it could be used in conjunction with SYDI-Server to hit both WMI and non-WMI servers.

If your logged in account does not have access to the server being queried then you can try to net use \\server\IPC$ user:server\username * prior to running the command. The command should then use these impersonated credentials to connect.

This script only outputs the direct members of the local Administrators group. A second script is on this site which can be executed following this script to expand out the AD group memberships as well.

Here is the script.

cls
$csvfile = Import-Csv BadServer.csv
'"Server","Username","Type"' | Out-File -filepath "Users.csv"
foreach($line in $csvfile)
{
$strcomputer = $line.server
$computer = [ADSI]("WinNT://" + $strcomputer + ",computer")

#uncomment the following lines if you need to get all groups from the computer

# $computer.psbase.children | Where-Object{ $_.psbase.schemaClassName -eq 'group'} | foreach {
# '"' +$strcomputer + '","' + $_.name + '"' | Out-File -filepath "Groups.csv" -append
# #Write-Host $_.name
# }
$group = $computer.psbase.children.find("Administrators")
$members = $group.psbase.invoke("Members")
foreach( $member in $members)
{
#Write-Host $member.GetType().GetFields()
$compname = $member.GetType().InvokeMember("AdsPath", 'GetProperty', $null,$member, $null)
$strname = $compname.Remove(0,8).Replace("/","\")
'"' +$strcomputer + '","' + $strname+ '","' + $member.GetType().InvokeMember("Class", 'GetProperty', $null,$member, $null) + '"' | Out-File -filepath "Users.csv" -append
}

$members = $null
$group = $null
$computer = $null
}

Write-Host "done"
start .