Skip to content

Commit

Permalink
Merge pull request #412 from Snozzberries/cis
Browse files Browse the repository at this point in the history
First CIS test
  • Loading branch information
merill authored Aug 27, 2024
2 parents ab1fc05 + 278d8e0 commit 33a6fb6
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 29 deletions.
3 changes: 2 additions & 1 deletion powershell/Maester.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ FunctionsToExport = 'Add-MtTestResultDetail', 'Clear-MtGraphCache', 'Connect-Mae
'Test-MtCaLicenseUtilization', 'Test-MtCaMfaForAdmin',
'Test-MtCaMfaForAdminManagement', 'Test-MtCaMfaForAllUsers',
"Test-MtCaGroupsRestricted",
"Test-MtCaGaps",
"Test-MtCaGap",
'Test-MtCaMfaForGuest', 'Test-MtCaMfaForRiskySignIn',
'Test-MtCaRequirePasswordChangeForHighUserRisk',
'Test-MtCaSecureSecurityInfoRegistration', 'Test-MtCisaDiagnosticSettings',
Expand Down Expand Up @@ -131,6 +131,7 @@ FunctionsToExport = 'Add-MtTestResultDetail', 'Clear-MtGraphCache', 'Connect-Mae
'Test-MtCisaSafeLinkClickTracking', 'Test-MtCisaExoAlert', 'Test-MtCisaExoAlertSiem',
'Test-MtCisaAuditLog', 'Test-MtCisaAuditLogPremium', 'Test-MtCisaAuditLogRetention',
'Get-MtExo', 'Clear-MtExoCache',
'Test-MtCisCloudAdmin',
'Test-MtConditionalAccessWhatIf',
'Test-MtConnection',
'Test-MtEidscaControl',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
<#
.Synopsis
This function checks if all objects found in policy exclusions are found in policy inclusions.
This function compares to object arrays
.Description
Checks for gaps in conditional access policies, by looking for excluded objects which are not specifically inlcuded
in another conditional access policy. Instead of looking at the historical sign-ins to find gaps, we try to spot possibly
overlooked exclusions which do not have a fallback.
Reference:
https://learn.microsoft.com/en-us/entra/identity/monitoring-health/workbook-conditional-access-gap-analyzer
Provides the differences in objects between two arrays of objects.
.Example
Test-MtCaGaps
Get-ObjectDifference
.LINK
https://maester.dev/docs/commands/Test-MtCaGaps
https://maester.dev/docs/commands/Get-ObjectDifference
#>
function Get-ObjectDifferences {
function Get-ObjectDifference {
[CmdletBinding()]
[OutputType([object[]])]
param (
[System.Collections.ArrayList]$excludedObjects,
[System.Collections.ArrayList]$includedObjects
Expand All @@ -38,8 +34,22 @@ function Get-ObjectDifferences {
return $objectDifferences
}

function Get-RalatedPolicies {
<#
.Synopsis
Provides MarkDown text for specific array of objects
.Description
Returns a structured MarkDown string resolving objects
.Example
Get-RelatedPolicy
.LINK
https://maester.dev/docs/commands/Get-RelatedPolicy
#>
function Get-RelatedPolicy {
[CmdletBinding()]
[OutputType([string])]
param (
[System.Collections.ArrayList]$Arr,
[String]$ObjName
Expand All @@ -57,7 +67,22 @@ function Get-RalatedPolicies {
return $result
}

function Test-MtCaGaps {
<#
.Synopsis
This function checks if all objects found in policy exclusions are found in policy inclusions.
.Description
Checks for gaps in conditional access policies, by looking for excluded objects which are not specifically inlcuded
in another conditional access policy. Instead of looking at the historical sign-ins to find gaps, we try to spot possibly
overlooked exclusions which do not have a fallback.
.Example
Test-MtCaGap
.LINK
https://maester.dev/docs/commands/Test-MtCaGap
#>
function Test-MtCaGap {
[CmdletBinding()]
[OutputType([bool])]
param ()
Expand Down Expand Up @@ -138,14 +163,14 @@ function Test-MtCaGaps {
Write-Verbose "Created a mapping with all excluded objects for each policy:`n $mapping"

# Find which objects are excluded without a fallback
[System.Collections.ArrayList]$differencesUsers = @(Get-ObjectDifferences -excludedObjects $excludedUsers -includedObjects $includedUsers)
[System.Collections.ArrayList]$differencesGroups = @(Get-ObjectDifferences -excludedObjects $excludedGroups -includedObjects $includedGroups)
[System.Collections.ArrayList]$differencesRoles = @(Get-ObjectDifferences -excludedObjects $excludedRoles -includedObjects $includedRoles)
[System.Collections.ArrayList]$differencesApplications = @(Get-ObjectDifferences -excludedObjects $excludedApplications -includedObjects $includedApplications)
[System.Collections.ArrayList]$differencesServicePrincipals = @(Get-ObjectDifferences -excludedObjects $excludedServicePrincipals -includedObjects $includedServicePrincipals)
[System.Collections.ArrayList]$differencesLocations = @(Get-ObjectDifferences -excludedObjects $excludedLocations -includedObjects $includedLocations)
[System.Collections.ArrayList]$differencesPlatforms = @(Get-ObjectDifferences -excludedObjects $excludedPlatforms -includedObjects $includedPlatforms)
Write-Host "Finished searching for gaps in policies."
[System.Collections.ArrayList]$differencesUsers = @(Get-ObjectDifference -excludedObjects $excludedUsers -includedObjects $includedUsers)
[System.Collections.ArrayList]$differencesGroups = @(Get-ObjectDifference -excludedObjects $excludedGroups -includedObjects $includedGroups)
[System.Collections.ArrayList]$differencesRoles = @(Get-ObjectDifference -excludedObjects $excludedRoles -includedObjects $includedRoles)
[System.Collections.ArrayList]$differencesApplications = @(Get-ObjectDifference -excludedObjects $excludedApplications -includedObjects $includedApplications)
[System.Collections.ArrayList]$differencesServicePrincipals = @(Get-ObjectDifference -excludedObjects $excludedServicePrincipals -includedObjects $includedServicePrincipals)
[System.Collections.ArrayList]$differencesLocations = @(Get-ObjectDifference -excludedObjects $excludedLocations -includedObjects $includedLocations)
[System.Collections.ArrayList]$differencesPlatforms = @(Get-ObjectDifference -excludedObjects $excludedPlatforms -includedObjects $includedPlatforms)
Write-Verbose "Finished searching for gaps in policies."

# Check if all excluded objects have fallbacks
if (
Expand All @@ -167,55 +192,55 @@ function Test-MtCaGaps {
$testResult = "The following user objects did not have a fallback:`n`n"
$differencesUsers | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add group objects to results
if ($differencesGroups.Count -ne 0) {
$testResult += "The following group objects did not have a fallback:`n`n"
$differencesGroups | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add role objects to results
if ($differencesRoles.Count -ne 0) {
$testResult += "The following role objects did not have a fallback:`n`n"
$differencesRoles | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add application objects to results
if ($differencesApplications.Count -ne 0) {
$testResult += "The following application objects did not have a fallback:`n`n"
$differencesApplications | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add service principal objects to results
if ($differencesServicePrincipals.Count -ne 0) {
$testResult += "The following service principal objects did not have a fallback:`n`n"
$differencesServicePrincipals | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add location objects to results
if ($differencesLocations.Count -ne 0) {
$testResult += "The following location objects did not have a fallback:`n`n"
$differencesLocations | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
# Add platform objects to results
if ($differencesPlatforms.Count -ne 0) {
$testResult += "The following platform objects did not have a fallback:`n`n"
$differencesPlatforms | ForEach-Object {
$testResult += " - $_`n`n"
$testResult += Get-RalatedPolicies -Arr $mappingArray -ObjName $_
$testResult += Get-RelatedPolicy -Arr $mappingArray -ObjName $_
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions powershell/public/cis/Test-MtCisCloudAdmin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
1.1.1 (L1) Ensure Administrative accounts are separate and cloud-only

Administrative accounts are special privileged accounts that could have varying levels of access to data, users, and settings. Regular user accounts should never be utilized for administrative tasks and care should be taken, in the case of a hybrid environment, to keep Administrative accounts separated from on-prem accounts. Administrative accounts should not have applications assigned so that they have no access to potentially vulnerable services (EX. email, Teams, SharePoint, etc.) and only access to perform tasks as needed for administrative purposes.

#### Remediation action:

To created licensed, separate Administrative accounts for Administrative users:

1. Navigate to **Microsoft 365 admin center**.
2. Click to expand **Users** select **Active users**
3. Click **Add a user**.
4. Fill out the appropriate fields for Name, user, etc.
5. When prompted to assign licenses select as needed **Microsoft Entra ID P1** or
**Microsoft Entra ID P2**, then click **Next**.
6. Under the **Option settings** screen you may choose from several types of
Administrative access roles. Choose **Admin center access** followed by the
appropriate role then click **Next**.
7. Select **Finish adding**.

#### Related links

* [Microsoft 365 Admin Center](https://admin.microsoft.com)
* [CIS Microsoft 365 Foundations Benchmark v3.1.0 - Page 16](https://www.cisecurity.org/benchmark/microsoft_365)

<!--- Results --->
%TestResult%
78 changes: 78 additions & 0 deletions powershell/public/cis/Test-MtCisCloudAdmin.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<#
.SYNOPSIS
Checks if Global Admins are cloud users
.DESCRIPTION
Ensure Administrative accounts are separate and cloud-only
CIS Microsoft 365 Foundations Benchmark v3.1.0
.EXAMPLE
Test-MtCisCloudAdmin
Returns true if no global admins are hybrid sync
.LINK
https://maester.dev/docs/commands/Test-MtCisCloudAdmin
#>
function Test-MtCisCloudAdmin {
[CmdletBinding()]
[OutputType([bool])]
param()

Write-Verbose "Getting Global Admin role"
$role = Get-MtRole | Where-Object {`
$_.id -eq "62e90394-69f5-4237-9190-012177145e10" } # Global Administrator

Write-Verbose "Getting role members"
$assignments = Get-MtRoleMember -roleId $role.id

Write-Verbose "Filtering for users"
$globalAdministrators = $assignments | Where-Object {`
$_.'@odata.type' -eq "#microsoft.graph.user"
}

$userIds = @($globalAdministrators.Id)

Write-Verbose "Requesting users onPremisesSyncEnabled property"
$users = Invoke-MtGraphRequest -RelativeUri "users" -UniqueId $userIds -Select id,displayName,onPremisesSyncEnabled

Write-Verbose "Filtering users for onPremisesSyncEnabled"
$result = $users | Where-Object {`
$_.onPremisesSyncEnabled -eq $true
}

$testResult = ($result|Measure-Object).Count -eq 0

$sortSplat = @{
Property = @(
@{
Expression = "onPremisesSyncEnabled"
Descending = $true
},
@{
Expression = "displayName"
}
)
}

if ($testResult) {
$testResultMarkdown = "Well done. Your tenant has no hybrid Global Administrators:`n`n%TestResult%"
} else {
$testResultMarkdown = "Your tenant has 1 or more hybrid Global Administrators:`n`n%TestResult%"
}

$resultMd = "| Display Name | Cloud Only |`n"
$resultMd += "| --- | --- |`n"
foreach($item in $users | Sort-Object @sortSplat){
$itemResult = "❌ Fail"
if($item.id -notin $result.id){
$itemResult = "✅ Pass"
}
$resultMd += "| $($item.displayName) | $($itemResult) |`n"
}
$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $resultMd

Add-MtTestResultDetail -Result $testResultMarkdown

return $testResult
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
Test-MtCaGroupsRestricted | Should -Be $true -Because "there is one or more policy without protection of included or excluded groups"
}
It "MT.1036: All excluded objects should have a fallback include in another policy. See https://maester.dev/docs/tests/MT.1036" -Tag "MT.1036", "Warning" {
Test-MtCaGaps | Should -Be $true -Because "there is one ore more object excluded without an include fallback in another policy."
Test-MtCaGap | Should -Be $true -Because "there is one ore more object excluded without an include fallback in another policy."
}
Context "License utilization" {
It "MT.1022: All users utilizing a P1 license should be licensed. See https://maester.dev/docs/tests/MT.1022" -Tag "MT.1022" {
Expand Down
10 changes: 10 additions & 0 deletions tests/cis/Test-MtCisCloudAdmin.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Describe "CIS" -Tag "CIS 1.1.1", "CIS E3 Level 1", "CIS E3", "CIS", "Security", "All", "CIS M365 v3.1.0" {
It "CIS 1.1.1: Ensure Administrative accounts are separate and cloud-only" {

$result = Test-MtCisCloudAdmin

if($null -ne $result) {
$result | Should -Be $true -Because "admin accounts are separate and cloud-only"
}
}
}
26 changes: 26 additions & 0 deletions website/docs/tests/cis/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
id: overview
title: CIS Microsoft 365 Foundations Benchmark Tests
sidebar_label: 🏢 CIS Overview
description: Implementation of CIS Microsoft 365 Foundations Benchmark Controls
---

# CIS Microsoft 365 Foundations Benchmark

## Overview

The tests in this section verifies that a Micorosft 365 tenant's configuration conforms to the [CIS Microsoft 365 Foundations Benchmark](https://www.cisecurity.org/benchmark/microsoft_365) recommendations (v3.1.0).

The CIS published material is shared for these tests as it aligns with their licensing of [CC BY-NC-SA 4.0](https://www.cisecurity.org/terms-and-conditions-table-of-contents).

## Connecting to Azure, Exchange and other services

In order to run all the CIS tests, you need to install and connect to the Azure and Exchange Online modules.

See the [Installation guide](/docs/installation#optional-modules-and-permissions) for more information.

## Tests

| Cmdlet Name | CIS Recommendation ID |
| - | - |
| Test-MtCisCloudAdmin | CIS 1.1.1: Ensure Administrative accounts are separate and cloud-only |

0 comments on commit 33a6fb6

Please sign in to comment.