Active Directory (AD) 보안을 관리하는 것은 특히 암호를 공유하는 계정을 식별하거나 약한 인증 관행을 사용하는 경우 어려울 수 있습니다. 강력하고 가벼운 PowerShell 도구는 공유 암호 사용을 신속하게 밝히고 조직의 AD 보안 자세를 향상시킵니다. LazyAdminFinder LazyAdminFinder 란 무엇입니까? LazyAdminFinder는 DSInternals 모듈을 활용하는 PowerShell 스크립트입니다. 공유된 NTLM 암호 해시를 감지하고 보고합니다. 암호 재사용을 식별하기 위해 역사적인 해시 데이터를 아카이브 및 비교합니다.Archive and compare historical hash data to identify password reuse. 위험한 계좌 행동을 파악하는 포괄적 인 보고서를 제공합니다. Active Directory 관리자를 위해 특별히 설계된 LazyAdminFinder는 암호 보안에 대한 빠르고 실행 가능한 통찰력을 제공합니다. LazyAdminFinder를 사용하는 이유는 무엇입니까? Identify weak or compromised passwords. Security Improvement: Maintain AD integrity for audit and compliance purposes. Compliance: Quickly audit accounts without manually reviewing data. Efficiency: LazyAdminFinder는 어떻게 작동합니까? Fetch 및 Filter 사용자 계정 스크립트는 AD 사용자 계정을 수집하여 비활성화, 기계 및 서비스 계정을 필터링하여 활성 사용자에만 집중합니다. NTLM 해시 추출 및 아카이브 LazyAdminFinder는 계정에서 NTLM 해시를 추출하고 역사적인 비교를 위해 SHA-256 해시를 안전하게 보관합니다. 암호 재사용 탐지 현재 실행을 이전 아카이브와 비교함으로써 도구는 암호 재사용을 감지하여 잠재적인 보안 위험이 있는 계정을 표시합니다.This helps identify: 관리자 또는 특권 사용자가 표준 및 관리 계정 사이에서 암호를 동기화합니다. 사용자, 특히 관리자는 이전에 사용한 값으로 암호를 반복적으로 재설정합니다. 공유 암호 그룹화 스크립트는 동일한 NTLM 해시를 공유하는 계정을 식별하고 그룹화하여 의도적 인 암호 공유 또는 여러 사용자가 동일한 암호를 독립적으로 선택하는 것을 나타낼 수 있습니다.예를 들어, 여러 사용자가 "Summer2025!"와 같은 공통 암호를 선택하면 LazyAdminFinder는 이러한 계정을 함께 그룹화하여 잠재적 인 공유 암호와 암호 생성에서 위험한 병렬 사고를 신속하게 발견 할 수 있습니다. 권장되는 사용 주파수 LazyAdminFinder를 조직의 암호 재설정 정책에 따라 정기적으로 실행하는 것을 고려하십시오. 예를 들어 정책이 90일마다 암호 재설정을 요구하는 경우, 스크립트를 약 100일마다 실행하도록 일정하십시오.이 타이밍은 관리자 또는 특권 사용자가 이전에 사용한 암호로 암호를 재설정할 수 있는 인스턴스를 캡처할 수 있도록합니다. LazyAdminFinder 설치 단계 1 : 준비 PowerShell이 시스템에 설치되어 있는지 확인합니다. 필요한 모듈을 설치하십시오: Install-Module DSInternals 2단계: 스크립트를 설정 제공된 스크립트 내에서 이러한 필수 매개 변수를 사용자 정의하십시오:Customize these essential parameters within the provided script: Domain Controller: $dc = "your-domain-controller.example.com" 이름의 컨텍스트 : $nc = "DC=yourdomain,DC=com" 출력 보고서 경로: $outputReport = "C:\Path\To\SharedPasswordsReport.csv" 아카이브 디렉토리 : $archiveDir = "C:\Path\To\HashArchives" 3단계: 스크립트 실행 스크립트를 Domain admin로 실행하십시오: powershell.exe -ExecutionPolicy Bypass -File "C:\Path\To\LazyAdminFinder.ps1" 결과를 검토하기 자세한 CSV 보고서가 생성되며 계정이 암호를 공유하거나 약한 암호를 독립적으로 선택하는 것을 명확하게 강조합니다. 역사적인 아카이브는 미래의 비교를 위해 안전하게 저장되어 강력한 암호 관리를 보장합니다. LazyAdminFinder에 대한 사용 사례 보안 감사: 정기적으로 공유, 약하거나 위협된 비밀번호를 식별하고 수정합니다. Incident Response: 보안 사건으로 인해 손상된 계정을 신속하게 탐지합니다. 일상적인 준수 검사: 강력한 계정 보안 관행을 유지함으로써 지속적인 준수를 보장합니다. 특권 계정 관리: 여러 계정에서 암호를 재사용하거나 동기화할 수 있는 관리자나 특권 사용자를 대상으로 합니다. 사용자 정의 LazyAdminFinder 환경과 필요에 따라 스크립트를 조정할 수 있습니다 : 필터링 논리를 변경하여 특정 계정 유형을 포함/ 제외합니다.Modify filtering logic to include/exclude specific account types. 보관 및 보고 경로를 저장 설정에 따라 조정합니다.Adjust archiving and reporting paths for your storage preferences. 필요에 따라 추가 보고 능력을 통합합니다. LazyAdminFinder 사용의 장점 Automates complex AD security checks. Rapid Security Assessments: Clearly identifies potential security risks within AD. Enhanced Visibility: Enables administrators to swiftly address vulnerabilities before exploitation. Proactive Defense: LazyAdminFinder는 Active Directory에서 암호 감사의 중요한 작업을 단순화하여 관리자가 안전하고 준수하며 효율적인 IT 환경을 유지할 수 있도록 합니다. 공유 암호를 발견하고, 보안 위험을 해결하고, LazyAdminFinder를 사용하여 AD 보안을 쉽게 강화하십시오. #Another /\_[]_/\ # fine |] _||_ [| # ___ \/ || \/ # /___\ || # (|0 0|) || # __/{\U/}\_ ___/vvv # / \ {~} / _|_P| # | /\ ~ /_/ [] # |_| (____) # \_]/______\ Barberion # _\_||_/_ Production # (_,_||_,_) # Import-Module DSInternals # === Parameters === $dc = "your-domain-controller.example.com" $nc = "DC=yourdomain,DC=com" $outputReport = "C:\Path\To\SharedPasswordsReport.csv" $archiveDir = "C:\Path\To\HashArchives" # Ensure archive directory exists if (-not (Test-Path $archiveDir)) { New-Item -ItemType Directory -Path $archiveDir | Out-Null } $timestamp = (Get-Date).ToString("yyyyMMdd_HHmmss") # === 1. Fetch & filter AD user accounts === Write-Host "[+] Fetching AD user accounts (skipping machines, service & disabled)..." $filteredAccounts = Get-ADReplAccount -All -Server $dc -NamingContext $nc | Where-Object { $_.NTHash -and $_.SamAccountName } | Where-Object { $_.SamAccountName -notmatch '\$$' } | Where-Object { $_.SamAccountName -notmatch '^(?i)(svc|service)' } | Where-Object { if ($_.PSObject.Properties.Match('IsDisabled').Count -gt 0) { -not $_.IsDisabled } else { # bit 2 = disabled ( ($_.UserAccountControl -band 2) -eq 0 ) } } if (-not $filteredAccounts) { Write-Host "[-] No matching user accounts found. Exiting." exit } # === 2. Convert NTLM byte array to hex string === $filteredAccounts | ForEach-Object { $hex = [BitConverter]::ToString($_.NTHash) -replace '-', '' $_ | Add-Member -MemberType NoteProperty -Name NTLMHashString -Value $hex -Force } # === 3. Archive SHA-256 of each NTLM hash === Write-Host "[+] Archiving SHA-256 of each NTLM hash..." $sha256 = [System.Security.Cryptography.SHA256]::Create() $currentArchive = $filteredAccounts | ForEach-Object { $shaBytes = $sha256.ComputeHash($_.NTHash) $shaHex = [BitConverter]::ToString($shaBytes) -replace '-', '' [PSCustomObject]@{ SamAccountName = $_.SamAccountName HashSha256 = $shaHex } } $sha256.Dispose() $archiveFile = Join-Path $archiveDir "UserHashArchive_$timestamp.csv" $currentArchive | Export-Csv -Path $archiveFile -NoTypeInformation # === 4. Compare to previous archive === $allArchives = Get-ChildItem -Path $archiveDir -Filter "UserHashArchive_*.csv" | Sort-Object LastWriteTime -Descending if ($allArchives.Count -gt 1) { $previousFile = $allArchives[1].FullName Write-Host "[+] Comparing this run to previous archive: $previousFile" $prevData = Import-Csv $previousFile $reused = Compare-Object ` -ReferenceObject $prevData ` -DifferenceObject $currentArchive ` -Property SamAccountName, HashSha256 ` -IncludeEqual | Where-Object { $_.SideIndicator -eq '==' } | Select-Object SamAccountName, HashSha256 if ($reused) { $reuseReport = Join-Path $archiveDir "PasswordReuse_$timestamp.csv" Write-Host "[!] $($reused.Count) accounts reused their old hash; exporting to $reuseReport" $reused | Export-Csv -Path $reuseReport -NoTypeInformation } else { Write-Host "[+] No password‑reuse detected this run." } } else { Write-Host "[+] Only one archive present; skipping reuse comparison." } # === 5. Shared‑password grouping & report === Write-Host "[+] Grouping accounts by shared NTLM hash..." $sharedGroups = $filteredAccounts | Group-Object -Property NTLMHashString | Where-Object { $_.Count -gt 1 } if (-not $sharedGroups) { Write-Host "[-] No shared hashes found." exit } $reportResults = @() Write-Host "[+] Generating shared‑password report:" foreach ($g in $sharedGroups) { $h = $g.Name $users = $g.Group | ForEach-Object { $_.SamAccountName } $reportResults += [PSCustomObject]@{ SharedHash = $h UserCount = $users.Count Users = $users -join ', ' } Write-Host '----------------------------------------------' Write-Host "Shared Hash: $h" Write-Host "User Count : $($users.Count)" Write-Host "Users : $($users -join ', ')" Write-Host '----------------------------------------------`n' } Write-Host "[+] Saving shared‑password report to $outputReport" $reportResults | Export-Csv -Path $outputReport -NoTypeInformation Write-Host "[Summary] Shared groups: $($reportResults.Count); Total archives: $($allArchives.Count)"