PowerShell Basics–Objects and the Pipeline

By now you would have noticed if you have been reading my other posts where I use PowerShell that it is not your typical Shell and that it behaves in a unique way when it comes to the output generated by the commands we issue in it, this is because PowerShell is an Object based Shell, this means that everything is an object. Those that have programed in Perl, Ruby, Python, C# or any other Objects based language know very well the power of objects, for those that come from a Bash, cmd.exe or any other regular text based shell you will notice that in PowerShell is a lot simpler to parse and use data, typically on one of this shells we are searching for strings, extracting the strings of text that we need and then piping them to something else, this can be very labor intensive. Lets start by defining what an Object is simple terms, an object is Data and it has 2 types of components:

  • Properties – Information we can retrieve and/or set by name.
  • Method – something we can have happen to the data, it can take arguments to determine what to do or to provide additional information.
To better illustrate Objects and the advantages if offers versus a text based shell lets look at how it is used in PowerShell and why it is one of the foundations for it. Lets look at services. To get a list of services on the system we just do a Get-Service and we get the output, now lets dissect that output: image Man time PowerShell will show the output in a table format or in a list format, looking at a table format like the one above we can see each service is represented individually (as rows) and some information of each is shown (as columns). when we look at this each column represents a property and each row an individual object. But wait we know there has to be more to an service than this, right? well yes. PowerShell uses what is known as Format views for certain types of object so as to show what the creator of the cmdlet considers to be the most common pieces of information for use. To look at the Object be it from a command, cmdlet, function workflow ..etc the most common method is to pipe it to the Get-Member cmdlet to get a view
Get-Service | Get-Member

This will produce a list of:


  • Properties – Information about the object.
  • Methods – Things we can do to the objects.
  • ScriptMethods – Pieces of embedded code that will execute when called.
  • Events – Things that happen to an object we can subscribe to be notified when they happen.

It will also tell us what is the .Net Object Class it is being returned.

image

While using this you may noticed that for some outputs of commands you will see more than one Object Class, Get-Member is smart enough to only show the info for the unique classes that are returned. Lets look at the information we can get from a single object, for the example I will use the BITS service so as to not break my machine.  Lets start by saving it in to a variable so it is easier to manipulate. Variables in PowerShell are maded with a $ in the front just like in Perl .

$BITSSrv = Get-Service -Name BITS

Now lets start with the fun part, the methods. To see what we can do with the object we use the Get-Member cmdlet but specify that we want only the methods shown:

PS C:\> $BITSSrv | Get-Member -MemberType Method


TypeName: System.ServiceProcess.ServiceController

Name MemberType Definition
---- ---------- ----------
Close Method void Close()
Continue Method void Continue()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type reque...
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
ExecuteCommand Method void ExecuteCommand(int command)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
Pause Method void Pause()
Refresh Method void Refresh()
Start Method void Start(), void Start(string[] args)
Stop Method void Stop()
WaitForStatus Method void WaitForStatus(System.ServiceProcess.ServiceContro...

As we can see there are several actions we can take against the service object, we can start, stop, pause, refresh (The object is only the state of what it represents at the given time we retrieved it). Wehn we look at the definitions we can see it shows us that some methods like Pause and Refresh do not take any arguments and others like Start can be called in several ways in some without us giving it arguments in others it tells us the type of class the method will take. Some can be deduce quite easily others we have to look in the MSDN Website for the class information (ServiceController Class)  do the way output is formatted we may loose some of the info we may need. for this we can use one of the Format Cmdlets to make it wrap the line so we can have a better look.

PS C:\> $BITSSrv | Get-Member -MemberType Method | Format-Table -Wrap


TypeName: System.ServiceProcess.ServiceController

Name MemberType Definition
---- ---------- ----------
Close Method void Close()
Continue Method void Continue()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type
requestedType)
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
ExecuteCommand Method void ExecuteCommand(int command)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
Pause Method void Pause()
Refresh Method void Refresh()
Start Method void Start(), void Start(string[] args)
Stop Method void Stop()
WaitForStatus Method void WaitForStatus(System.ServiceProcess.ServiceControlle
rStatus desiredStatus), void WaitForStatus(System.Service
Process.ServiceControllerStatus desiredStatus, timespan
timeout)

Lets stop the service:

PS C:\> $BITSSrv.Stop()

PS C:\> $BITSSrv.Status
Running

