Thursday, October 9, 2014

Update Existing PST file path for Users

Replaced an aging file server, and ran into the issue of Terminal Services users not being able to load their Outlook pst data files. After much surfing, searching, and a call to Microsoft, I still had nothing. Thanks to a co-worker and mucho caffeine, we finally located the reg keys (in binary) that stored the pst location.

The script below is tested against Outlook 2010, but has been successful in my testing. Set the $oldpath and $newpath to fit your environment. Test it against a few accounts before deploying. I take no responsibility for the use of this in any org. :D

$encodingType = [Text.Encoding]::Default.HeaderName
$oldPath = "" #Old fileserver/path name here
$newPath = "" #New fileserver/path name here


function bin2string{
    Param(
        $thispath, #Path to the Key
        [string]$thisProperty #Name of the property (typically in HEX)
    )
    $binary = Get-ItemProperty -path $thisPath.pspath | select -expand $thisProperty
    $string = [Text.Encoding]::GetEncoding($encodingType).GetString( $binary )
    return $string
}

function string2bin{
    param(
        [string]$thisString
    )
    $newBinary = [Text.Encoding]::GetEncoding($encodingType).GetBytes( $thisString )
    return $newBinary
}


set-location "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Default Outlook Profile"
$path = get-location


$keys = get-childitem $path

foreach($key in $keys){
    #write-host $key -ForegroundColor DarkCyan
    $properties = get-itemproperty -path $key.pspath
    #$properties | where {$_ -match "01020fff"}
    if (($properties | where {$_ -match "01020fff"}).count -ne 0 -AND ($properties | where {$_ -match "001f3006"}).count -ne 0){
        #Make sure this is an Outlook Data File Key
        if ((bin2string $key "001f3006") -match "O\WU\WT\WL\WO\WO\WK\W+D\WA\WT\WA\W+F\WI\WL\WE"){
            Write-Host "Found $(bin2string $key "001f3006") in $Key" -ForegroundColor Yellow

            #Get string from Property 01020fff, the functional Path property
            $nowString = bin2string $key "01020fff"
            #Fix string in Property 01020fff, the functional Path property
            $nowString = $nowString -replace $oldPath,$newPath
            #Convert Fixed String Back into Binary
            $newBinary = string2bin $nowString
            #Set the Bin as the Value of Property 01020fff, the functional Path property
            Set-ItemProperty -path $key.pspath  -name "01020fff" -value $newBinary

            #Get string from Property 001f6700
            $nowString2 = bin2string $key "001f6700"
            $nowString2
            #Fix string in Property 001f6700
            $nowString2 = $nowString2 -replace ($oldPath.ToCharArray() -join "\W"),($newPath.ToCharArray() -join " ")
            $nowString2
            #Convert Fixed String Back into Binary
            $newBinary2 = string2bin $nowString2
            #Set the Bin as the Value of Property 001f6700
            Set-ItemProperty -path $key.pspath  -name "001f6700" -value $newBinary2

            }
        }
    }

Wednesday, October 1, 2014

Updating Shortcuts for a File Server replacement

When retiring an older File Server, one has to account for the many possible uses and references to the old file server name. Shortcuts is one that can be addressed via Powershell. The following script finds all shortcuts that have the $from server, and update the references to point to the $to server (these are set in the script beforehand). The only catch is that I didn't build in long file name support, so if you have paths over 255 characters, some additions will be required.
<# 
 .NOTES
 ===========================================================================
  Author: Jared Shippy
  Created: 09/19/2014
 ===========================================================================
 .DESCRIPTION
  Script for updating shortcuts for new file server location
 .PARAMETER Path
  Path to target folder.
 .PARAMETER Recurse
  Updates shortcuts through subfolders if used.
 .PARAMETER Whatif
  Writes no changes, displays files that would be affected.
 .EXAMPLE
  # See which shortcuts would be affected in c:\testFolder (subfolders included)
  lnkUpdate -path c:\testFolder -recurse -whatif
 .EXAMPLE
  # Make the perform the change in c:\testfolder ONLY (no subfolders)
  lnkUpdate -path c:\testfolder
#>

param (
 # Path to root of Targeted Folder
 [Parameter(Mandatory=$true)]
 [string]$path,
 # Perform Recursive?
 [switch]$recurse,
 # Does nothing, shows what it *would* do
 [switch]$whatif
)

# Initialization Variables
$shell = New-Object -ComObject wscript.shell
$from = "oldServer" #The name of the old file server
$to = "newServer" #The name of the new file server

