Wednesday, February 19, 2014

Use PowerShell to fix Windows file permission vulnerabilties.

NOTE: I have an updated version of this script in another post....Variations on a theme

We use a forensics tool to internally scan our network for vulnerabilities on a regular basis.  This tool spits out a list of all sorts of stuff which we then go and address.  One of the largest lists it gives us is insecure file permissions on Windows systems.

I wrote this script to address the issues.  One problem I faced was that the files are largely owned by the "TrustedInstaller" account.  You cannot easily adjust the permissions if that account has ownership so you need to change the ownership.

The script uses two flat text files as input.  The first is just a list of target systems, one per line.  The second is a list of files to adjust. Complete local paths are assumed.  The script adjusts the paths from a local path to a remote path.  It generates basic log files, one per target in the C:\Scripts\Logs folder.  I decided on one per target since there was a lot of info in each.

The script first checks if the target is reachable.  It then cycles through the files, checking if each exists.  It then invokes a remote copy of TAKEOWN to take ownership of the file.  Once that's done the permissions are edited, local admins is added, and TrustedInstaller is reapplied as owner.

NOTE: I set -whatif statements on the three lines that do the actual work.  Remove or comment them out before you actually run the script.

Hopefully someone will get use out of it.

!!!   WARNING  !!!   Don't blame me...you've been warned...  This script can cause catastrophic corruption of Windows.  If you reset the wrong permission on system files Windows WILL NOT BOOT.  We ran into this on our test run.  Test systems booted to a black screen after displaying the Windows flag.  To repair it we booted to a live Windows CD, then ran "icacls * /t /c /q /reset" from the "c:\windows" folder.  The command recursively replaces all inherited permissions on all files.  After that the system booted normally.

Watch for word wrapping below.....


#======================================================================================
#         File Name : EditRemotePermissions.ps1
#   Original Author : Kenneth C. Mazie (kcmjr AT kcmjr.com)
#                   :
#       Description : Used to adjust insecure file permissions on remote systems.
#                   :
#             Notes : Normal operation is with no command line options.
#                   :
#          Warnings :
#                   : 
#             Legal : Public Domain. Modify and redistribute freely. No rights reserved.
#                   : SCRIPT PROVIDED "AS IS" WITHOUT WARRANTIES OR GUARANTEES OF
#                   : ANY KIND. USE AT YOUR OWN RISK. NO TECHNICAL SUPPORT PROVIDED.
#                   :
#           Credits : Code snippets and/or ideas came from many sources including but
#                   :   not limited to the following:
#                   :
#    Last Update by : Kenneth C. Mazie
#   Version History : v1.0 - 02-19-14 - Original
#    Change History : v1.1 - 00-00-00 -   
#                   :
#=======================================================================================

Clear-Host

function Run-RemoteCMD {
    param(
    [Parameter(Mandatory=$true,valuefrompipeline=$true)]
    [string]$computer,
    [string]$command
    )
    begin {
         [string]$cmd = "CMD.EXE /C " +$command
          }
    process {
        $newproc = Invoke-WmiMethod -class Win32_process -name Create -ArgumentList ($cmd) -ComputerName $computer -whatif
        sleep -Seconds 1
        if ($newproc.ReturnValue -eq 0 ){
        Write-Output "`nCommand $($command) invoked Sucessfully on $($computer)`n"
        Add-Content $LogFile "----------------------------------------------------------------`nCommand $($command) invoked Sucessfully on $($computer)"
            } # if command is sucessfully invoked it doesn't mean that it did what its supposed to do it means that the command only sucessfully ran on the cmd.exe of the server. syntax errors can occur due to user input
        }
    }
 
$Debug = $true    #--[ Set to $false to quiet local output ]--
$ErrorActionPreference = "SilentlyContinue"
$BadUsers = @("Everyone","Domain Users","Authenticated Users","Users")
$TargetList = Get-Content c:\Scripts\TargetList.txt
$PathList = Get-Content c:\scripts\PathList.txt

#$TargetList = "testbox"      #--[ Use for testing ]--
#$PathList = "c:\test\new text document.txt"     #--[ Use for testing ]--

ForEach ($Target in $TargetList){
  $LogFile = "c:\scripts\logs\$Target-{0:MM-dd-yyyy_HHmm}.log" -f (Get-Date)
  If (Test-Connection -ComputerName $Target){
    ForEach ($File in $PathList){
      [string]$Drive = ($File.split(":"))[0]
      [string]$File = ($File.split(":"))[1]
      $Drive = $Drive + "$"
      $CurrentTarget = "\\$Target\$Drive$File"
      If (Test-Path -Path $CurrentTarget){
        $ACL = Get-ACL $CurrentTarget
        If ($Debug){$ACL | format-List}
      
        Run-RemoteCMD $Target "takeown /f $CurrentTarget /a "

        #$ACL.Access | %{$acl.RemoveAccessRule($_)}   #--[ Removes all access rules.  DONT USE
        $ACL.SetAccessRuleProtection($True, $False)
        $Rights = [System.Security.AccessControl.FileSystemRights]::FullControl
        $Inheritance = [System.Security.AccessControl.FileSystemAccessRule]::ContainerInherit -bor [System.Security.AccessControl.FileSystemAccessRule]::ObjectInherit
        $Propagation = [System.Security.AccessControl.PropagationFlags]::None
        $Access = [System.Security.AccessControl.AccessControlType]::Allow

        $ACE = New-Object System.Security.AccessControl.FileSystemAccessRule("BUILTIN\Administrators", $Rights, $Inheritance, $Propagation, $Access)
        $ACL.AddAccessRule($ACE)
        ForEach ($BadUser in $BadUsers){
          $ACE = New-Object System.Security.AccessControl.FileSystemAccessRule($BadUser, $Rights, $Inheritance, $Propagation, $Access)
          $ACL.RemoveAccessRule($ACE)
        }
        Set-Acl $CurrentTarget $ACL -WhatIf
      
        $NewOwner=New-Object System.Security.Principal.NTAccount("NT SERVICE\TrustedInstaller")
        $ACL.SetOwner($NewOwner)
        Set-Acl $CurrentTarget $ACL -WhatIf
      
        $ACL = ""
        $ACE = ""
        $ACL = Get-ACL $CurrentTarget
      
        Add-Content $LogFile "PREMISSIONS ADJUSTED - $CurrentTarget"
        If ($Debug){$ACL | Format-List}
        Add-Content $LogFile "----------------------------------------------------------------"
        If ($Debug){Write-Host "----------------------------------------------------------------"`n}
      }Else{
      If ($Debug){Write-Host "NOT FOUND - $CurrentTarget"}
      Add-Content $LogFile "NOT FOUND - $CurrentTarget"
      }
    }
  }Else{
  Add-Content $LogFile "----------------------------------------------------------------`nNoConnection to $Target`n----------------------------------------------------------------`n"
  Write-Host "----------------------------------------------------------------`nNoConnection to $Target`n----------------------------------------------------------------`n"
  }
}
Write-Host "Completed...." -ForegroundColor Red