Ad 1.: ADMX import obsahuje odkaz na "Windows" namespace, je tedy potřeba importovat Windows.admx, což je defaultní šablona pro Windows (C:\Windows\PolicyDefinitions).
Ad 2.: Ani úspěšně importované šablony nefungují. Musel jsem použít import a nastavení pomocí OMA-URI:
Nejdřív se importují vlastní klíče - ideální je použít soubor SinclairMakeMeAdmin.admx z Githubu, ale je potřeba vyměnit všechny nálezy makemeadmin:MakeMeAdmin
za MakeMeAdmin
.
Poté je možné už nastavit konkrétní hodnoty v registrech, já jsem použil tyto (Name a Description jsem doplnil dle potřeby, Type je ve všech případech String):
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/AllowedEntities
Value: <enabled/><data id = "AllowedEntitiesMultiText" value = "%COMPUTERNAME%\MakeMeAdmin"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/AdminRightsTimeout
Value: <enabled/> <data id= "AdminRightsTimeoutValue" value ="10"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/RemoveOnLogout
Value: <enabled/> <data id= "RemoveOnLogoutValue" value ="1"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/LogElevatedProcesses
Value: <enabled/> <data id= "ProcessLoggingDropDown" value ="1"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/RequireAuthForPrivileges
Value: <enabled/> <data id= "RequireAuthForPrivilegesValue" value ="1"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/PromptForReason
Value: <enabled/> <data id= "PromptForReasonEnum" value ="2"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/MaximumReasonLength
Value: <enabled/> <data id= "MaximumReasonLengthValue" value ="333"/>
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/MakeMeAdmin~Policy~MakeMeAdmin/CannedReasons
Value: <enabled/><data id="CannedReasonsMultiText" value="Software Installation#xF000;System Configuration#xF000;Troubleshooting"/>
Pozor na oddělovač v případě MultiText, musí se použít správný znak.
Ad 3.: Problém je v nastavení CIS benchmark for Windows (L1) v části 2.2.2, kde se doporučuje nastavit user right "Access this computer from the network" na "Administrators, Remote Desktop Users".
Pokud se v Make Me Admin nastaví "RequireAuthForPrivileges", aplikace využívá pro autentifikaci Logon Type 3 (Network Logon) - uživatel ale není ve skupině Administrators, což selže.
Řešením je přidat právo "Access this computer from the network" i pro skupinu s MMA uživateli (např. %COMPUTERNAME%\MakeMeAdmin).
Ad 4.: V doménovém prostředí fungují vnořené skupiny (nested groups) hezky, ale v AAD prostředí to nejde.
Vzhledem k tomu, že pomocí Intune se spravují zejména mobilní zařízení, je asi vhodnější používat lokální skupiny. Onboarding provádím následujícím PowerShell skriptem, který vytvoří lokální skupinu a vloží do ní aktuálně přihlášeného uživatele:
# Define the group name
$groupName = "MakeMeAdmin"
# Get the currently logged-on user
$loggedOnUser = (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName).Split('\')[1]
# Check if the group exists
$groupExists = Get-LocalGroup | Where-Object Name -EQ $groupName
# Create the group if it doesn't exist
if (-not $groupExists) {
New-LocalGroup -Name $groupName -Description "Group for temporary admin rights"
}
# Check if the user is already a member of the group
$userIsMember = Get-LocalGroupMember -Group $groupName | Where-Object Name -EQ $loggedOnUser
# Add the user to the group if they're not already a member
if (-not $userIsMember) {
Add-LocalGroupMember -Group $groupName -Member $loggedOnUser
}
Pro odebrání uživatelů ze skupiny můžeme použít tento skript:
# Define the group name
$groupName = "MakeMeAdmin"
# Check if the group exists
$groupExists = Get-LocalGroup | Where-Object Name -EQ $groupName
# If the group exists, remove all its members
if ($groupExists) {
$groupMembers = Get-LocalGroupMember -Group $groupName
foreach ($member in $groupMembers) {
Remove-LocalGroupMember -Group $groupName -Member $member.SID
}
}
V případě, že v Intune využíváme AAD-joined zařízení, je situace o trochu složitější. AAD-joined zařízení totiž vůbec nemusí mít lokální uživatele. Pokud je deployment AAD-only, pak by to ještě šlo obejít tím, že AAD uživatel má formát uživatelského jména AzureAD\username. Pokud je ale AAD v hybridním módu a AAC Connect nedejbože synchronizuje víc domén, nepodařilo se mi najít způsob, jak pod SYSTEM získat uživatelské jméno přihlášeného uživatele ve správném tvaru (synchronizovaní uživatelé mají username ve formátu DOMAIN\username).
Řešením tak je použít Remediations. První načte jméno uživatele a zapíše ho na disk.
Detection:
$directoryPath = "C:\ProgramData\MakeMeAdmin"
$filePath = "$directoryPath\loggedOnUsername.txt"
# Check if the file exists
if (Test-Path $filePath) {
$loggedOnUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName
# Check if the file contains the correct username
if ((Get-Content $filePath) -eq $loggedOnUser) {
exit 0
} else {
exit 1
}
} else {
exit 1
}
Remediation:
$directoryPath = "C:\ProgramData\MakeMeAdmin"
$filePath = "$directoryPath\loggedOnUsername.txt"
$loggedOnUser = Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty UserName
# Create the directory if it doesn't exist
if (-not (Test-Path $directoryPath)) {
New-Item -Path $directoryPath -ItemType Directory
}
# Create or overwrite the file with the correct username
Set-Content -Path $filePath -Value $loggedOnUser
Složka ProgramData je defaultně skrytá a všichni uživatelé do ní mají READ přístup. Pokud si tam uživatel vytvoří složku, může do ní zapisovat. Tahle Remediation musí být spuštěna pod aktuálním uživatelem.
Druhá Remediation je už pak snadná:
Detection:
# Check for the existence of the file
if (-not (Test-Path "C:\ProgramData\MakeMeAdmin\loggedOnUsername.txt")) {
exit 1
}
# Read the user from the file
$userFQDN = Get-Content "C:\ProgramData\MakeMeAdmin\loggedOnUsername.txt"
# Check if group exists
if (-not (Get-LocalGroup -Name "MakeMeAdmin" -ErrorAction SilentlyContinue)) {
exit 1
}
# Check if the user is a member of the group
if (-not (Get-LocalGroupMember -Group "MakeMeAdmin" -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $userFQDN })) {
exit 1
}
exit 0
Remediation:
# Define Log function
function Write-Log {
param (
[Parameter(Mandatory=$true)]
[string]$Message
)
$logFile = "C:\ProgramData\MakeMeAdmin\MMA-Remediation.log"
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Add-Content -Path $logFile -Value "$timestamp - $Message"
}
# Check for the existence of the file
if (-not (Test-Path "C:\ProgramData\MakeMeAdmin\loggedOnUsername.txt")) {
Write-Log "Error: loggedOnUsername.txt not found."
exit 1
}
# Read the user from the file
$userFQDN = Get-Content "C:\ProgramData\MakeMeAdmin\loggedOnUsername.txt"
Write-Log "Extracted user from file: $userFQDN"
# Check if group exists
if (-not (Get-LocalGroup -Name "MakeMeAdmin" -ErrorAction SilentlyContinue)) {
# Create group
try {
New-LocalGroup -Name "MakeMeAdmin" -Description "User group for MakeMeAdmin purposes"
Write-Log "MakeMeAdmin group created."
} catch {
Write-Log "Error occurred creating MakeMeAdmin group: $_"
exit 1
}
} else {
Write-Log "MakeMeAdmin group already exists."
}
# Check if the user is a member of the group
if (-not (Get-LocalGroupMember -Group "MakeMeAdmin" -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq $userFQDN })) {
# Add user to the group
try {
Add-LocalGroupMember -Group "MakeMeAdmin" -Member $userFQDN
Write-Log "User $userFQDN added to MakeMeAdmin group."
} catch {
Write-Log "Error occurred adding user $userFQDN to MakeMeAdmin group: $_"
exit 1
}
} else {
Write-Log "User $userFQDN is already a member of MakeMeAdmin group."
}
Tahle už běží standardně pod SYSTEM. Obě Remediations můžou běžet každou hodinu.
Zbývá už jen nasadit poslední Remediation jako offboarding.
Detection:
# Check members of the 'MakeMeAdmin' local group
try {
$groupMembers = net localgroup MakeMeAdmin
# Filtering out the header, footer, and empty lines from the output
$filteredMembers = $groupMembers | Where-Object { $_ -and $_ -notlike "Command completed successfully." -and $_ -notlike "*Members*" -and $_ -notlike "----*" }
if (-not $filteredMembers) {
Write-Output "Compliant"
Exit 0
} else {
Write-Warning "Not Compliant"
Exit 1
}
} catch {
Write-Warning "Not Compliant"
Exit 1
}
Remediation:
Start-Transcript -Path $env:TEMP\ClearMakeMeAdminGroup.txt
try {
$groupMembers = net localgroup MakeMeAdmin
# Filtering out the header, footer, and other non-member lines from the output
$filteredMembers = $groupMembers | Where-Object {
$_ -and
$_ -notlike "Command completed successfully." -and
$_ -notlike "*Members*" -and
$_ -notlike "----*" -and
$_ -notlike "Alias name*" -and
$_ -notlike "Comment*"
}
# Check if there are members and remove each member from the group
if ($filteredMembers) {
foreach ($member in $filteredMembers) {
try {
net localgroup MakeMeAdmin $member /delete
Write-Host "Successfully removed $member from MakeMeAdmin group"
} catch {
Write-Warning "Failed to remove $member from MakeMeAdmin group"
}
}
} else {
Write-Host "MakeMeAdmin group is already empty. No action needed."
}
Write-Host "Remediation completed successfully."
} catch {
Write-Warning "Failed to clear MakeMeAdmin group or the group doesn't exist"
Write-Host "Remediation encountered an error."
}
Stop-Transcript