# Targeting Logic - To recurse or not recurse
$lnkFiles = $null
if ($recurse -eq $true)
 {
  $lnkFiles = Get-ChildItem -Recurse (Join-Path $path \*.lnk)
 }
 else
 {
  $lnkFiles = Get-ChildItem (Join-Path $path \*.lnk)
}
foreach ($file in $lnkFiles)
{
 
 if (($shell.createShortcut($file.fullname)).targetPath -match "$from" -or ($shell.createShortcut($file.fullname)).workingdirectory -match "$from" -or ($shell.createShortcut($file.fullname)).arguements -match "$from")
 
 {
  Write-Host "Match on $($file.fullname)" -ForegroundColor 'DarkCyan'
  if ($whatif -eq $true)
  {
   Write-Host "WHATIF: WOULD replace path here"
  }
  else
  {
   $fixThis = $shell.createShortcut($file.fullname)
   $fixThis.targetPath = $fixThis.targetPath -replace "$from", "$to"
   $fixThis.workingDirectory = $fixThis.workingDirectory -replace "$from", "$to"
   $fixThis.arguments = $fixThis.arguments -replace "$from", "$to"
   $fixThis.Save()
  }
  
 }
}



# check and update links
foreach ($shortcut in $shortcuts)
{
 # Find the correct property for the next line:
 If ($shortcut.property -match "$from")
 {
  $shortcut.property -replace "$from", "$to"
 }
 else
 {
  Write-Host "No $from in this Shortcut" -ForegroundColor Green
  
 }
}
 


Friday, July 18, 2014

Detecting Cryptolocker/Cryptowall by encrypted files

Have you encountered the Crypto line of ransomware? Having encountered a couple variants of this with a few of my customers, I began wondering how one would detect an infection in an enterprise environment, where recent backups are available, but users *may* not advertise they caught a virus. Detecting the encrypted files before all of your backups are overwritten would be important. How would we do this?

File headers combined with write times appears to be an effective means detection, as Encrypted file headers do not match even each-other, and write-times will be VERY close to eachother when a crypto-ransomware hits, so write-times helps weed out false positives.

Turning to powershell, I borrowed from Will at http://learningpcs.blogspot.com/2012/07/powershell-v3-check-file-headers.html. I made some minor modifications to his script, limiting the file types it targets, and causing it to search recursively through the directory tree of it's target. Enable verbose messaging and you will see the headers for any mismatches, so you can add your own if needed. This script will ONLY tell you about mismatches.


<
# http://learningpcs.blogspot.com/2012/07/powershell-v3-check-file-headers.html
# http://garcol.blogspot.com/2014/07/detecting-cryptolockercryptowall-by.html
function Check-Header
{
       param(
             $path
       )
      
       # Hexidecimal signatures for expected files
       $pdf = '25504446';
       $doc = 'D0CF11E0';
       $docx = '504B0304';
       $xls = 'D0CF11E0';
       $xls2 = '093C7461';
       $xlsx = '504B0304';

       
       $targets = Get-ChildItem -file -Path $path -recurse -include ('*.pdf','*.doc','*.docx','*.xls','*.xlsx') | select fullname, lastwritetime
       foreach ($file in $targets){     
            # Get content of each file (up to 4 bytes) for analysis
            $HeaderAsHexString = $null
            [Byte[]]$fileheader = Get-Content -Path $file.fullname -TotalCount 4 -Encoding Byte
            ForEach($_ in $fileheader) {
                if(("{0:X}" -f $_).length -eq 1)
                    {
                     $HeaderAsHexString += "0{0:X}" -f $_
                    }else{
                     $HeaderAsHexString += "{0:X}" -f $_
                    }
            }
       # Validate file header
       if (@($pdf, $doc, $docx, $xls, $xls2, $xlsx) -contains $HeaderAsHexString){
            #do nothing
            }else{
            $file
            Write-Verbose -message $HeaderAsHexString
            } 
       
    }
}


Thursday, July 3, 2014

Powershell Scripting Telnet

There are some of us out there that use appliances, equipment, or software that is still stuck with the telnet protocol. Where there are options for scripting interactions with webpages, SSH, and many other resources, I did not find many options for telnet scripting, and I'm not at the point in powershell where I could whip up a telnet client in powershell. Fortunately, Grant Carthew has already done the heavy lifting here. Below you can find a modified version of his script that allows scripting interactions through the telnet protocol, retrieving the responses so that logic can be performed on them.

Credits to the Ugly Gizmo blog, this script would not be without Grant's work.
<#
.SYNOPSIS
A modified graceful telnet scripting client using PowerShell and the .NET Framework.
 
.DESCRIPTION
This script was originally made with a view of using it to have full control over the text
stream for automating Cisco router and switch configurations. Modifications have been
added for scripting automation and logic
 
