To find file shares with weak permissions and fix them before potential misuse, I authored a PowerShell script and a small management pack for System Center Operations Manager (SCOM) 2012 R2 (and higher).

We sometimes need to give application developers or supporting third parties administrative access to servers. With a few clicks, you can create a file share providing a convenient way to transfer files to and from the server. Unfortunately, keeping the default permissions can lead to some unwanted results. Ransomware that scans the network for vulnerabilities and encrypts everything accessible may even cause serious service outages.

State view showing share objects and their states

State view showing share objects and their states

Alert view showing critical alerts on a weak share permission condition

Alert view showing critical alerts on a weak share permission condition

In the following sections, I'd like to explain the management pack components roughly and the logic behind them.

Reading file share permissions ^

The following script helps gather all available shares together with their share and NTFS permissions and stores them in a list. It omits default shares.

To ensure the management pack also works with Windows Server 2008, the code is compliant with PowerShell version 2.

#region Initializing_ObjectDiscovery_InSCOM
param($sourceId,$managedEntityId,$discoveryItem,$ComputerName)

$api           = New-Object -ComObject 'MOM.ScriptAPI'
$discoveryData = $api.CreateDiscoveryData(0, $sourceId, $managedEntityId)
#endregion Initializin_ObjectDiscovery_InSCOM

#get all shares
$shares    = Get-WmiObject -Class Win32_Share
$shareList = New-Object -TypeName System.Collections.ArrayList
foreach ($share in $shares) {

  #excluding default shares
  if (($share.Name -notmatch '(?im)^[a-z]{1,1}\$') -and ($share.Name -notmatch '(?im)^[admin]{5,5}\$') -and ($share.Name -notmatch '(?im)^[ipc]{3,3}\$') -and ($share.Name -notmatch '(?im)^[print]{5,5}\$') )  {

    $shareAccessInfo = ''
    $ntfsAccessInfo  = ''

    #extract permissions from the current share
    $fileAccessControlList = Get-Acl -Path $($share.Path) | Select-Object -ExpandProperty Access | Select-Object -Property FileSystemRights, AccessControlType, IdentityReference

    #excluding uncritical information such as Built-in Accounts, Administrators, System, the NT Service, and Trusted installer
    foreach ($fileAccessControlEntry in $fileAccessControlList) {
      if (($fileAccessControlEntry.FileSystemRights -notmatch '\d') -and ($fileAccessControlEntry.IdentityReference -notmatch '(?i)Builtin\\Administrators|NT\sAUTHORITY\\SYSTEM|NT\sSERVICE\\TrustedInstaller')) {
        $ntfsAccessInfo += "$($fileAccessControlEntry.IdentityReference); $($fileAccessControlEntry.AccessControlType); $($fileAccessControlEntry.FileSystemRights)" + ' | '
      }
    } #END foreach ($fileAccessControlEntry in $fileAccessControlList)
    $ntfsAccessInfo = $ntfsAccessInfo.Substring(0,$ntfsAccessInfo.Length - 3)
    $ntfsAccessInfo = $ntfsAccessInfo -replace ',\s?Synchronize',''

    #getting share permissions
    $shareSecuritySetting    = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name='$($share.Name)'"
    $shareSecurityDescriptor = $shareSecuritySetting.GetSecurityDescriptor()
    $shareAccessControlList = $shareSecurityDescriptor.Descriptor.DACL

    #converting share permissions to be human readable
    foreach($shareAccessControlEntry in $shareAccessControlList) {

      $trustee    = $($shareAccessControlEntry.Trustee).Name
      $accessMask = $shareAccessControlEntry.AccessMask

      if($shareAccessControlEntry.AceType -eq 0) {
        $accessType = 'Allow'
      } else {
        $accessType = 'Deny'
      }

      if ($accessMask -match '2032127|1245631|1179817') {
        if ($accessMask -eq 2032127) {
          $accessMaskInfo = 'FullControl'
        } elseif ($accessMask -eq 1179817) {
          $accessMaskInfo = 'Read'
        } elseif ($accessMask -eq 1245631) {
          $accessMaskInfo = 'Change'
        } else {
          $accessMaskInfo = 'unknown'
        }
        $shareAccessInfo += "$trustee; $accessType; $accessMaskInfo" + ' | '
      }

    } #END foreach($shareAccessControlEntry in $shareAccessControlList)

    if ($shareAccessInfo -match '|') {
      $shareAccessInfo = $shareAccessInfo.Substring(0,$shareAccessInfo.Length - 3)
    }

    #putting extracted information together into a custom object
    $myShareHash = @{'Name'=$share.Name}
    $myShareHash.Add('FileSystemSPath',$share.Path )
    $myShareHash.Add('Description',$share.Description)
    $myShareHash.Add('NTFSPermissions',$ntfsAccessInfo)
    $myShareHash.Add('SharePermissions',$shareAccessInfo)
    $myShareObject = New-Object -TypeName PSObject -Property $myShareHash
    $myShareObject.PSObject.TypeNames.Insert(0,'MyShareObject')

    #store the custom object in a list
    $null = $shareList.Add($myShareObject)

  } #END if (($share.Name -notmatch '(?im)^[a-z]{1,1}\$') -and ($share.Name -notmatch '(?im)^[admin]{5,5}\$') -and ($share.Name -notmatch '(?im)^[ipc]{3,3}\$') )
} #END foreach ($share in $shares)

