Verifying Patching with PowerShell (Part 1 Finding the Java Versions)

One of the greatest dilemmas that both the system admin as well as the the security professional face is knowing if a patch tool and even more I would say it would be having visibility of what is currently in their environment. Lets look at to approaches:

  1. We will use WMI to check the Java version of a series of host.
  2. We will use WMI to check if a specific Microsoft patch is present (Get-Hotfix was behaving strangely in a workflow for me).
For both cases we will save the data in to a CSV file for simplicity of parsing the data. We will be pulling the hosts to check directly from AD using the AD cmdlets that are installed as part of the RSAT (Remote Server Administration Tools) that can be installed on a Windows 7 or Windows 8 hosts or we could as well run this from a Windows 2008 R2 or Windows 2012 server that have the ActiveDirectory module installed. Lets start by pulling the Computers we want from the domain. If the host we are on is a Windows 7 or Windows 8 hosts we need to know if the RSAT component is installed and if so if the AD PowerShell Module is available in the features, I will cover the commands as if we where on command shell since this could be useful if you are a pentester that just gained access to hosts that have RSAT tools Winking smile (used this last week and it worked beautifully) To check if the AD PowerShell component is installed we can use the DSIM command:
C:\Windows\system32>dism /online /Get-features | findstr /i powershell

Feature Name : RemoteServerAdministrationTools-Roles-AD-Powershell

As we can the the feature is available to use. To install we can use the DSIM tool itself:

dism /online /enable-feature /featurename:RemoteServerAdministrationTools-Roles-AD-Powershell

In the case of a Windows 2008 R2 Server or a Windows 2012 server we would import the Server Manager Module:

PS > Import-Module ServerManager 

Then we would query for the feature and install it:

PS > Get-WindowsFeature RSAT-AD-PowerShell | Add-WindowsFeature 

Once the tool is installed we can load the module and execute the PowerShell cmdlets to query AD, in the case of PowerShell v3 modules are automatically loaded and they do not need no be loaded by hand. To import we would use:

PS > Import-Module ActiveDirectory 

Note: if you are in a pentest and have shell on a host that have the RSAT tools you an invoke your powershell commands against AD either from a domain account or from system since both the computer account and domain user accounts have read permissions on AD allowing for enumeration of the domain.

For our specific need we will use the Get-ADComputer cmdlet, this cmdlet allows the pulling of computer information where we can filter by either property of the computer or by using an LDAP filter. To get a list of all the properties we could filter on we can just use the Get-Member cmdlet and have it only show properties:

PS > Get-ADComputer -Filter * -Properties * | Get-Member -MemberType Properties 

In this case so as to keep things simple I recommend  you filter either by OS type or by OU. Here is an example where we will filter for only those machines that are running client OS and we retrieve their Name, OS and IPv4Address:

PS C:\> Get-ADComputer -Filter "OperatingSystem -notlike '*server*'" `

-Properties Name,OperatingSystem,ipv4address `

| Select-Object Name,OperatingSystem,ipv4address
Name OperatingSystem ipv4address
---- --------------- -----------
WIN701 Windows 7 Enterprise 192.168.10.20
WINXP01 Windows XP Professional 192.168.10.30
WINXP02 Windows XP Professional 192.168.10.31
WIN702 Windows 7 Ultimate 192.168.10.21
WIN801 Windows 8 Enterprise 192.168.10.40

If we want to limit our search we use the –SearchBase parameter:

PS C:\>  Get-ADComputer -Filter "OperatingSystem -notlike '*server*'" `

-Properties Name,OperatingSystem,ipv4address -SearchBase "OU=HR,DC=acmelabs,DC=com" `

| Select-Object Name,OperatingSystem,ipv4address

Name                                         OperatingSystem                              ipv4address                               
----                                         ---------------                              -----------                               
WIN702                                       Windows 7 Ultimate                           192.168.10.21                             
WIN701                                       Windows 7 Enterprise                         192.168.10.20                             

Now that we are able to list the PC’s that we want to check against, lets look in the case of third party tool like Java I like checking the version of the file that application uses since it is simpler that querying the registry with WMI.

Since a user can install both a 32bit and a 64bit version of java we will have to look in the 2 default locations. The WMI Class to use for this is the CIM_Datafile  this class will allow me to query a specific file and get it’s properties.

Note: You may see examples in the internet use Win32_Product, this class should be avoided for enumerating installed software. The Win32_Product class works by enumerating every MSI package that is installed on the system. When a package is touched, it performs a reconfiguration where the application is validated (and repaired if found to be inconsistent with the original MSI). So configuration settings may go to default and services that may have been disabled will be re-enabled, this may expose the systems to greater risk or break the software in some configurations.