.PARAMETER TelnetHost
The address of the server or router hosting the telnet service.
 
.PARAMETER Port
The TCP port number of the Telnet service running on the Telnet host.

.NOTES
Modified: http://garcol.blogspot.com/2014/07/powershell-scripting-telnet.html
Original: http://uglygizmo.blogspot.com/2013/10/a-telnet-client-written-in-powershell.html
#>


 
param
(
    [parameter(Mandatory)]
    [String]
    $TelnetHost,
    [Int]
    $TelnetPort = 23
)
 
# Initialize variables
[System.Text.ASCIIEncoding]$ASCIIEncoding = [System.Text.Encoding]::ASCII
[System.Net.Sockets.Socket]$Socket = $null
 
# Checking host address and port.
if ($TelnetHost -match ":")
{
    $hostBytes = $TelnetHost.Split(':')
    $TelnetHost = $hostBytes[0]
    $TelnetPort = $hostBytes[1]
}
if ([String]::IsNullOrEmpty($TelnetHost))
{
    Write-Host -Object "Error: Invalid host address (null or empty)." -ForegroundColor Red
    return
}
 
# Setup and connect the TCP Socket.
$Socket = New-Object -TypeName System.Net.Sockets.Socket(
    [System.Net.Sockets.AddressFamily]::InterNetwork,
    [System.Net.Sockets.SocketType]::Stream,
    [System.Net.Sockets.ProtocolType]::Tcp)
$Socket.NoDelay = $true
try
{
    $Socket.Connect($TelnetHost, $TelnetPort)
}
catch
    {
    "Unable to connect to host {0}:{1}" -f $TelnetHost,$TelnetPort
    return
}
 
# This state object is used to pass the connected Socket and the
# PowerShell parent Host reference to the child PowerShell object.
$State = [PSCustomObject]@{"Socket"=$Socket;"Host"=$Host;"Exception"=$null;"Response"=$null}
 
# This script block is used as the receive code for the Socket
# from within the child PowerShell object.
$Script = {
    param($state)
    [string[]]$returnLog = "Started"
    # This encoding object is used to decode the return string.
    [System.Text.ASCIIEncoding]$ASCIIEncoding = [System.Text.Encoding]::ASCII
 
    # TELNET commands
    [Byte]$GA = 249 # Go Ahead
    [Byte]$WILL = 251 # Desire to begin
    [Byte]$WONT = 252 # Refusal to perform
    [Byte]$DO = 253 # Request that the other party perform
    [Byte]$DONT = 254 # Demand that the other party stop performing
    [Byte]$IAC = 255 # Interpret as Command
 
    # TELNET options
    [Byte]$ECHO = 1 # Used to check the echo mode
    [Byte]$SUPP = 3 # Suppress go ahead
    # Used to hold the number of bytes returned from the network stream.
    [Int]$bytes = 0
    # Buffer to hold the returned Bytes.
    [Byte[]]$buffer = New-Object -TypeName Byte[]($state.Socket.ReceiveBufferSize)
    # This is the main receive loop.
    while ($state.Socket.Connected)
    {
        try
        {
        # The following statement will block the thread until data is received.
        $bytes = $state.Socket.Receive($buffer)
        }
        catch
        {
        # This exception reference is used to pass the error back to the
        # parent PowerShell process.
        $state.Exception = $Error[0]
        break
        }
 
        if ($bytes -gt 0)
        {
            $index = 0
            $responseLen = 0
            # The index is used to move through the buffer to analyze the received data
            # looking for Telnet commands and options.
            while ($index -lt $bytes)
            {
                if ($buffer[$index] -eq $IAC)
                {
                    try
                    {
                        switch ($buffer[$index + 1])
                        {
                            # If two IACs are together they represent one data byte 255 
                            $IAC
                            {
                                $buffer[$responseLen++] = $buffer[$index]
                                $index += 2
                                break
                            }
                            # Ignore the Go-Ahead command
                            $GA
                            {
                                $index += 2
                                break
                            }
                            # Respond WONT to all DOs and DONTs
                            {($_ -eq $DO) -or ($_ -eq $DONT)}
                            {
                                $buffer[$index + 1] = $WONT
                                $state.Socket.Send($buffer, $index, 3,
                                [System.Net.Sockets.SocketFlags]::None) | Out-Null
                                $index += 3
                                break
                            }
                            # Respond DONT to all WONTs
                            $WONT
                            {
                                $buffer[$index + 1] = $DONT
                                $state.Socket.Send($buffer, $index, 3,
                                [System.Net.Sockets.SocketFlags]::None) | Out-Null
                                $index += 3
                                break
                            }
                            # Respond DO to WILL ECHO and WILL SUPPRESS GO-AHEAD
                            # Respond DONT to all other WILLs
                            $WILL
                            {
                                [Byte]$action = $DONT
 
                                if ($buffer[$index + 2] -eq $ECHO)
                                {
                                    $action = $DO
                                }
                                elseif ($buffer[$index + 2] -eq $SUPP)
                                {
                                    $action = $DO
                                }
 
                                $buffer[$index + 1] = $action
                                $state.Socket.Send($buffer, $index, 3,
                                    [System.Net.Sockets.SocketFlags]::None) | Out-Null
                                $index += 3;
                                break;
                            }
                        }
                    }
                    catch
                    {
                        # If there aren't enough bytes to form a command, terminate the loop.
                        $index = $bytes
                    }
                }
                else
                {
                    if ($buffer[$index] -ne 0)
                    {
                        $buffer[$responseLen++] = $buffer[$index]
                    }
                    $index++
                }
            }
            # Displays the response with no command codes on the parent PowerShell object.
            $returnString = $ASCIIEncoding.GetString($buffer, 0, $responseLen)
            $returnLog += $returnString
            #$state.Host.UI.Write($returnString)
            $state.response += $returnString
        }
    }
} # End of the child PowerShell script definition.
 