#region Passing_DiscoveredObject_ToSCOM
foreach ($shareItem in $shareList) {
	
      $Key         = $ComputerName + '-' + $($shareItem.Name)
      $displayName = 'Share ' + $($shareItem.Name) + ' On ' + $ComputerName

      $instance = $discoveryData.CreateClassInstance("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']$")
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/ComputerName$",$ComputerName)
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/Key$",$Key)	
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/Name$",$shareItem.Name)
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/FileSystemPath$",$shareItem.FileSystemPath)		
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/Description$",$shareItem.Description)				
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/NTFSPermissions$",$shareItem.NTFSPermissions)							
      $instance.AddProperty("$MPElement[Name='ABC.Windows.Server.AdminInfo.Share']/SharePermissions$",$shareItem.SharePermissions)
      $instance.AddProperty("$MPElement[Name='System!System.Entity']/DisplayName$", $displayName)
      $discoveryData.AddInstance($instance)	
	
 } #END foreach ($shareItem in $shareList)		

$discoveryData
#endregion Passing_DiscoveredObject_ToSCOM

Finding weak share permissions ^

Below is the code that reads the share information already in SCOM's inventory.

The logic that judges the health state of the specific share object goes as follows: If the NTFS permissions are "Allow Modify" or "Allow Full Control" for either Everyone or Authenticated Users or Builtin\Users then it assigns "Warning State."

In addition, if the share permissions also match the pattern "Allow Modify" or "Allow Full Control" for either Everyone or Authenticated Users or Builtin\Users , it will raise the "Error State" and a critical alert.

$api      = New-Object -ComObject 'MOM.ScriptAPI'
$testedAt = "Tested on: $(Get-Date -Format u) / $(([TimeZoneInfo]::Local).DisplayName)"
#retrieving all discovered share objects from the SCOM inventory
$classAdminInfoShare          = Get-SCOMClass -Name 'ABC.Windows.Server.AdminInfo.Share'
$classAdminInfoShareInstances = Get-SCOMClassInstance -Class $classAdminInfoShare		
		
foreach ($adminInfoShare in $classAdminInfoShareInstances) {
	
  #extracting information for convenience in further processing
  $Key              = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].Key'.Value
  $ComputerName     = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].ComputerName'.Value
  $Name             = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].Name'.Value
  $FSPath           = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].FileSystemPath'.Value
  $Description      = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].Description'.Value
  $NTFSPermissions  = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].NTFSPermissions'.Value		
  $sharePermissions = $adminInfoShare.'[ABC.Windows.Server.AdminInfo.Share].SharePermissions'.Value
  $state            = ''
  $regPat           = '(Everyone|BUILTIN\\Users|Authenticated\sUsers);\s?Allow;[a-zA-Z,\s]{1,}?(Modify|Change|FullControl)'
  	
  #checking if NTFS permissions match 'weak pattern'; if so, Warning State	
  if ($NTFSPermissions -match $regPat) {
    $state       = 'Yellow'			
    $alertInfo   = 'Pontential risky permission found. Please correct.'
    #checking if also Share permissions match 'weak pattern'; if so, Critical State
    if ($sharePermissions -match $regPat) {
      $state     = 'Red'
      $alertInfo = 'Dangerous permission found. Please correct ASAP.'
    }			
  } else {
    #if not matching 'weak pattern' it's a healthy state
    $state = 'Green'
  } #END if ($NTFSPermissions -match $regPat)		
  #adding useful information showing Health Explorer and in the Alert
  $supplement = " Share: $($Name) / $($FSPath) `n NTFS Permission: $($NTFSPermissions) `n Share Permissions: $($sharePermissions)`n Alert Info: $($alertInfo)"
	
  #sending property back to SCOM	
  $bag = $api.CreatePropertybag()					
  $bag.AddValue("Key",$Key)
  $bag.AddValue("Name",$Name)		
  $bag.AddValue("State",$state)				
  $bag.AddValue("Supplement",$supplement)		
  $bag.AddValue("TestedAt",$testedAt)			
  $bag	
} #END foreach ($adminInfoShare in $classAdminInfoShareInstances)

