Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 143 additions & 92 deletions Invoke-Locksmith.ps1

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Locksmith.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
FunctionsToExport = 'Invoke-Locksmith'
GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a'
HelpInfoURI = 'https://raw.githubusercontent.com/jakehildreth/Locksmith/main/en-US/'
ModuleVersion = '2025.5.26'
ModuleVersion = '2025.9.8'
PowerShellVersion = '5.1'
PrivateData = @{
PSData = @{
Expand Down
8 changes: 4 additions & 4 deletions Locksmith.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ $Assembly = @(
}
)
$FoundErrors = @(
Foreach ($Import in @($Assembly)) {
foreach ($Import in @($Assembly)) {
try {
Write-Verbose -Message $Import.FullName
Add-Type -Path $Import.Fullname -ErrorAction Stop
Expand All @@ -77,10 +77,10 @@ $FoundErrors = @(
}
}
#Dot source the files
Foreach ($Import in @($Classes + $Enums + $Private + $Public)) {
Try {
foreach ($Import in @($Classes + $Enums + $Private + $Public)) {
try {
. $Import.Fullname
} Catch {
} catch {
Write-Error -Message "Failed to import functions from $($import.Fullname): $_"
$true
}
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC1.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC13.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ function Find-ESC13 {
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC15.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ function Find-ESC15 {
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
2 changes: 1 addition & 1 deletion Private/Find-ESC16.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
if ($_.DisableExtensionList -eq 'Yes') {
$Issue.Issue = @"
The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When
this extension is disabled, every certificate issued by this CA will be unable to to reliably map a certificate to a
this extension is disabled, every certificate issued from this template will be unable to reliably map a certificate to a
user or computer account's SID for authentication.

More info:
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC2.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC3C1.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC3C2.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
7 changes: 6 additions & 1 deletion Private/Find-ESC9.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@
} else {
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
}
if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) {
if (
($SID -notmatch $SafeUsers) -and
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
($entry.ActiveDirectoryRights -match 'GenericAll') )
) {
$Issue = [pscustomobject]@{
Forest = $_.CanonicalName.split('/')[0]
Name = $_.Name
Expand Down
7 changes: 3 additions & 4 deletions Private/Set-AdditionalCAProperty.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
process {
$ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object {
$CAEnrollmentEndpoint = @()
#[array]$CAEnrollmentEndpoint = $_.'msPKI-Enrollment-Servers' | Select-String 'http.*' | ForEach-Object { $_.Matches[0].Value }
foreach ($directory in @('certsrv/', "$($_.Name)_CES_Kerberos/service.svc", "$($_.Name)_CES_Kerberos/service.svc/CES", 'ADPolicyProvider_CEP_Kerberos/service.svc', 'certsrv/mscep/')) {
$URL = "://$($_.dNSHostName)/$directory"
try {
Expand All @@ -81,7 +80,7 @@
$Cache = [System.Net.CredentialCache]::New()
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
$Request.Credentials = $Cache
$Request.Timeout = 1000
$Request.Timeout = 100
$Request.GetResponse() | Out-Null
$CAEnrollmentEndpoint += @{
'URL' = $FullURL
Expand All @@ -95,7 +94,7 @@
$Cache = [System.Net.CredentialCache]::New()
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
$Request.Credentials = $Cache
$Request.Timeout = 1000
$Request.Timeout = 100
$Request.GetResponse() | Out-Null
$CAEnrollmentEndpoint += @{
'URL' = $FullURL
Expand All @@ -109,7 +108,7 @@
$Cache = [System.Net.CredentialCache]::New()
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
$Request.Credentials = $Cache
$Request.Timeout = 1000
$Request.Timeout = 100
$Request.GetResponse() | Out-Null
$CAEnrollmentEndpoint += @{
'URL' = $FullURL
Expand Down
4 changes: 4 additions & 0 deletions Private/Set-RiskRating.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ function Set-RiskRating {
$Principals += $OtherIssue.IdentityReference.Value
$OtherIssueRisk += 1
}
else {
$Principals += $OtherIssue.IdentityReference.Value
$OtherIssueRisk += 0.1
}
$CheckedESC5Templates.$($OtherIssue.Name) = $Principals
} # forech ($OtherIssue)
if ($OtherIssueRisk -ge 2) {
Expand Down
13 changes: 8 additions & 5 deletions Public/Invoke-Locksmith.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ function Invoke-Locksmith {

# GenericAll, WriteDacl, and WriteOwner all permit full control of an AD object.
# WriteProperty may or may not permit full control depending the specific property and AD object type.
$DangerousRights = 'GenericAll|WriteDacl|WriteOwner|WriteProperty'
$DangerousRights = 'GenericAll|Write'
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern 'Write' is overly broad and will match unintended permissions like 'WriteExtendedAttributes' or 'WriteValidatedWrite'. Consider using more specific patterns like 'WriteDacl|WriteOwner|WriteProperty' to avoid false positives.

Suggested change
$DangerousRights = 'GenericAll|Write'
$DangerousRights = 'GenericAll|WriteDacl|WriteOwner|WriteProperty'

Copilot uses AI. Check for mistakes.

# Extended Key Usage for client authentication. A requirement for ESC3.
$EnrollmentAgentEKU = '1\.3\.6\.1\.4\.1\.311\.20\.2\.1'
Expand Down Expand Up @@ -320,11 +320,13 @@ function Invoke-Locksmith {
[!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues
identified in the environment. For more details including:

- DistinguishedName of impacted object(s)
- Remediation guidance and/or code
- Detailed Risk Rating
- General remediation guidance and/or code for all issues
- Custom remediation guidance and/or code for some issues!
- Revert guidance and/or code (in case remediation breaks something!)
- Distinguished Name of impacted object(s)

Run Locksmith in Mode 1!
Try Mode 1!

# Module version
Invoke-Locksmith -Mode 1
Expand Down Expand Up @@ -385,5 +387,6 @@ Invoke-Locksmith -Mode 1
}
}
Write-Host 'Thank you for using ' -NoNewline
Write-Host "Locksmith <3`n" -ForegroundColor Magenta
Write-Host 'Locksmith <3 ' -ForegroundColor Magenta -NoNewline
Write-Host "(https://github.com/jakehildreth/Locksmith)`n"
}
Loading