# Create a child PowerShell object to run the background Socket receive method.
$PS = [PowerShell]::Create()
$PS.AddScript($Script).AddArgument($State) | Out-Null
[System.IAsyncResult]$AsyncJobResult = $null
try
{
    # The receive job is started asynchronously.
    $AsyncJobResult = $PS.BeginInvoke()



    #YOUR TELNET COMMANDS IN HERE

    # Sends Commands
    function sendCmd($command)
    {
        # Wait, then send data.
        start-sleep -m 500
        $Data = $ASCIIEncoding.GetBytes($command)
        $Socket.Send($Data) | Out-Null
        $Socket.Send(13) | Out-Null #sends the return keystroke after each cmd
        start-sleep -m 500
    }

    ######################################################################################
    #TELNET COMMANDS IN HERE
    # Use sendCmd("") to send text commands, use $Socket.Send() to send ascii encoded keystrokes
    #

    # List of Commands to be Sent
    [string[]]$cmdList = "username"
    $cmdlist += "password"
 
    # Login
    foreach ($cmd in $cmdlist){ sendCmd($cmd) | out-null}
    

    sendCmd("show something") | out-null
    $thisData = $state.response #Can perform logic on the response here
    $state.response = $null
    
    # Issue the Exit command, closing the session gracefully
    sendCmd("exit") | out-null


    #
    #TELNET COMMANDS IN HERE
    ######################################################################################





}
finally
{   
    # Cleanup the socket and child PowerShell process.
    if ($Socket -ne $null)
    {
        $Socket.Close()
        $Socket.Dispose()
        $Socket = $null
    }
 
    if ($PS -ne $null -and $AsyncJobResult -ne $null)
    {
        $PS.EndInvoke($AsyncJobResult)
        $PS.Dispose()
    }
    
}

Tuesday, April 8, 2014

get-XALoadDetails

I wanted a tool to be able to pull load information from the different cmdlets available in the XenApp powershell snapin, I also took the opportunity to learn how powershell modules work. Grabbing from get-xaserver, get-xaloadevaluator, and get-xaserver it produces the following output:

LoadEval LoginStatus   Load     Sessions Max ServerName
-------- -----------   ----     -------- --- ----------
Default  AllowLogOns   2100           21 100 XenAppSvr

It accepts pipeline input (from get-xaworkergroupserver for instance) and includes handling for the snap-in to avoid the getting the snap-in scope-locked.



xenModules.psm1
<?ps
#Requires -version 2.0
#Requires –PSSnapin Citrix.XenApp.Commands