PS C:\> $BITSSrv.Refresh()

PS C:\> $BITSSrv.Status
Stopped

As it can be seen I forgot that when the object is in a variable it is just a representation at the moment it was saved. Let look at Parenthetical execution go get around this, when we wrap the command around ( ) we can use the properties and methods of the object it returns directly. 

PS C:\> (Get-Service -Name BITS).Start()

PS C:\> (Get-Service -Name BITS).Status
Running

Now since we are poling the information on each command we get the latest information.  You can notice from the examples that Properties do not require ( ) at the end of invocation and Methods do, just something to keep in mind, in fact using tab completion or the ISE it will remind you.

Let look at the properties:

PS C:\> Get-Service -Name BITS | Get-Member -MemberType Property


TypeName: System.ServiceProcess.ServiceController

Name MemberType Definition
---- ---------- ----------
CanPauseAndContinue Property bool CanPauseAndContinue {get;}
CanShutdown Property bool CanShutdown {get;}
CanStop Property bool CanStop {get;}
Container Property System.ComponentModel.IContainer Container {get;}
DependentServices Property System.ServiceProcess.ServiceController[] DependentServices ...
DisplayName Property string DisplayName {get;set;}
MachineName Property string MachineName {get;set;}
ServiceHandle Property System.Runtime.InteropServices.SafeHandle ServiceHandle {get;}
ServiceName Property string ServiceName {get;set;}
ServicesDependedOn Property System.ServiceProcess.ServiceController[] ServicesDependedOn...
ServiceType Property System.ServiceProcess.ServiceType ServiceType {get;}
Site Property System.ComponentModel.ISite Site {get;set;}
Status Property System.ServiceProcess.ServiceControllerStatus Status {get;}

You will notice that some of the properties have in the end {get;set} or only {get;} this means we can assign a value to the property of the same type as it is listed in the definition. Since PowerShell will only list some of the properties depending on the formatting it has defined. Do take in to account this is only for the instance of the object, we would not be changing the service it self but the representation we may have in a variable. To change the service itself it would have to be thru a Method.

The formatting of what is displayed is shown following the guidelines shown in the format.pmxml file for each type in the Windows PowerShell folder, for the service it would be in C:\Windows\System32\WindowsPowerShell\v1.0 since the view is sometimes limited and we want to do a quick browse of all that is in the properties we can use the formatting cmdlets to exposed all, I tend to use the Format-List cmdlet:

PS C:\> Get-Service -Name BITS | Format-List -Property *


Name : BITS
RequiredServices : {RpcSs, EventSystem}
CanPauseAndContinue : False
CanShutdown : False
CanStop : True
DisplayName : Background Intelligent Transfer Service
DependentServices : {}
MachineName : .
ServiceName : BITS
ServicesDependedOn : {RpcSs, EventSystem}
ServiceHandle : SafeServiceHandle
Status : Running
ServiceType : Win32ShareProcess
Site :
Container :

PipeLine

By now you would have noticed in this blogpost and others that the pipeline is used quite a bit in PowerShell. The typical pipeline in other shells will move the standard out of a command to the standard input of another:

image

In the case of PowerShell we are moving objects so there are 2 ways a cmdlet can receive commands from the pipeline:


  1. By Value – this is where the output of a cmdlet must be the same type as what the –InputObject parameter of another takes:
  2. By Property Name – This is when the object has a property that matched the name of a parameter in the other.

So by Value would be:

image

And when we look at the parameters in help we can identify them by looking of they accept from the pipeline and how:

image

 

By Property it would be:

 

image

 

and when we look at the help information for the parameter it would look like:

image

The examples above are from the Service cmdlets so we could just pipe the Get-Service cmdlet any of the other cmdlets for managing services:

PS C:\> gcm *service* -Module  Microsoft.PowerShell.Management

CommandType Name ModuleName
----------- ---- ----------
Cmdlet Get-Service Microsoft.PowerShell.Man...
Cmdlet New-Service Microsoft.PowerShell.Man...
Cmdlet New-WebServiceProxy Microsoft.PowerShell.Man...
Cmdlet Restart-Service Microsoft.PowerShell.Man...
Cmdlet Resume-Service Microsoft.PowerShell.Man...
Cmdlet Set-Service Microsoft.PowerShell.Man...
Cmdlet Start-Service Microsoft.PowerShell.Man...
Cmdlet Stop-Service Microsoft.PowerShell.Man...
Cmdlet Suspend-Service Microsoft.PowerShell.Man...

