# # --------------------- # XSD SCHEMA DEFINITION # --------------------- # # # ----------------------------- # SCHEMATRON RULES DEFINITION # ----------------------------- # # # ----------------------------- # TRANSLATIONS DEFINITION # ----------------------------- # data _system_translations { ConvertFrom-StringData @' # ultimate fallback text # # Execute/Script and Write for handler # ExecuteWritePermissionsCheck_Title=Grant a handler execute/script or write permissions, but not both ExecuteWritePermissionsCheck_Problem=The attribute 'accessPolicy' in the handlers section under path '{0}' is set to allow both Execute/Script and Write permissions. ExecuteWritePermissionsCheck_Impact=By allowing both Execute/Script and Write permissions, a handler can run malicious code on the target server. ExecuteWritePermissionsCheck_Resolution=Determine if the handler requires both Execute/Script and Write permissions, and revoke the one that is not needed. ExecuteWritePermissionsCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. # # Expired Certificates # ExpiredCertificatesCheck_Title=Make sure that your certificates are current ExpiredCertificatesCheck_Problem=SSL Binding '{0}:{1}:' has a certificate that has expired, or will expire in 30 days. The certificate has thumbprint '{2}' and is located in store '{3}'. ExpiredCertificatesCheck_Impact=An expired certificate becomes invalid and can prevent users from accessing your site. ExpiredCertificatesCheck_Resolution=Renew the certificate or choose a new certificate for the site. ExpiredCertificatesCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. # # notListedIsapisAllowed should not be true # NotListedISAPIsAllowedCheck_Title=The configuration attribute 'notListedIsapisAllowed' should be false NotListedISAPIsAllowedCheck_Problem=The configuration attribute 'notListedIsapisAllowed' in section 'system.webServer/security/isapiCgiRestriction' is set to true. NotListedISAPIsAllowedCheck_Impact=Any unlisted ISAPI extension, including potentially malicious extensions, will be allowed to run. NotListedISAPIsAllowedCheck_Resolution=Set 'notListedIsapisAllowed' to false and add each ISAPI extension to the list of allowed extensions. NotListedISAPIsAllowedCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. # # notListedCgisAllowed should not be true # NotListedCGIsAllowedCheck_Title=The configuration attribute 'notListedCgisAllowed' should be false NotListedCGIsAllowedCheck_Problem=The configuration attribute 'notListedCgisAllowed' in section 'system.webServer/security/isapiCgiRestriction' is set to true. NotListedCGIsAllowedCheck_Impact=Any unlisted CGI extension, including potentially malicious extensions, will be allowed to run. NotListedCGIsAllowedCheck_Resolution=Set 'notListedCgisAllowed' to false and add each CGI extension to the allowed list. NotListedCGIsAllowedCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. # # Application Pool should not be priviliged # AppPoolIdentityCheck_Title=Application pools should be set to run as application pool identities AppPoolIdentityCheck_Problem=Application pool '{0}' is set to run as an administrator, as local system, or to 'Act as part of the operating system'. AppPoolIdentityCheck_Impact=The application pool can execute high-privileged code, including potentially malicious code that can negatively affect your server. AppPoolIdentityCheck_Resolution=Set the application pool to run as the application pool identity. AppPoolIdentityCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. # # Custom errors should be set to LocalOnly or Custom # CustomErrorsCheck_Title=Hide Custom Errors from displaying remotely CustomErrorsCheck_Problem=The errorMode attribute of section '{0}' [path:{1}] is set to Detailed. CustomErrorsCheck_Impact=Users browsing to your site or application could see some privileged information that is contained in the detailed error pages being sent remotely. CustomErrorsCheck_Resolution=Set the Custom Errors 'errorMode' to 'DetailedLocalOnly' or 'Custom'. CustomErrorsCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. # # Basic Authentication should not be used without SSL # BasicAuthSSLCheck_Title=Use SSL when you use Basic authentication BasicAuthSSLCheck_Problem=Basic authentication is enabled for configuration path '{0}' but it lacks a required SSL binding. BasicAuthSSLCheck_Impact=If you use Basic authentication without SSL, credentials will be sent in clear text that might be intercepted by malicious code. BasicAuthSSLCheck_Resolution=Use Basic authentication with an SSL binding, and make sure that the site or application is set to require SSL. Alternatively, use a different method of authentication. BasicAuthSSLCheck_Compliant=The IIS Best Practices Analyzer scan has determined that you are in compliance with this best practice. '@ } Import-LocalizedData -BindingVariable _system_translations -filename WebServer.psd1 # # ------------------ # FUNCTIONS - START # ------------------ # # # Function Description: # Creates the Document element for the Xml Document # # Arguments: # $ns - Namespace Name # $name - Name of the document element # # Return Value: # returns the created document element # function Create-DocumentElement( $ns, $name ) { [xml] "<$name xmlns='$ns'>" } # # Function Description: # # This function will add the Server Manager module so that Roles # can be queried # # Arguments: # # None # # Return Value: # # None # function RoleQueryInitialize { Import-Module ServerManager } # # Function Description: # # This function will remove the Server Manager module after the Roles # have been queried # # Arguments: # # None # # Return Value: # # None # function RoleQueryShutdown { Remove-Module ServerManager } # # Function Description: # # This function will check to see if the specified role is installed # # Arguments: # # $roleId - Id of the Role # # Return Value: # # $true - If Role is Installed # $false - If Role is not Installed # function IsRoleInstalled ( $roleId ) { $roleInstalled = $false # # Use the Server Manager CmdLet to obtain detail about Role # $Role = Get-WindowsFeature $roleId if ( $Role -ne $null ) { $roleInstalled = $Role.Installed } $roleInstalled } function getExecuteAndWritePermissionHandlers { $objects = @() $objects += get-webconfiguration '/system.webServer/handlers[(contains(@accessPolicy, "Script") and contains(@accessPolicy, "Write")) or (contains(@accessPolicy, "Execute") and contains(@accessPolicy, "Write"))]' -recurse if ($objects -ne $null) { @" $( foreach ($o in $objects) { $xpathQuery = $o.ItemXPath $commitPath = $o.PSPath @"
$xpathQuery $commitPath true
"@ } )
"@ } else { @"
false
"@ } } function getExpiredCertificates { $result = @() $sslBind = get-item iis:\sslbindings\* foreach ($b in $sslBind) { if ($b.Sites -eq $null -or $b.Sites.Count -eq 0) {continue} $p = join-path "cert:\LocalMachine" $b.Store $p = join-path $p $b.Thumbprint $cert = get-item $p if ($cert.NotAfter -le ([DateTime]::Now).AddDays(30)) { $result += $b } } if ($result -ne $null) { @" $( foreach ($b in $result) { $ip = $b.IPAddress $port = $b.port $hash = $b.Thumbprint $store = $b.Store @" $ip $port $hash $store true "@ } ) "@ } else { @" false "@ } } function getNonListedIsapiAllowed { $moduleInstalled = get-webconfiguration "/system.webServer/globalModules/add[@name='IsapiModule']" MACHINE/WEBROOT/APPHOST $moduleEnabled = get-webconfiguration "/system.webServer/modules/add[@name='IsapiModule']" MACHINE/WEBROOT/APPHOST -recurse if ($moduleInstalled -ne $null -and $moduleEnabled -ne $null) { $test = (get-webconfiguration /system.webServer/security/isapiCgiRestriction/@notListedIsapisAllowed MACHINE/WEBROOT/APPHOST).Value.ToString().ToLower() @" $test "@ } } function getNonListedCgiAllowed { $moduleInstalled = get-webconfiguration "/system.webServer/globalModules/add[@name='CgiModule']" MACHINE/WEBROOT/APPHOST $moduleEnabled = get-webconfiguration "/system.webServer/modules/add[@name='CgiModule']" MACHINE/WEBROOT/APPHOST -recurse if ($moduleInstalled -ne $null -and $moduleEnabled -ne $null) { $test = (get-webconfiguration /system.webServer/security/isapiCgiRestriction/@notListedCgisAllowed MACHINE/WEBROOT/APPHOST).Value.ToString().ToLower() @" $test "@ } } function getAppPoolPrivilegedIdentity { $ad = [adsi] "WinNT://$((get-item env:\computername).Value),computer" $admins = $ad.Children.Find("Administrators","group") $badPools = @() $sysPools = get-webconfiguration '/system.applicationHost/applicationPools/add/processModel[compare-string-ordinal(@identityType, "LocalSystem",false())=0]/parent::node()' -pspath MACHINE/WEBROOT/APPHOST if ($sysPools -ne $null) { $badPools += $sysPools } $servicePools = get-webconfiguration '/system.applicationHost/applicationPools/add/processModel[compare-string-ordinal(@identityType, "LocalService",false())=0]/parent::node()' -pspath MACHINE/WEBROOT/APPHOST if ($servicePools -ne $null) { $priv = [Microsoft.IIS.Powershell.Framework.LSAUtility]::GetUserRights("LocalService") if ($priv -contains 'SeTcbPrivilege') { $badPools += $servicePools } } $servicePools = get-webconfiguration '/system.applicationHost/applicationPools/add/processModel[compare-string-ordinal(@identityType, "NetworkService",false())=0]/parent::node()' -pspath MACHINE/WEBROOT/APPHOST if ($servicePools -ne $null) { $priv = [Microsoft.IIS.Powershell.Framework.LSAUtility]::GetUserRights("NetworkService") if ($priv -contains 'SeTcbPrivilege') { $badPools += $servicePools } } $specificUserPools = get-webconfiguration '/system.applicationHost/applicationPools/add/processModel[compare-string-ordinal(@identityType, "SpecificUser",false())=0]/parent::node()' -pspath MACHINE/WEBROOT/APPHOST foreach ($pool in $specificUserPools) { if ($pool -eq $null) {continue} trap { continue } $userName = $pool.processModel.userName if ($userName.Length -gt 0) { $adUser = $ad.Children.Find($userName,"user") $isAdmin = $admins.IsMember($adUser.Path) if ($isAdmin -eq $true) { $badPools += $pool } else { $priv = [Microsoft.IIS.Powershell.Framework.LSAUtility]::GetUserRights($userName) if ($priv -contains 'SeTcbPrivilege') { $badPools += $pool } } } } if ($badPools -ne $null) { @" $( foreach ($o in $badPools) { $name = $o.name @" $name true "@ } ) "@ } else { @" false "@ } } function getCustomErrors { $moduleInstalled = get-webconfiguration "/system.webServer/modules/add[@name='CustomErrorModule']" MACHINE/WEBROOT/APPHOST if ($moduleInstalled -ne $null) { $badSections = @() $badSections = get-webconfiguration "/system.webServer/httpErrors[@errorMode != '' and (compare-string-ordinal(@errorMode, 'DetailedLocalOnly', false())!=0 and compare-string-ordinal(@errorMode, 'Custom', false())!=0)]" MACHINE/WEBROOT/APPHOST -recurse if ($badSections -ne $null) { @" $( foreach ($o in $badSections) { $xpathQuery = $o.ItemXPath $commitPath = $o.PSPath @"
$xpathQuery $commitPath false
"@ } )
"@ } else { @"
true
"@ } } } function getBasicAuthSSL { $moduleInstalled = get-webconfiguration "/system.webServer/globalModules/add[@name='BasicAuthenticationModule']" MACHINE/WEBROOT/APPHOST $moduleEnabled = get-webconfiguration "/system.webServer/modules/add[@name='BasicAuthenticationModule']" MACHINE/WEBROOT/APPHOST -recurse if (($moduleInstalled -ne $null) -and ($moduleEnabled -ne $null)) { $result = @() $enabledSites = @() $enabledSites = get-webconfiguration "/system.webServer/security/authentication/basicAuthentication[@enabled='true']" MACHINE/WEBROOT/APPHOST -recurse if ($enabledSites -ne $null) { foreach ($s in $enabledSites) { $p = $s.PSPath if ($s.Location -ne $null -and $s.Location -ne "") { $p += "/" + $s.Location } $flags = get-webconfiguration "/system.webServer/security/access/@sslFlags" $p if ( $flags -ne $null ) { try { $arr = $flags.Split(",") } catch { } if ($arr -contains "Ssl") {continue} $result += $s } } } if ($result -ne $null) { @" $( foreach ($o in $result) { $name = $o.PSPath + "/" + $o.Location @"
$name true
"@ } )
"@ } else { @"
false
"@ } } } function main($document) { $execWriteHandlersText = getExecuteAndWritePermissionHandlers if ($execWriteHandlersText.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($execWriteHandlersText) } $poolPrivText = getAppPoolPrivilegedIdentity if ($poolPrivText.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($poolPrivText) } $isapis = getNonListedIsapiAllowed if ($isapis.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($isapis) } $cgis = getNonListedCgiAllowed if ($cgis.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($cgis) } $errorMode = getCustomErrors if ($errorMode.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($errorMode) } $expiredCerts = getExpiredCertificates if ($expiredCerts.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($expiredCerts) } $authSsl = getBasicAuthSSL if ($authSsl.Length -gt 0) { $document.DocumentElement.CreateNavigator().AppendChild($authSsl) } } # # ------------------ # FUNCTIONS - END # ------------------ # # # ------------------------ # SCRIPT MAIN BODY - START # ------------------------ # # # Initialize to perform querying Role information # RoleQueryInitialize $WSWebInstalled = IsRoleInstalled "Web-WebServer" # # Role Information obtained. # RoleQueryShutdown # # Set the Target Namespace to be used by XML # $tns="http://schemas.microsoft.com/bestpractices/models/ServerManager/WebServer/WebServerComposite/2008/04" # # Create a new XmlDocument # $doc = Create-DocumentElement $tns "WebServerComposite" import-module WebAdministration # # If WSWebServer installed, we need to discover data related to that # if ( $WSWebInstalled -eq $true ) { main $doc } $doc # # ------------------------ # SCRIPT MAIN BODY - END # ------------------------ #