為Active Directory配置密碼黑名單
毫於疑問,許多攻擊者目前已經掌握了多種破解密碼的專業知識,其中破解流行密碼的字典攻擊變得越來越有攻擊力。因此,美國國家標準與技術研究院建議為Active Directory配置密碼黑名單,以阻止此類攻擊。不過,為Active Directory配置密碼黑名單目前仍然是一項相對較新的創新,企業尚未實現廣泛的應用。
然而,對於像Yelp這樣注重安全的人氣網站來說,他們已經及時採用了此類防禦機制。由於Yelp使用Active Directory(AD)對所有員工進行身份驗證和管理,因此可以實現他們自己的自定義密碼過濾器動態連結庫(Password Filter DLL)。Yelp是美國著名商戶點評網站,創立於2004年,類似於中國的大眾點評網。
在這篇文章中,我將詳細描述一下Yelp是如何利用現有的開源DLL來構建密碼黑名單服務,以滿足安全策略和需求。
解決方案
經過大量的研究和測試,Yelp的安全管理人員發現 ofollow,noindex">OpenPasswordFilter(OPF) 最符合他們的需求。他們選擇了最原始的版本,並給它增加了連線SQL資料庫的能力,而不是比較純文字中的雜湊值。使用自定義密碼過濾器動態連結庫的最大問題之一是本地安全機構(LSA)執行的任何錯誤都可能導致正在執行的DC伺服器崩潰。另一個主要的問題是確保在不影響使用者體驗的情況下快速有效地新增新密碼。所選擇的解決方案通過結合面向服務的體系結構解決了這兩個問題,該體系結構將基本的LSA執行緒程式碼與過濾功能分離,同時還集成了可由服務直接查詢的基於SQL的資料庫。此外,在過濾過程中,該設計還進行了安全保護,任何觸發的異常或錯誤都將導致系統開啟失敗,從而最大程度的限制了令人擔憂的DC關閉的可能性。鑑於LSA對DLL錯誤的脆弱性,這是必要的權衡,特別是密碼重置被破壞後所造成的記錄錯誤。
系統架構
密碼過濾服務由三部分組成:
· OPF Service:用於密碼驗證的模組(通過 loopback與DLL通訊);
· OPF DLL:原始密碼過濾器DLL,它與LSA連線以用於憑證驗證;
·OPF DB:資料庫中包含所有易受攻擊密碼的SHA-1雜湊值;
每當客戶端請求更改密碼時,此請求將通過其指定的與LSA聯絡的域控制器進行路由。如果未滿足預設密碼策略(最小密碼長度和某些字元標準的組合),則不會呼叫密碼篩選器DLL,並自動拒絕密碼。但是,如果滿足預設策略要求,則將使用密碼呼叫DLL。該過程的示意圖如下所示:
Active Directory密碼過濾器身份驗證流程
具體步驟如下,總共7步:
1.客戶端通過DC向LSA發起密碼請求更改,從DC到LSA的聯絡是通過配置LSA通知包完成的。如果滿足預設密碼策略,則將呼叫已註冊的DLL,否則將拒絕密碼。
2.PasswordFilter函式是Microsoft的PasswordFilter DLL介面的三種核心方法之一。此函式根據是否應進行重置返回一個布林響應。OPF版本嘗試連線到loopback介面上的特定埠,以呼叫已註冊的服務。如果連線失敗,則由於其故障安全性,該函式返回true。
3.如果可以連線建立起來後,則DLL會嘗試將憑據傳送到OPF服務。首先發送preamble碼,即隨機接入前導碼,然後傳送憑證。
4.然後檢查雜湊憑證(SHA-1)是否存在於容易受損密碼的資料庫中,對密碼雜湊值的索引使得這一過程明顯更快。
5.根據是否找到雜湊,將代表成功或失敗的訊息返回給OPFService,OPFService以初始DLL的形式,將該訊息作為布林值返回給LSA。
6.對所有已註冊的DLL重複上述過程,假設一切都成功,密碼將正式提交給安全帳戶管理器(SAM)。
7. 然後呼叫PasswordChangeNotify函式為每個註冊DLL進行同步。
安全性測試
最後的安全性測試尤為重要,因為一旦安全性得不到實際保證,則任何實際中的錯誤都會產生嚴重影響,比如導致域控制器崩潰。此外,為了確保系統不會因測試而佔用大量的系統資源。因此,在測試之前,測試人員先在一個獨立的域控制器上測試了該服務,然後再在實驗室環境中進行了測試。方法如下:
1.在獨立域或實驗室域中設定一個DC;
2.根據Yelp的密碼策略配置預設的域密碼策略設定;
3.下載一些最常被攻擊的密碼列表,從中進行抽樣;
4.將抽樣的密碼隨機分成四個子集,每個大小為5000:
4.1符合預設政策但屬於黑名單的子集命名為A;
4.2 符合預設政策但不屬於黑名單的子集命名為B;
4.3 不符合預設策略且屬於黑名單的子集命名為C;
4.4不符合預設策略且不屬於黑名單的子集命名為D;
對於上述四個子集,安全人員只期望子集B可以重置成功。
5.為子集B和子集C生成SHA-1雜湊值;
6.在要測試的總密碼數量中建立啟用的LDAP/">LDAP使用者;
7.建立一個CSV檔案,每行包含使用者、密碼和與密碼分類相關的類別(A,B,C,D);
8.執行他們定製的Shell/">PowerShell指令碼(下面會提到),以檢查已驗證的安全行為;
Powershell指令碼的詳細資訊
1.每個啟用的使用者都嘗試重置各自的密碼;
2.使用秒錶記錄所需的總時間;
3.根據退出程式碼驗證預期的重置成功;
4.然後執行AD繫結以驗證是否成功登入;
5.預期的AD繫結行為也基於退出程式碼並要進行驗證;
6.結果按密碼類別彙總並輸出,具體而言,就是每個密碼重置的平均時間和發現的錯誤數量;下面的指令碼就是執行上述測試的:
param([string]$file, [string]$dc, [string]$admin_usr, [string]$admin_pwd)# Default error usage message Set-Variable errUsg -option Constant -Value "$($MyInvocation.MyCommand.Name) [CSV_FILE] [DC] [ADMIN_USR] [ADMIN_PWD]"# Ensure proper parameters are given $CommandName = $MyInvocation.InvocationName$ParameterList = (Get-Command -Name $CommandName).Parametersforeach ( $key in $ParameterList.Keys) { $value = (Get-Variable $key -ErrorAction SilentlyContinue).Valueif ([string]::IsNullOrEmpty($value)) { Write-Host "Required parameter not found." Write-Host "$errUsg" exit 1} }# Check existence of csv fileif (!(test-path $file)) { Write-Host "File $file not found." exit 1}# Set password types and associated expectations (0 = failure, 1 = success)$csv = Import-CSV -Path $file$pwdTypes = @("NO_POLICY_NO_DUMP", "NO_POLICY_YES_DUMP", "YES_POLICY_NO_DUMP", "YES_POLICY_YES_DUMP")$expected = @(0, 0, 1, 0)$times = @(0) * 4$errors = @(0) * 4$counts = @(0) * 4# Check provided DC is valid and that admin login credentials validdsquery user -u $admin_usr -p $admin_pwd -s $dc -q > $null 2>&1if ($LastExitCode -ne 0) { Write-Host "Invalid remote credentials or DC server specified." exit 1}# Start loggingstart-transcript -path C:\nail\syslog\DLL_LOG_$(get-date -format 'MM-dd-yyyy-HH-mm-ss').logForeach ($el in $csv) { # Extracted relevant fields $ex = $expected[$el.Id]$type = $pwdTypes[$el.Id]$usr = dsquery user -samid $el.User -s $dc -u $admin_usr -p $admin_pwd $pwd = $el.Password# Record time in milleseconds for password reset $sw = [system.diagnostics.stopwatch]::startNew() dsmod user $usr -pwd "$pwd" -mustchpwd no -d $dc -u $admin_usr -p "$admin_pwd" > $null 2>&1$sw.Stop() # Check exit code with expectations to validate reset success $r_s = If ($lastexitcode -ne 0) {0} Else {1} $t = $sw.get_ElapsedMilliseconds() $times[$el.Id] += $t if ($r_s -ne $ex) { $errors[$el.Id]++} # Perform AD bind and check with expectations to verify successful login (new-object directoryservices.directoryentry "", $usr, $pwd).psbase.name -ne $null > $null 2>&1$b_s = If ($lastexitcode -ne 0) {0} Else {1} if ($b_s -ne $ex) { $errors[$el.Id]++} # Log appropriately in the format of (EXPECTED, ACTUAL) for both the reset and bind $logline = "($usr) : [ $type] : RESET ($ex,$r_s) : BIND ($ex,$b_s) : TIME ($t)" if (($b_s -ne $ex) -or ($r_s -ne $ex)) { $logline = "[ERROR] [PASSWORD = $pwd] $logline" } $counts[$el.Id]++Write-Output $logline}# Aggregate final resultsWrite-Output ("*" * 22)Write-Output "RESULTS:"for ($i=0; $i -lt $pwdTypes.Length; $i++) { $type = $pwdTypes[$i] if ($counts[$i] -ne 0) { $t = $times[$i] / $counts[$i] } else { $t = 0} $es = $errors[$i] Write-Output "($type) ERRORS: $es AVG TIME: $t"}stop-transcript
安全測試結果
通過測試,安全管理人員不僅可以驗證密碼過濾服務的正確性,還可以檢視重置時間間隔等資料。
AD密碼重置時間間隔(帶有DLL))
更重要的是,DLL還進行了邊際時間的計算:
AD密碼重置時間間隔(帶有DLL)
AD密碼重置時間間隔(帶有DLL)
在對安全人員找出的100萬個密碼樣本進行檢測後,他們有了以下發現:
平均剩餘時間(在獨立的域控制器測試環境下)
平均剩餘時間(在試驗環境下)