So this would allow us to do:

PS C:\> Get-Service -Name BITS | Stop-Service

PS C:\> (Get-Service -Name BITS).Status
Stopped

PS C:\> Get-Service -Name BITS | Start-Service

PS C:\> (Get-Service -Name BITS).Status
Running


I hope that you found this blog post in the series useful and on the next one we will cover how to filter and process the Objects generated so we can glue even more types of cmdlets together.

Verifying Patching with PowerShell (Part 2 Microsoft Hotfixes)

In this second part we will look at querying for Microsoft Hotfixes against a given array of hosts. This Workflow will differ a bit as you will see from the one I showed in my previous post do to changes and improvements I was requested by a friend doing consulting at a client, he quickly modified the workflow in the previous post to use a list of IP Addresses and modified it to check for Internet Explorer versions so as to aid him in a risk assessment for a customer. On this one I took it a bit further and came with the following requirements:

  • Take an array of either computer names or IP addresses instead of an object.
  • Provide the ability of using alternate credentials for connecting to WMI on the remote hosts.
  • Test each given hosts to see if port 135 TCP is open.
  • Return an object with the patch information and a state of Installed or not Installed so as to be able to parse easier.

PowerShell provides 2 main ways to get patch information from a system:

  1. Get-Hotfix Commandlet
  2. Query to the Win32_QuickFixEngineering WMI class

In my testing Get-Hotfix when used inside of a workflow in PowerShell v3 tended to default to WinRM not allowing me to use DCOM as the method of communication. This became very annoying since not all customer environments will have WinRM running but are more than likely to allow DCOM RPC connection to their Windows Hosts. So I opted with the second option of using the Get-WMIObject to query the Class Win32_QuickFixEngineering . The command as the base for the workflow is rather simple:

Get-WmiObject -Class Win32_QuickFixEngineering -Filter "HotFixID='$($hid)'"

Where $hid holds the HotFix ID of the hotfix we want to test. With the Get-WMIObject cmdlet we can also give it the option of alternate credentials to use as well as a Computer Name to target.

The logic for the workflow is a simple one, we go thru each computer in the computers array in parallel and for each we check for the hotfix id

