PowerShell – Regin Malware Detection

The IT press has been full of stories about Symantec’s discovery of the Regin Malware threat. Symantec have released a security response about the threat (http://www.symantec.com/content/en/us/enterprise/media/security_response/whitepapers/regin-analysis.pdf) which includes MD5 file hashes, registry locations and known file locations associated with the malware. I needed a means of detecting of its presence on our network, so after seeing a post on Twitter from @jsnover I thought I would expand on the MD5 file check and include the file locations, registry items and compile the results into a text file which is stored on a central repository for review.

Note that for best results you should run the PS script under the local system account, you will also need these three files (rename these from .doc to .txt)
Files
MD5Signatures
Registry
The files should be placed in the location specified as $ReginSourceFiles.

Note that the script and source files are provided without support and should be used at your own risk. Details of the registry entries, MD5 hashes and file locations are taken from Symantec’s Regin analysis report.

<#	
	.NOTES
	===========================================================================
	 Created by:   	Maurice Daly
	 Filename:    ReginDetect.ps1 	
	===========================================================================
	.DESCRIPTION
		PowerShell script to scan for knownn registry, file names and MD5 hashes
		assoicated with the Regin malware threat. Results are uploaded to a
		central file share.
#>

Import-Module Storage
$ErrorActionPreference = "SilentlyContinue"
$ReginSourceFiles = "\\YOURSERVER\YOURSHARE\ReginSourceFiles"
$ReginResults = "\\YOURSERVER\YOURSHARE\ReginResults"
$ReginTemp = 'C:\Temp\ReginScan'
Get-ChildItem -Path $ReginSourceFiles | Copy-Item -Destination $ReginTemp -Include *.txt
If (!(Test-Path -Path $ReginTemp))
{
	New-Item -ItemType Directory -Path 'C:\Temp\ReginScan'
}
$MD5Values = Get-Content $ReginTemp\MD5Signatures.txt
$RegValues = Get-Content $ReginTemp\Registry.txt
$FileValues = Get-Content $ReginTemp\Files.txt
Clear-Host

# Checking for Registry entries
Write-Host -ForegroundColor 'White' "Regin Scanning Tool"
Write-Host -ForegroundColor 'Cyan' "Checking Registry for Regin entries"
$RegistryDetection = foreach ($RegEntry in $RegValues)
{
	for ($i = 1; $i -le 10; $i++)
	{
		write-progress -id 1 -activity "Scanning Registry Hive" -status "Checking for $RegEntry" -percentComplete ($i * 10);
		$RegTest = Test-Path $RegEntry
	}
	if ($RegTest -eq $true)
	{
		Write-Output "Regin registry entry found - $RegEntry"
	}
}
sleep 2

# Checking for known files
Write-Host -ForegroundColor 'White' "Commencing known file name scan."

$KnownFileDetection = foreach ($KnownFile in $FileValues)
{
	for ($i = 1; $i -le 10; $i++)
	{
		write-progress -id 1 -activity "Scanning Knonwn Files" -status "Scanning $KnownFile" -percentComplete ($i * 10);
		$FileTest = Test-Path $KnownFile
	}
	if ($FileTest -eq $true)
	{
		Write-Output "Known Regin file found at $KnownFile"
	}
}
sleep 2

# Check entire drive for MD5 hash values
Clear-Host
Write-Host -ForegroundColor 'White' "Commencing MD5 file hash scan, this might take several hours."
$FilesToScan = Get-ChildItem C:\ -Recurse -Exclude 0
$FileDetection = foreach ($File in $FilesToScan)
{
	for ($i = 1; $i -le 10; $i++)
	{
		write-progress -id 1 -activity "Scanning Files & Folders" -status "Scanning $File" -percentComplete ($i * 10);
		$FileTest = Get-FileHash -Path $File -Algorithm MD5 | ? Hash -In $MD5Values
	}
	if ($FileTest -eq $true)
	{
		Write-Output "Regin MD5 file hash found - $File"
	}
}

Clear-Host
Write-Host -ForegroundColor 'Green' "Scanning Complete"
Write-Host ""
$Result = If (($RegistryDetection -gt $null) -or ($KnownFileDetection -gt $null) -or ($FileDetection -gt $null))
{
	Clear-Host
	Write-Host -BackgroundColor 'White' -ForegroundColor 'Red' "Regin elements have been found on workstation $env:COMPUTERNAME"
	If ($RegistryDetection -gt $null)
	{
		Write-Host ""
		Write-Host -ForegroundColor 'White' -BackgroundColor 'Red' "Registry entries detected at the following locations:"
		$RegistryDetection
	}
	If ($KnownFileDetection -gt $null)
	{
		Write-Host ""
		Write-Host -ForegroundColor 'White' -BackgroundColor 'Red' "Known files detected at the following locations:"
		$KnownFileDetection
	}
	If ($FileDetection -gt $null)
	{
		Write-Host ""
		Write-Host -ForegroundColor 'White' -BackgroundColor 'Red' "Known files detected at the following locations:"
		$FileDetection
		Write-Host ""
	}
	$Result | Out-File -FilePath ("$ReginResults\" + $ENV:COMPUTERNAME + ".txt") -Force
	Write-Host ""
	Write-Host "Results uploaded to $ReginResults"
}

PowerShell – Apply Multiple Hotfixes Automatically

If you have ever had the need to apply multiple hotfixes to a server, this handy little script will apply all hotfixes located within a network share of your choice (simply download the MSU files to this location). The progress of each installation is displayed and you will be prompted to reboot post patching.

Script:

$HotfixDir = '\\YOURSERVER\YOURPATCHSHARE'
$HotfixTemp = 'C:\Temp\Hotfixes'
If (!(Test-Path -Path $HotfixTemp))
{
	New-Item -ItemType Directory -Path 'C:\Temp\Hotfixes\'
}
Set-Location -Path $HotfixTemp
Get-ChildItem -Path $HotfixDir | Copy-Item -Destination $HotfixTemp -Include *.msu
$Hotfixes = Get-ChildItem $HotfixTemp | Where-Object { $_.Name -like '*.msu' } | Select Name | Sort-Object

foreach ($Hotfix in $Hotfixes.name)
{
	$NextHotfix = ("$hotfixtemp" + "\$Hotfix" + " /quiet" + " /norestart")
	Start-Process C:\Windows\System32\wusa.exe -ArgumentList $NextHotfix
	while ((Get-Process wusa -ErrorAction 'SilentlyContinue') -ne $Null)
	{
		for ($i = 1; $i -le 10; $i++)
		{
			write-progress -id 1 -activity "Processing $Hotfix" -status "Installing..." -percentComplete ($i * 10);
			sleep 1;
			
		}
	}
}
Write-Host -ForegroundColor 'Green' "Patching completed. Please reboot for updates to take effect"

PowerShell – Restart Client Citrix ICA session & Print Spooler – Self Service

Our IT Helpdesk often receives calls from users who for a multitude of reasons have issues with their Citrix connection,  more often due to the fact they haven’t turned on their printer before connecting and cant see their local printer etc.

To resolve this we deployed the following self-service script that undertake the following processes to ensure that the user’s Citrix session is re-started in a clean state. In the script you will see I have listed a “YourApplication.lnk” as we have a common application that all users of Citrix use so it calls this by default.

The process is as follows:

1. Notify the user that their Citrix session will be closed.
2. Log out of Citrix session gracefully.
3. Terminate the Citrix ICA client.
4. Restart the Print Spooler service (to resolve any printing related issues).
5. Start the Citrix ICA client.
6. Check if the Citrix client is running and open the default business application.

If the process is unsuccessful or the user does not have the Citrix client installed an email is generated to the IT Helpdesk using their local Outlook client.

The script has been tested with both the legacy Citrix Online Plugin and Citrix Receiver 3.4 Enterprise.

Script:

$Prompt = new-object -comobject wscript.shell

# Variables for Citrix
$CitrixPNAPath = Get-Process -Name PNAMain -ErrorAction 'SilentlyContinue' | ForEach-Object { Write-Output $($_.Path) }
$CitrixAgentPath = ($CitrixPNAPath | Split-Path) + "\pnagent.exe"
$CitrixInstalled = Test-Path -Path $CitrixPNAPath
$CitrixShortCut = "$ENV:APPDATA" + "\Microsoft\Windows\Start Menu\Programs\Citrix Applications\Claims Applications\YourApplication.lnk"

# Message body for auto failure message
$MailBody = @"
I am having issues with my Citrix connection at present. I tried running the self service script but it was unable to resolve the problem. Can you please take a look.
- This is an automatically generated email on behalf of the user.
"@

# Send email function
Function Notify-Helpdesk
{
Start-Process Outlook
$Outlook = New-Object -com Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.importance = 2
$Mail.subject = "Citrix Problems"
$Mail.body = $MailBody
$Mail.To = 'it.helpdesk@yourdomain.domain'
$Mail.Send()
}

# Detect & Repair Citrix
If ($CitrixInstalled -eq $true)
{
$PromptWindow = $Prompt.popup('You will now be logged out of Citrix', 0, 'IT Self-Service')
Start-Process -FilePath $CitrixAgentPath -ArgumentList " /LogOff"
sleep 5
Get-Process | Where-Object {
$_.ProcessName -like "*WFICA*"
} | Stop-Process -Force
sleep 10
Restart-Service -Name Spooler -Force
Start-Process -FilePath $CitrixPNAPath
sleep 2
If ((Get-Process -Name PNAMain).responding -eq 'True')
{
$PromptWindow = $Prompt.popup('Citrix ICA client has been restarted successfully. YourApplication will now launch automatically.', 0, 'IT Self-Service')
invoke-item $CitrixShortCut
}
else
{
$PromptWindow = $Prompt.popup('Citrix ICA client failed to restart successfully. Outlook will now open and attempt to notify IT of your issue via email.', 0, 'IT Self-Service')
Notify-Helpdesk
}
}
else
{
$PromptWindow = $Prompt.popup('Citrix ICA client not found. Outlook will now open and attempt to notify IT of your issue via email.', 0, 'IT Self-Service')
Notify-Helpdesk
}

PowerShell Script to Email Notification of Password Expiration

Things that cross my mind...

We all have that set of users that either mainly use a mobile device for email access or possibly a client running a non Microsoft Windows OS as their main workstation.

Those users don’t get that friendly reminder to change their password that comes with logging onto a Windows OS near to their domain password expiration date, and this usually ends up with passwords expiring and phone calls to the IT Helpdesk to change them.

Wouldn’t it be much simpler if that group of users were emailed near to the time of password expiration, allowing the user to logon to OWA and change their password in their own time, negating the need for calls to the IT Helpdesk. In an attempt to reduce some of those calls to our own IT Helpdesk I wrote a PowerShell script to email members of a security group every day when their domain password…

View original post 379 more words

PowerShell – Convert all VHD’s on your HV cluster to VHDX

The other morning I was reviewing my hyper-v estate for VM’s running with VHD’s in the legacy VHD format. I thought to myself that a script to convert all of the VHD’s but also taking in several other important steps such as shutting down the VM, optimising the new VHDX, shrink the VHD (where possible), connecting the new VHDX and starting the machine would be nice.

I started down the path of writing the script when I came across a post on Aidan Finn’s blog (http://www.aidanfinn.com/?p=16007) which undertook the majority of what I was trying to achieve with the exception that the script runs on a single host. I decided to press on write my own script to cover off the cluster element.

The result is the below script, which only requires the name of your cluster. Once this is specified the script will connect to each of your cluster hosts in sequence, get a list of VM’s with VHD’s and proceed with the conversion process.

There is a section within the script which you can uncomment (AT YOUR OWN RISK) to remove the legacy VHD disks post conversion. As always make sure you have backup’s, allow for downtime and also make sure you have sufficient space on your LUN’s to facilitate the conversion process.  The scripts are provided as is and without support.

Cluster Script (Updated as per Jeffery Hick’s comment) :


$Cluster = "ENTER CLUSTER NAME IN HERE"
$HVNodes = Get-ClusterNode -Cluster $Cluster
ForEach ($HVNode in $HVNodes) {
    $HVPS = New-PSSession -ComputerName $HVNode
    Write-Host ""
    Write-Host "==========================================================="
    Write-Host "Connecting to $HVNode to read VM settings"
    Write-Host "==========================================================="
    Write-Host ""
    Invoke-Command -Session $HVPS -ScriptBlock {
    ForEach ($VM in (Get-VM)){
    $VMName = $VM.name
    $VMDetails = Get-VM -Name $VMName | Select-Object VMID | get-vhd | Where-Object {$_.vhdFormat -eq "VHD"}
        If($VMStatus.State -eq "Running"){
        Write-Host ""
        Write-Host -ForegroundColor Red "==========================================================="
        Write-Host "Stopping VM $VMName"
        Write-Host -ForegroundColor Red "==========================================================="
        Write-Host ""
        Stop-VM -VMName $VMName
        }
        ForEach ($VHD in $VMDetails){
        $VMVHDDetails = $VHD.Path
        $VMStatus = Get-VM -name $VMName
        Write-Host ""
        Write-Host "==========================================================="
        Write-Host "Beginning VHD conversion process for VM $VMName"
        Write-Host "==========================================================="
        Write-Host ""
            ForEach ($VHD in $VMVHDDetails){
            $NewVHD = ($VHD + "x")
                if(!(Test-Path $NewVHD))
                {
                Write-Host ""
                Write-Host "Step 1."
                Write-Host -ForegroundColor Green "Converting $VHD to fixed VHDX format"
                Convert-VHD -Path $VHD -DestinationPath $NewVHD -VHDType Fixed -verbose
                sleep 10
                Write-Host ""
                Write-Host "Step 2."
                Write-Host -ForegroundColor Green "Changing $NewVHD Sector Size (4096k)"
                Set-VHD -Path $NewVHD -PhysicalSectorSize 4096
                sleep 10
                Write-Host ""
                Write-Host "Step 3."
                Write-Host -ForegroundColor Green "Modifying VM Settings To Use $NewVHD"
                Get-VMHardDiskDrive -VMName $VMName | Where-object {$_.path -eq "$VHD"} | Set-VMHardDiskDrive -Path $NewVHD
                #sleep 10
                Write-Host ""
                Write-Host "Step 4."
                Write-Host -ForegroundColor Cyan "Shrinking VHDX to minimal file size"
                Resize-VHD -Path $NewVHD -ToMinimumSize
                #Uncomment this section if you wish to remove the old VHD drives post conversion. DO SO AT YOUR OWN RISK
                #Write-Host ""
                #Write-Host "Step 5."
                #Write-Host -ForegroundColor Red "Removing old VHD"
                #Remove-VHHardDiskDrive $VHD
                #sleep 10
                Write-Host ""
                }
                Else {
                Write-Host -ForegroundColor Gray "Skipping $VHD as virtual disk already exists in the target location"
                }
                }
        Write-Host ""
        Write-Host -ForegroundColor Green "==========================================================="
        Write-Host "Starting VM $VMName"
        Write-Host -ForegroundColor Green "==========================================================="
        Write-Host ""
        Start-VM -VMName $VMName
        }
    }
    }
}

Cluster Script (With minimal screen feedback):

$Cluster = "Enter your cluster name here"
$HVNodes = Get-ClusterNode -Cluster $Cluster
ForEach ($HVNode in $HVNodes) {
    $HVPS = New-PSSession -ComputerName $HVNode
    Write-Host ""
    Write-Host "Checking $HVNode"
    Invoke-Command -Session $HVPS -ScriptBlock {
    ForEach ($VM in (Get-VM)){
    $VMName = $VM.name
    $VMDetails = Get-VM -Name $VMName | Select-Object VMID | get-vhd | Where-Object {$_.vhdFormat -eq "VHD"}
        If($VMStatus.State -eq "Running"){
        Write-Host "Stopping $VMName"
        Write-Host ""
        Stop-VM -VMName $VMName
        }
        ForEach ($VHD in $VMDetails){
        $VMVHDDetails = $VHD.Path
        $VMStatus = Get-VM -name $VMName
        Write-Host ""
        Write-Host "Beginning VHD conversion process for VM $VMName"
            ForEach ($VHD in $VMVHDDetails){
            $NewVHD = ($VHD + "x")
                if(!(Test-Path $NewVHD))
                {
                Write-Host ""
                Write-Host "Converting $VHD to fixed VHDX format"
                Convert-VHD -Path $VHD -DestinationPath $NewVHD -VHDType Fixed -verbose
                sleep 10
                Write-Host "Changing $NewVHD Sector Size"
                Set-VHD -Path $NewVHD -PhysicalSectorSize 4096
                sleep 10
                Write-Host "Modifying VM Settings To Use $NewVHD"
                Get-VMHardDiskDrive -VMName $VMName| Where-object {$_.path -eq "$VHD"} | Set-VMHardDiskDrive -Path $NewVHD
                sleep 10
                Write-Host "Shrinking VHDX to minimal file size"
  Resize-VHD -Path $NewVHD -ToMinimumSize
                #Uncomment this section if you wish to remove the old VHD drives post conversion. DO SO AT YOUR OWN RISK
                #Write-Host "Removing old VHD"
                #Remove-VHHardDiskDrive $VHD
                #sleep 10
                Write-Host ""
                }
                Else {
                Write-Host "Skipping $VHD as virtual disk already exists in the target location"
                }
                }
Start-VM -VM $VMName
        }

    }
    }
}

Single Host Script:

ForEach ($VM in (Get-VM)){
    $VMName = $VM.name
    $VMDetails = Get-VM -Name $VMName | Select-Object VMID | get-vhd | Where-Object {$_.vhdFormat -eq "VHD"}
    ForEach ($VHD in $VMDetails){
    $VMVHDDetails = $VHD.Path
    $VMStatus = Get-VM -name $VMName
    If($VMStatus.State -eq "Running"){
    Write-Host "Stopping $VMName"
    Stop-VM -VMName $VMName
    }
    ForEach ($VHD in $VMVHDDetails){
            $NewVHD = ($VHD + "x")
                if(!(Test-Path $NewVHD))
                {
                Write-Host ""
                Write-Host "Converting $VHD to fixed VHDX format"
                Convert-VHD -Path $VHD -DestinationPath $NewVHD -VHDType Fixed -verbose
                sleep 10
                Write-Host "Changing $NewVHD Sector Size"
                Set-VHD -Path $NewVHD -PhysicalSectorSize 4096
                sleep 10
                Write-Host "Modifying VM Settings To Use $NewVHD"
                Get-VMHardDiskDrive -VMName $VMName | Where-object {$_.path -eq "$VHD"} | Set-VMHardDiskDrive -Path $NewVHD
                sleep 10
                Write-Host "Shrinking VHDX to minimal file size"
  Resize-VHD -Path $NewVHD -ToMinimumSize
                #Uncomment this section if you wish to remove the old VHD drives post conversion. DO SO AT YOUR OWN RISK
                #Write-Host "Removing old VHD"
                #Remove-VHHardDiskDrive $VHD
                sleep 10
                Write-Host ""
                }
                Else {
                Write-Host "Skipping $VHD as virtual disk already exists in the target location"
                }
             }
 Start-VM -VM $VMName
        }
    }

PowerShell – Find Dynamic Disks On Your Hyper-V Cluster

I had been looking for a script for a while that would interrogate my Hyper-V clusters and report back with a list of virtual machines running with dynamic VHD’s, as I wanted to convert all existing dynamic VHD’s to a fixed format.

So I wrote the following script to connect to each of the hyper-v hosts within a cluster, read the VHD details of the VM and report back with the VM name, host, path to the virtual disk, disk format and type (hard coded in my instance to dynamic)

$Cluster = "CLUSTERNAME"
$SaveTo = "FILEPATH"
$HVNodes = Get-ClusterNode -Cluster $Cluster
$Results = ForEach ($HVNode in $HVNodes) {
    Invoke-Command -Computername $HVNode.name {   
    ForEach ($VM in (Get-VM)){
    Get-VM -Name $VM.name | Select-Object VMID | get-vhd | Where-Object {$_.vhdType -eq &quot;Dynamic&quot;} | fl @{Label=&quot;VirtualMachine&quot;;Expression={($VM.name)}},ComputerName, Path, VHDFormat, VHDType
    }
    }
    }
$Results | out-file -FilePath $SaveTo

PowerShell – 7 Year File Audit

Data retention can be a real headache for system administrators. In my current role I am required to report on files in excess of 7 years since the last time they were written to, the below PowerShell script can be run against a directory or drive to report on these files and provide you with a total count for reporting purposes.

(Note : I typically run this script in conjunction with SysInternals PSExec to run the script at a system level and avoid any of the issues around permissions based access.)

$dir = "Drive Or Folder Path"
$years = "7"
Get-ChildItem -Path $dir -ErrorAction SilentlyContinue -Recurse | Where-Object {$_.LastWriteTime -lt (get-date).addYears(-$years)} | select Name, Directory, Length, LastWriteTime | Export-Csv -Path "C:\FilesOver7Years.csv"
$NoOfFiles = Get-ChildItem -Path $dir -ErrorAction SilentlyContinue -Recurse | Where-Object {$_.LastWriteTime -lt (get-date).addYears(-$years)}
($NoOfFiles).Count

If you wish you could write a report out to HTML and run this for management on an internal IIS instance.

PowerShell – Find & Remove Files/Folders Older Than X Days

Ever wanted to purge all files and folders based on their creation date?. In my work environment we wanted to do just that, and depending on the location we needed to specify different retention lengths hence the following simple but effective PS script:

$dir = "\\FILESERVER\SHARETOINSPECT\"
$day = "365"
Get-ChildItem -Path $dir | Where-Object {$_.PSIsContainer -eq $true -and $_.CreationTime -lt (get-date).adddays(-$day)} | Remove-Item -Recurse -Force -Verbose

In the above example I have specified 365 days as the variable, however you can modify this using years, hours etc. Running the above script will result in all files and folders with creation dates older than the specified max being purged from the system.

As always use with caution and ensure you have backups.