Management pack components ^

Classes

Everything in SCOM that has a health state is an object. Instead of targeting all Windows servers directly and changing their health states (green/yellow/red) directly according to the share information found with that management pack, I decided to create a dedicated computer class named ABC.Windows.Server.AdminInfo.Server. The idea behind this is that the computer is still running well even if only a share is misconfigured.

<ClassType ID="ABC.Windows.Server.AdminInfo.Server" Base="Windows!Microsoft.Windows.ComputerRole" Accessibility="Internal" Abstract="false" Hosted="true" Singleton="false">
    <Property ID="ComputerName" Type="string" AutoIncrement="false" Key="true" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
</ClassType>

For the shares a dedicated class is required as well. But if you have a dedicated class, objects can have a health state you can monitor.

<ClassType ID="ABC.Windows.Server.AdminInfo.Share" Accessibility="Public" Abstract="false" Base="System!System.LogicalEntity" Hosted="false" Singleton="false" Extension="false">
    <Property ID="Key" Type="string" AutoIncrement="false" Key="true" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
    <Property ID="ComputerName" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="256" MinLength="0" Required="false" Scale="0" />
    <Property ID="Name" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="4096" MinLength="0" Required="false" Scale="0" />
    <Property ID="FileSystemPath" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="4096" MinLength="0" Required="false" Scale="0" />
    <Property ID="Description" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="32768" MinLength="0" Required="false" Scale="0" />
    <Property ID="NTFSPermissions" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="32768" MinLength="0" Required="false" Scale="0" />
    <Property ID="SharePermissions" Type="string" AutoIncrement="false" Key="false" CaseSensitive="false" MaxLength="32768" MinLength="0" Required="false" Scale="0" />
</ClassType>

Discoveries

The mechanism that creates instances of an object that corresponds to the class definition and that stores them in the SCOM database is called a discovery. There are different types of discoveries, starting from matching registry values over results of a WMI query to scripts that can cover everything. Targets define on which component the discovery should run.

The first discovery ABC.Windows.Server.AdminInfo.Discovery.AdminInfo.Server finds "…AdminInfo.Server" objects. It targets all Windows servers (that SCOM already monitors). FilteredRegistryDiscoveryProvide scans the registry, and if the key HKLM\SOFTWARE\Microsoft exists, it will create the object. The interval is daily.

The second discovery ABC.Windows.Server.AdminInfo.Discovery.AdminInfo.Share finds shares and gathers some parameters. It targets the previously discovered …AdminInfo.Server computer objects. TimedPowerShell.DiscoveryProvider triggers the DiscoverAdminInfoItems.ps1 PowerShell script, which does the logic (see above: Finding Shares and retrieving their permissions). The interval is hourly.

Monitors

Monitors are for finding out which health state an object has. By default, monitors do not meet the requirement; thus, I created a dedicated one. ABC.AdminInfo.ThreeState.Test.MonitorType targets all objects of the class ABC.Windows.Server.AdminInfo.Share.

This monitor here uses PowerShell to determine the state of the share objects. (See above: Checking shares and alerting if weak permissions are set).

The interval is quarterly.

Views

To make all discovered shares and their health states visible, you can use a state view Share State. This shows the most important properties. Shares that meet the error criteria will raise a critical alert. The alert view Share Alerts will show those alerts.

You can find both views in a folder named ABC.Windows.Server.AdminInfo.Folders.

Conclusion ^

You can download the management pack with the extensions .xml or .mpb. I published the software under the GNU General Public License. Feel free to use it without costs or obligations. The software is provided "as is" without express or implied warranty.

If you don't like the naming used, feel free to change the text in the XML file. Make sure you search with case sensitivity. I used Visual Studio 2015 with Authoring Extensions for this management pack. Feel free to use the sources I published on Github.

Subscribe to 4sysops newsletter!

Further Reading ^

You can find a technical deep drive about the concepts of management packs in the System Center Management Pack Authoring Guide. Brian Wren, Principal Knowledge Engineer at Microsoft teaches in 23 episodes on Channel 9 how to author management packs with Visual Studio Authoring Extensions (Download VSAE).

0 Comments

Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account