Workflow Confirm-Hotfix {

[cmdletbinding()]

param(

 

[parameter(Mandatory=$true)]

[psobject[]]$Computers,

 

[parameter(Mandatory=$true)]

[string[]]$KB

 

......

 

foreach -parallel ($computer in $computers) {

InlineScript {

foreach ($hid in $using:KB){

......

}

}

 

}

}

When using workflows there are several restrictions that make working with them very different than working say with a function specially when it comes to how to set parameters, use of variables and that positional parameters can not be use. Also the cmdlets that can be use are limited. This is why you will see me using the $using variable to pass variables to the script block that is the InlineScript. The use of InlineScript relaxes many of the rules that are imposed inside the workflow.

My Next task is to test if TCP Port 135 is open and set a timeout so as to be able to test quickly if the host is up or not before attempting to connect with WMI, the main advantage of this are:

  • Checks if the host is alive since most environments have firewalls on the PCs and servers that block ICMP Echo.
  • Checks that the proper port for WMI is open and if it is blocked and reset is send by a firewall.

Workflow Confirm-Hotfix {

[cmdletbinding()]

param(

 

[parameter(Mandatory=$true)]

[psobject[]]$Computers,

 

[parameter(Mandatory=$true)]

[string[]]$KB

 

......

 

foreach -parallel ($computer in $computers) {

InlineScript {

$TCPclient = new-Object system.Net.Sockets.TcpClient

$Connection = $TCPclient.BeginConnect($using:computer,135,$null,$null)

$TimeOut = $Connection.AsyncWaitHandle.WaitOne(3000,$false)

if(!$TimeOut) {

 

$TCPclient.Close()

Write-Verbose "Could not connect to $($using:computer) port 135."

 

}

else {

 

Try {

$TCPclient.EndConnect($Connection) | out-Null

$TCPclient.Close()

foreach ($hid in $using:KB){

......

}

 

}

 

Catch {

 

write-verbose "Connction to $($using:computer) on port 135 was refused."

}

}

 

}

}

The rest of the Workflow is just creating the object and passing the credentials. The final workflow looks like:

 

Workflow Confirm-HotFix {

[cmdletbinding()]

param(

 

[parameter(Mandatory=$true)]

[psobject[]]$Computers,

 

[parameter(Mandatory=$true)]

[string[]]$KB,

 

[System.Management.Automation.PSCredential] $Credentials

 

)

 

foreach -parallel ($computer in $computers) {

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

InlineScript {

# Move credentials in to the inline script for easier manipulation

$creds = $using:Credentials

# If none are provided create an empty PSCredential Object to force use of current user token.

if (!$creds){

$creds = ([PSCredential]::Empty)

}

$TCPclient = new-Object system.Net.Sockets.TcpClient

$Connection = $TCPclient.BeginConnect($using:computer,135,$null,$null)

$TimeOut = $Connection.AsyncWaitHandle.WaitOne(3000,$false)

if(!$TimeOut) {

 

$TCPclient.Close()

Write-Verbose "Could not connect to $($using:computer) port 135."

 

}

else {

 

Try {

$TCPclient.EndConnect($Connection) | out-Null

$TCPclient.Close()

 

# Check each computer for the info.

foreach ($hid in $using:KB){

Write-Verbose -Message "Checking for $($hid) on $($using:computer)"

$KBs = Get-WmiObject -class Win32_QuickFixEngineering -Filter "HotFixID='$($hid)'" -ComputerName $using:computer -Credential $creds

if ($KBs){

# Process each version found

Write-Verbose -Message "Hotfix $($hid) found on $($using:computer)"

$objprops =[ordered] @{'Computer'=$Using:computer;

'HotFix'=$hid;

'InstalledDate' = $KBs.InstalledOn;

'InstalledBy' = $KBs.InstalledBy;

'Description' = $KBs.Description;

'Caption' = $KBs.Caption;

'Installed'=$true}

[PSCustomObject]$objprops

 

}

else {

#If not found return an object with Installed False

Write-Verbose -Message "Hotfix $($hid) not found in $($using:computer)"

$objprops =[ordered] @{'Computer'=$Using:computer;

'HotFix'=$hid;

'InstalledDate' = "";

'InstalledBy' = "";

'Description' = "";

'Caption' = "";

'Installed'=$false}

[PSCustomObject]$objprops

}

}

}

 

Catch {

 

write-verbose "Connction to $($using:computer) on port 135 was refused."

}

}

}

}

}

 

Now we can use alternate credentials and have the workflow test for connectivity:

PS > Confirm-HotFix -Computers 192.168.10.20,192.168.10.1 -KB KB976902 -Credentials (Get-Credential acmelabs\administrator) -Verbose
   VERBOSE: [localhost]:Running against 192.168.10.1
   VERBOSE: [localhost]:Running against 192.168.10.20
   VERBOSE: [localhost]:Checking for KB976902 on 192.168.10.20
   VERBOSE: [localhost]:Could not connect to 192.168.10.1 port 135.
   VERBOSE: [localhost]:Hotfix KB976902 found on 192.168.10.20


   Computer              : 192.168.10.20
   HotFix                : KB976902
   InstalledDate         : 1/22/2013 12:00:00 AM
   InstalledBy           : NT AUTHORITY\SYSTEM
   Description           : Update
   Caption               : http://support.microsoft.com/?kbid=976902
   Installed             : True
   PSComputerName        : localhost
   PSSourceJobInstanceId : 3704d139-8328-4bd2-adcc-06bc994bf8b5

And since we are returning Objects and not text we can manipulate the results:

   PS C:\> $hosts = Get-ADComputer -Filter * | select -ExpandProperty name
   PS C:\> Confirm-HotFix -Computers $hosts -KB KB976902 | Format-Table -Property computer,hotfix,installed -AutoSize

   Computer HotFix   Installed
   -------- ------   ---------
   WIN801   KB976902     False
   WIN2K01  KB976902     False
   WINXP01  KB976902     False
   WIN2K302 KB976902     False
   DC02     KB976902      True
   WIN2K301 KB976902     False
   WINXP02  KB976902     False
   DC01     KB976902     False
   WIN702   KB976902      True
   WIN701   KB976902      True

This workflow is great for testing that the patch management solution deployed the patches and they applied, good for a quick risk assessment on Patch Tuesdays and confirming what a Vulnerability Scanner reports. I added the workflow to my PowerShell Security Module I keep meaning of one day finishing and documenting https://github.com/darkoperator/PowerShellSecMod/blob/master/PSSec/PSSec.psm1#L63

As always I hope you find this useful and informative, any feedback you may have are more than welcome Smile .

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.

New Guide for Installing Metasploit Framework CentOS and RHEL 6

I know that many hostting companies offer CentOS 6 as their OS of choice for VPS do to its great track record. So I decided to write and maintain a guide for getting a Development environment for Metasploit Framework or for those that prefer to use only the OSS versions of it in CentOS 6 the guide also covers RHEL 6 for both x86 and x64 versions. /msf-centosrhel/

Hope you find it useful.

PowerShell Basics–Running Commands

Lets start running commands and tying in what was covered in the previous posts. As you may have guessed by now in PowerShell the cmdlets, functions, workflows and scripts are all named in a - format. This aids in classifying the functionality of them and keeps to the idea of having one specific area of functionality per command similar to Unix. To get a list of all of the Verbs we use the Get-Verb cmdlet to get the list of approved verbs for PowerShell. When we write our own we can name them anything we like but it is recommended to follow the same standard as the one present in the shell so as to maintain uniformity and ease of use. The verbs are grouped per functionality in the following groups:

  • Common
  • Data
  • Lifecycle
  • Diagnostic
  • Communications
  • Security
  • Other

In the blogpost on using the help subsystem I covered that the Get-Help cmdlet could be use to find commands on the system since it parses the help information, this could prove useful in exploring but it will also miss system commands, Dynamic Link Libraries and some time it can even be to noisy depending on what we want to find. There is a specific cmdlet in PowerShell whose whole purpose is to find all commands that are installed on the computer, including cmdlets, aliases, functions,workflows, filters, scripts, and applications. Get-Command gets the commands from Windows PowerShell modules and this is the Get-Command cmdlet. If we use the Get-Help cmdlet against the Get-Command cmdlet we get the following Syntax options:

image

We have 2 general ways we can run the cmdlet, the first one we can search by Verb or Noun and in the second one we can search by Name of the cmdlet. Lets take take the second way of running the cmdlet and look at the name option [[-Name] ] we see that it takes a string object and that we can provide it a list of string because it has the [ ] at the end of the type, we also see that -Name is between [ ] which means it is optional to. If we look at the parameter it self we will see:

PS C:\> Get-Help Get-Command -Parameter Name

-Name <String[]>
    Gets only commands with the specified name. Enter a name or name pattern. Wildcards are permitted.

    To get commands that have the same name, use the All parameter. When two commands have the same name, by default,
    Get-Command gets the command that runs when you type the command name.

    Required?                    false
    Position?                    1
    Default value                None
    Accept pipeline input?       True (ByValue, ByPropertyName)
    Accept wildcard characters?  false

NOTE: I see that is in “Accept wildcard characters?” it says false, that  is a bug that has been present since PowerShell 2.0 since I can remember, this is why I have gotten in to the habit of also reading the description of the parameters, I have only seen it happen with this cmdlet.

In the description we can see that we can use wildcard characters for looking in the names of the commands and also we see it is in position 1 so we can just pass that wildcard expression as the first argument passed to the cmdlet.  It will search for system commands in all the locations that are in the $PATH variable also. Here is an example where I show the contents of my Path environment variable and as it can be seen it includes Nmap and SQL Express installations so if I look for *nmap* and for *sql* it will show the commands for those:

PS C:\> $env:path
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\Wind
ows System Resource Manager\bin;C:\Program Files\Microsoft Network Monitor 3\;C:\Program Files\Microsoft SQL Server\110\Tool
s\Binn\;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files (x86)\Nmap
PS C:\> get-command *nmap*

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Application     nmap.exe
Application     nmap-update.exe
Application     zenmap.exe


PS C:\> get-command *sql*

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Application     SQLCMD.EXE
Application     SqlLocalDB.exe

When using Wildcard Characters we can use any of the characters listed in the table below:

image

When it comes to parameters PowerShell also has Tab Autocomplete for parameter and they can be shorten to the minimum unique characters like we can with Cisco IOS, PowerShell does this by looking at the cmdlet it self and in the case of Function, Scripts and Workflows it will even parse the code to read this so as to provide them to AutoComplete. So we could run the Get-Service command as follows to get the information for the BITS service:

PS C:\Windows\system32> Get-Service -Name bits

Status   Name               DisplayName
------   ----               -----------
Running  bits               Background Intelligent Transfer Ser...


PS C:\Windows\system32> Get-Service -Name bits*

Status   Name               DisplayName
------   ----               -----------
Running  BITS               Background Intelligent Transfer Ser...


PS C:\Windows\system32> Get-Service -Na bits*

Status   Name               DisplayName
------   ----               -----------
Running  BITS               Background Intelligent Transfer Ser...


PS C:\Windows\system32> Get-Service bits*

Status   Name               DisplayName
------   ----               -----------
Running  BITS               Background Intelligent Transfer Ser...

This provides great flexibility for running cmdlets, Functions and Workflows but it also adds the power of discoverability, we can just type a –<Tab> and keep hitting the Tab key to discover the parameter we want.

On PowerShell v3 the autocompleting is further expanded to include predefined lists or those that can be calculated and deduced by the command like list of processes, list of services, error actions ..etc.

Expansion for services as they are enumerated for autocomplete:

image

Expansion of predefine options:

image

in the none ISE Terminal one can cycle thru them by just keep hitting the <Tab> Key

PowerShell v3 added a graphical way to also explore PowerShell specific commands and build the command in the GUI and execute it in the GUI also using the Show-Command cmdlet

image

image

PowerShell just like other Unix shells allows the use of Aliases for commands, this allows for less typing. Now a word of advice, since PowerShell allows the shortening of parameters or use of the values of such can produce very hard to read code when scripting with PowerShell so for interactive shell this is perfect so as to allow for fast use but when sharing command strings or scripts get in to the habit of expanding the command alias and the parameters.

We can use the Get-Command cmdlet to search for all the cmdlets for managing Aliases:

PS C:\> Get-Command *alias*

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Export-Alias                                       Microsoft.PowerShell.Utility
Cmdlet          Get-Alias                                          Microsoft.PowerShell.Utility
Cmdlet          Import-Alias                                       Microsoft.PowerShell.Utility
Cmdlet          New-Alias                                          Microsoft.PowerShell.Utility
Cmdlet          Set-Alias                                          Microsoft.PowerShell.Utility

The use of the <verb>-<noun> format makes the identification of the purpose of the cmdlets quite simple. To get a list of the aliases on the current sessions we would use the Get-Alias cmdlet. We can create new cmdlets with New-Alias and Set-Alias, to modify an Alias the Set-Alias cmdlet is used.

To get the definition of a specific alias:

PS C:\> Get-Alias -Name ls

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           ls -> Get-ChildItem

To get what aliases are for a specific cmdlet

PS C:\> Get-Alias -Definition Get-ChildItem

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Alias           dir -> Get-ChildItem
Alias           gci -> Get-ChildItem
Alias           ls -> Get-ChildItem

To create a new alias

PS C:\> New-Alias -Name ll -Value Get-ChildItem
PS C:\> ll


    Directory: C:\


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----         8/16/2012   5:14 PM            inetpub
d----         7/26/2012   3:33 AM            PerfLogs
d-r--         1/18/2013  10:03 PM            Program Files
d-r--         1/13/2013   8:33 AM            Program Files (x86)
d----        10/30/2012  10:02 PM            SQLEXPRESS
d-r--         8/15/2012   8:35 PM            Users
d----         1/12/2013  10:56 PM            Windows

Sadly the aliases we create are not permanent and are lost once we close the session. We can export the aliases we create to a file and load them in to any session or autoload them with the use of profiles files (More on this on a later blogpost), to export them we use the Export-Alias cmdlet.

PS C:\> export-alias -path alias.csv

This will generate a a list in to a CSV File

image

It an also be exported as a Script

PS C:\> Export-Alias -Path .\alias.ps1 -as script

image

To import the aliases that where exported we use the Import-Alias.

PS C:\> Import-Alias -Path .\alias.csv -ErrorAction SilentlyContinue

Conclusion

As always I hope you found this blogpost useful and I invite you to read more on the subject covered by looking at the following conceptual help information inside of PowerShell using the Get-Help cmdlet (Yes it is homework):

  • about_CommonParameters
  • about_Parameters
  • about_Parameters_Default_Values
  • about_Core_Commands
  • about_Command_Syntax
  • about_Command_Precedence
  • about_Aliases