function get-XALoadDetails{
    #.Synopsis
        # Gathers XenApp Load Evaluation Data into a single object
    #.Description
        # Gathers XenApp Load Evaluation Data into a single object, useful for determining load balancing behavior. Requires Citrix Xenapp Powershell snapin.
    #.Parameter Server
        # Server to pull load Evaluation data from, or enter * or all, to poll the entire farm.
    #.Parameter HandleSnapin
        # Loads and unloads the Citrix.XenApp.Commands snapin for the script. Default is false.
    #.Example
        # Show Load Balancing Details for server "2K8-Hamster"
        # Get-XALoadDetails -Server 2K8-Hamster
    #.Example
        # Show Load Balancing Details for all XenApp Servers
        # Get-XALoadDetails -Server All
 
  [cmdletbinding()]
    param(
            # Target Server, or * to pull all Xen Servers
            [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True)]
            [string[]]$server,
            # Handle loading the snapin? Default is no.
            [Parameter(Position=1, Mandatory=$False)]
            [Alias("LS")]
            [switch]$handleSnapin
       )
    BEGIN{
        #Handling the Citrix Snapin. If the $handleSnapin is called, CHECK to see if the snapin is loaded before continuing
        #If the snapin was already loaded, set $handleSnapin to $false
            if($handleSnapin -eq $true){
                if ((Get-PSSnapin -name Citrix.XenApp.Commands -ErrorAction SilentlyContinue) -eq $null){
                    Add-PSSnapin Citrix.XenApp.Commands
                    }ELSE{
                    $handleSnapin = $false
                    }
            }
 
        #{Add-PSSnapin citrix*}
        $allLoads = Get-XAServerLoad #grab load info ONCE
        
         
         
                                                         
    }PROCESS{
        
        [string[]]$servers = " "
        if ($server -eq "*" -or $_ -eq "all")
            {$servers = Get-XAServer | sort ServerName | select -expand servername}
            ELSE
            {$servers = $server}
                
        
          #if (($server -eq "*") -or ($server -eq "all")){$server = Get-XAServer | sort ServerName | select -expand servername}
          $servers
        foreach ($server in $servers){
        if((Test-Connection -count 1 ($server) -quiet) -eq $true){
            "yup"
            $3in1 = get-xaserver ($server) -full | select ServerName, SessionCount, LogOnMode
            $allEvals = get-xaloadevaluator -server ($3in1.ServerName) | select ServerUserLoad, LoadEvaluatorName
            $obj = New-Object PSObject -Property @{
                MaxSessions = $allEvals.ServerUserLoad
                LoadEvaluator = $allEvals.LoadEvaluatorName
                LoginStatus = $3in1.LogOnMode
                Load = $allLoads | where{($3in1.ServerName) -eq $server.ServerName} | select -expand Load
                Sessions = $3in1.SessionCount
                ServerName = $3in1.Servername
                }
            $obj.pstypenames.Insert(0,'xenModule.LoadDetails')
            $obj
             
            }

        }
 
    }END{
    #Remove PSSnapin if it wasn't loaded before, to avoid scope locking
    if($handleSnapin -eq $true){Remove-PSSnapin Citrix.XenApp.Commands
        }
    }
 
}##End of get-XALoadDetails Function
export-modulemember -function get-XALoadDetails


xenModules.psd1
<?ps
#Requires –PSSnapin Citrix.XenApp.Commands
#
# Module manifest for module 'xenModules'
#
# Generated by: Jared Shippy
#
# Generated on: 04/03/2014
#

@{

# Script module or binary module file associated with this manifest
# RootModule = ''

ModuleToProcess = 'xenModules.psm1'

# Version number of this module.
ModuleVersion = '1.0'

# ID used to uniquely identify this module
#GUID = 'd0a9150d-b6a4-4b17-a325-e3a24fed0aa9'

# Author of this module
Author = 'Jared Shippy'

# Company or vendor of this module
#CompanyName = 'Unknown'

# Copyright statement for this module
#Copyright = '(c) 2012 User01. All rights reserved.'

# Description of the functionality provided by this module
# Description = ''

# Minimum version of the Windows PowerShell engine required by this module
#PowerShellVersion = '2'

# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''

# Minimum version of the Windows PowerShell host required by this module
# PowerShellHostVersion = ''

# Minimum version of the .NET Framework required by this module
# DotNetFrameworkVersion = ''

# Minimum version of the common language runtime (CLR) required by this module
# CLRVersion = ''

# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @()

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module
# ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
FormatsToProcess = @('xenModules.Format.ps1xml')

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
# NestedModules = @()

# Functions to export from this module
FunctionsToExport = '*'

# Cmdlets to export from this module
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module
AliasesToExport = '*'

# List of all modules packaged with this module
# ModuleList = @()

# List of all files packaged with this module
# FileList = @()

# Private data to pass to the module specified in RootModule/ModuleToProcess
# PrivateData = ''

# HelpInfo URI of this module
# HelpInfoURI = ''

# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''

}


xenModules.Format.ps1xml
<?xml



  

  HashTable
  
    XenModule.LoadDetails
  
  
    
      
        
        8
      
      
        13
      
      
        8
      
      
        8
        Right
      
      
        
        3
      
      
        10
      
    
    
      
        
           
             LoadEvaluator
           
           
             LoginStatus
           
           
             Load
           
           
             Sessions
           
           
              MaxSessions
           
           
             ServerName