For querying the CIM_Datafile class we will use Get-WMIObject since it is available in both PowerShell v2 and PowerShell v3. The use of WMI also allow us to provide alternate credentials if we wish.

Lets query a system with both 32bit and 64bit versions in the default locations and get the version for Java.dll:

PS C:\> Get-WmiObject -Class CIM_Datafile -Filter `

'(Name="C:\\Program Files\\Java\\jre7\\bin\\java.dll" OR Name="C:\\Program Files (x86)\\Java\\jre7\\bin\\java.dll")'
Compressed : False
Encrypted : False
Size :
Hidden : False
Name : c:\program files (x86)\java\jre7\bin\java.dll
Readable : True
System : False
Version : 7.0.100.18
Writeable : True

Compressed : False
Encrypted : False
Size :
Hidden : False
Name : c:\program files\java\jre7\bin\java.dll
Readable : True
System : False
Version : 7.0.100.18
Writeable : True

As we can see we are able to pull both versions of both files.

Since WMI takes a while to run PowerShell has several ways to speed up the process.  The most compatible one is to use Jobs, these will spin up a separate powershell.exe environment and execute the command and save the results in memory for us to pull and parse, the other is to use threads and pools and new to PowerShell v3 we can create what is called a Workflow. We will use this code to create a Workflow since it is less resource intensive that Jobs and easier to write that threads in a pool. A workflow is kind of a special function that allows one to run tasks in parallel up to 5 threads at a time so it will make it easier to pull data. Here is the Workflow I came up with:


Workflow Get-Java7Version {

[cmdletbinding()]

param(

[psobject[]]$Computers

)

foreach -parallel ($computer in $computers) {

Write-Verbose -Message "Running against $($computer.Name)"

# Check each computer for the info.

$versions = Get-WmiObject -Class CIM_Datafile `

-Filter '(Name="C:\\Program Files\\Java\\jre7\\bin\\java.dll" OR Name="C:\\Program Files (x86)\\Java\\jre7\\bin\\java.dll")' `

-PSComputerName $computer.ipv4address -ErrorAction SilentlyContinue | Select-Object -Property name,version

if ($versions){

# Process each version found

Write-Verbose -Message "Java found on $($computer.Name)"

foreach($version in $versions){

# Create a Custom PSObject with the info to process

$found = InlineScript {

New-Object –TypeName PSObject –Prop @{'Computer'=$Using:computer.Name;

'IPv4Address'=$Using:computer.ipv4address;

'File'=$Using:version.name;

'Version'=$Using:version.version}

}

# Return the custom object

$found

}

}

else {

Write-Verbose -Message "Java not found in $($computer.Name)"

}

}

}

The workflow breaks down as follows:


  • We add to our Workflow the cmdletbinding() this will allow us to inherit certain functionality found on cmdlets like being able to show verbose and debug output.
  • We define as a parameter for the workflow Computers which is a array of PSObjects. This will be objects that can be taken from the Get-ADComputer or any other cmdlet that produces objects with the properties of IPv4Address and Name for the computers we want to run against.
  • We now go thru each computer objects in $Computers and run the WMI query against each, the –parallel parameter in foreach can only be used inside of workflows. This will create a pool of jobs and run in parallel 5 at a time.
  • For each version we get we create a custom object with the computer name, IP Address, File Location and the Version of the file. We return the Object.

As you can see it is not to complicated other than some special rules for Workflows like the no use of positional parameters and the instantiation of objects must be done inside a inlinescript script block.

You can read more about Workflows in the “Getting Started with PowerShell Workflows” document from Microsoft  or using the Get-Help cmdlet and looking at about_Workflows and about_Workflow_Common_Parameters.

The workflow can be ran just like any other function. You can save the code in to a PS1 file and just do a `. .\<filename>.ps1` and it will add the function inside of it in to your session.

We will save the computers found with the Get-ADComputer cmdlet in to a variable:

 PS C:\> $computers = Get-ADComputer -Filter * `

-Properties Name,OperatingSystem,ipv4address `

| Select-Object Name,ipv4address

Now we can pass these to the workflow and use the –verbose option to see the progress:

image

Since we are returning objects we can manipulate the data using several formating and export cmdlets from PowerShell. One out my favorites is the Grid View cmdlet so I can filter and parse the data.

PS C:\> Get-Java7Version -Computers $computers -Verbose | select Computer,IPv4Address,Version,File | Out-GridView 

 

This will give us:

image

Now we can parse and filter the data anyway we want. For part 2 I will cover how to check the MS Hotfixes and also how to get some diagnostic data to determine if a hotfix failed and why.  As always I hope you found the information useful.