|
| 1 | +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
| 2 | + <!-- |
| 3 | + Author: Scott Sutherland (@_nullbind), NetSPI 2019 |
| 4 | + Version: 0.1 (work in progress) |
| 5 | + Description: This is a simple C# SQL Server client that can be run through msbuild. |
| 6 | + Instructions: |
| 7 | + 1. Save this to a .xml or .csproj file. |
| 8 | + 2. In a console, navigate to the folder containing this .csproj or .xml file. |
| 9 | + 3. Run the msbuild.exe command from its path using one of the commands below. |
| 10 | + You can explicitly define the .csproj file to load, but it is not required |
| 11 | + if only one .csproj file existing the your current working directory. |
| 12 | + C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe |
| 13 | + C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe sqlclient.csproj |
| 14 | + C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe sqlclient.xml |
| 15 | + |
| 16 | + Note: This execution technique is entirely based on Casey Smith magic. |
| 17 | + http://subt0x11.blogspot.com/2018/11/microsoft-build-engine-compromise-part_13.html |
| 18 | + --> |
| 19 | + <Target Name="SQLClient"> |
| 20 | + <SQLClientExample /> |
| 21 | + </Target> |
| 22 | + <UsingTask |
| 23 | + TaskName="SQLClientExample" |
| 24 | + TaskFactory="CodeTaskFactory" |
| 25 | + AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" > |
| 26 | + <Task> |
| 27 | + <Reference Include="C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll" /> |
| 28 | + <Reference Include="C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll" /> |
| 29 | + |
| 30 | + <Code Type="Class" Language="cs"> |
| 31 | + <![CDATA[ |
| 32 | + using System; |
| 33 | + using System.IO; |
| 34 | + using Microsoft.Build.Framework; |
| 35 | + using Microsoft.Build.Utilities; |
| 36 | + using System.Text; |
| 37 | + using System.Data; |
| 38 | + using System.Data.SqlClient; |
| 39 | + using System.Data.SqlTypes; |
| 40 | +
|
| 41 | + public class SQLClientExample : Task, ITask |
| 42 | + { |
| 43 | + public override bool Execute() |
| 44 | + { |
| 45 | + SQLCommands.RunQuery("",""); |
| 46 | + return true; |
| 47 | + } |
| 48 | + } |
| 49 | +
|
| 50 | + public class SQLCommands |
| 51 | + { |
| 52 | + public static string RunQuery(string MyConnString, string filePath) |
| 53 | + { |
| 54 | + // Read line from the client |
| 55 | + Console.WriteLine("----------"); |
| 56 | + Console.Write("SQLCLIENT> "); |
| 57 | + String MyQuery = Console.ReadLine().ToString(); |
| 58 | +
|
| 59 | + // Collect multi-line command until "go" is given |
| 60 | + string fullcommand = ""; |
| 61 | + string connString = MyConnString; |
| 62 | + while (MyQuery.ToLower() != "go") |
| 63 | + { |
| 64 | + fullcommand = fullcommand + "\n" + MyQuery; |
| 65 | +
|
| 66 | + // Exit if requested |
| 67 | + if(MyQuery.ToLower().Equals("exit") || MyQuery.ToLower().Equals("quit") || MyQuery.ToLower().Equals("bye")) |
| 68 | + { |
| 69 | + return "placeholder"; |
| 70 | + } |
| 71 | +
|
| 72 | + // Allow ability to set custom connection string |
| 73 | + bool loadCheck = MyQuery.ToLower().Contains("setconn"); |
| 74 | + if(loadCheck) |
| 75 | + { |
| 76 | + Console.Write("\nSetting connection string to:"); |
| 77 | + string newcon = MyQuery.Replace("setconn ",""); |
| 78 | + Console.Write("\n" + newcon + "\n"); |
| 79 | + connString = newcon; |
| 80 | + fullcommand = ""; |
| 81 | + Console.Write("\nSQLCLIENT> "); |
| 82 | + } |
| 83 | +
|
| 84 | + // Allow ability to set custom output file |
| 85 | + bool fileCheck = MyQuery.ToLower().Contains("setfile"); |
| 86 | + if(fileCheck) |
| 87 | + { |
| 88 | + Console.Write("\nSetting output path to:"); |
| 89 | + string newfile = MyQuery.Replace("setfile ",""); |
| 90 | + Console.Write("\n" + newfile + "\n"); |
| 91 | + filePath = newfile; |
| 92 | + fullcommand = ""; |
| 93 | + Console.WriteLine("----------"); |
| 94 | + Console.Write("\nSQLCLIENT> "); |
| 95 | + } |
| 96 | + |
| 97 | + // Clear console if requested |
| 98 | + if(MyQuery.ToLower().Equals("clear")) |
| 99 | + { |
| 100 | + Console.Clear(); |
| 101 | + fullcommand = ""; |
| 102 | + Console.WriteLine("----------"); |
| 103 | + Console.Write("SQLCLIENT> "); |
| 104 | + } |
| 105 | + |
| 106 | + // Provide help |
| 107 | + bool helpCheck = MyQuery.ToLower().Contains("help"); |
| 108 | + if(MyQuery.ToLower().Equals("help")){ |
| 109 | + Console.WriteLine("\n"); |
| 110 | + Console.WriteLine(" BELOW IS A LIST OF SUPPORTED COMMANDS"); |
| 111 | + Console.WriteLine("\n"); |
| 112 | + Console.WriteLine(" COMMAND TSQL queries"); |
| 113 | + Console.WriteLine(" DESCRIPTION Type the query, then \"go\":"); |
| 114 | + Console.WriteLine(" EXAMPLE SELECT @@VERSION"); |
| 115 | + Console.WriteLine(" GO"); |
| 116 | + Console.WriteLine("\n"); |
| 117 | + Console.WriteLine(" COMMAND setcon"); |
| 118 | + Console.WriteLine(" DESCRIPTION By default the connection string is harded coded. Use this command to change it on the fly."); |
| 119 | + Console.WriteLine(" EXAMPLE 1 setcon Server=SERVER1\\Instance;Database=Master;User ID=Username;Password=Password;"); |
| 120 | + Console.WriteLine(" EXAMPLE 2 setcon Server=SERVER1\\Instance;Database=Master;Integrated Security=SSPI;"); |
| 121 | + Console.WriteLine(" EXAMPLE 3 setcon Server=SERVER1\\Instance;Database=Master;Integrated Security=SSPI;uid=domain\\user;pwd=password123"); |
| 122 | + Console.WriteLine("\n"); |
| 123 | + Console.WriteLine(" COMMAND setfile"); |
| 124 | + Console.WriteLine(" DESCRIPTION By default, the output file is c:\\windows\\temp\\output.csv. Use this command to change it on the fly."); |
| 125 | + Console.WriteLine(" EXAMPLE setfile c:\\windows\\temp\\file.csv"); |
| 126 | + Console.WriteLine("\n"); |
| 127 | + fullcommand = ""; |
| 128 | + Console.Write("SQLCLIENT> "); |
| 129 | + } |
| 130 | + |
| 131 | + // Show multi-line input |
| 132 | + if((MyQuery.ToLower() != "clear") && (!loadCheck) && (!fileCheck) && (!helpCheck)) |
| 133 | + { |
| 134 | + Console.Write(" > "); |
| 135 | + } |
| 136 | +
|
| 137 | + // Collect additional query lines |
| 138 | + MyQuery = Console.ReadLine().ToString(); |
| 139 | + } |
| 140 | +
|
| 141 | + // Create data table to store results |
| 142 | + DataTable dt = new DataTable(); |
| 143 | + |
| 144 | + // Run query |
| 145 | + try{ |
| 146 | + // Set connection string |
| 147 | + if(connString.Equals("")) |
| 148 | + { |
| 149 | + connString = @"Server=SQLServer\INSTANCE;Database=Master;Integrated Security=SSPI;"; |
| 150 | + } |
| 151 | +
|
| 152 | + // Create new connection |
| 153 | + SqlConnection conn = new SqlConnection(connString); |
| 154 | + SqlCommand QueryCommand = new SqlCommand(fullcommand, conn); |
| 155 | + conn.Open(); |
| 156 | +
|
| 157 | + // Execute query and read data into data table |
| 158 | + SqlDataAdapter da = new SqlDataAdapter(QueryCommand); |
| 159 | + da.Fill(dt); |
| 160 | +
|
| 161 | + // Display output of data table |
| 162 | + DataRow[] currentRows = dt.Select(null, null, DataViewRowState.CurrentRows); |
| 163 | +
|
| 164 | + // Display results to screen |
| 165 | + if (currentRows.Length < 1 ) |
| 166 | + { |
| 167 | + Console.WriteLine("\nNo rows returned."); |
| 168 | + }else{ |
| 169 | + Console.WriteLine("\n QUERY RESULTS:\n"); |
| 170 | +
|
| 171 | + foreach (DataColumn column in dt.Columns) |
| 172 | + { |
| 173 | + Console.Write("\t{0}", column.ColumnName); |
| 174 | + } |
| 175 | +
|
| 176 | + Console.WriteLine("\t"); |
| 177 | +
|
| 178 | + foreach (DataRow row in currentRows) |
| 179 | + { |
| 180 | + foreach (DataColumn column in dt.Columns) |
| 181 | + { |
| 182 | + Console.Write("\t{0}", row[column]); |
| 183 | + } |
| 184 | + |
| 185 | + Console.WriteLine("\t"); |
| 186 | + } |
| 187 | +
|
| 188 | + // Write results to csv |
| 189 | + StringBuilder fileContent = new StringBuilder(); |
| 190 | + if(filePath.Equals("")){ |
| 191 | + filePath = "c:\\Windows\\Temp\\output.csv"; |
| 192 | + } |
| 193 | +
|
| 194 | + foreach (var col in dt.Columns) |
| 195 | + { |
| 196 | + fileContent.Append(col.ToString() + ","); |
| 197 | + } |
| 198 | +
|
| 199 | + fileContent.Replace(",", System.Environment.NewLine, fileContent.Length - 1, 1); |
| 200 | + foreach (DataRow dr in dt.Rows) |
| 201 | + { |
| 202 | + foreach (var column in dr.ItemArray) |
| 203 | + { |
| 204 | + fileContent.Append("\"" + column.ToString() + "\","); |
| 205 | + } |
| 206 | + |
| 207 | + fileContent.Replace(",", System.Environment.NewLine, fileContent.Length - 1, 1); |
| 208 | + } |
| 209 | +
|
| 210 | + try{ |
| 211 | + System.IO.File.WriteAllText(filePath, fileContent.ToString()); |
| 212 | + Console.WriteLine("\nSuccessfully wrote query output to " + filePath); |
| 213 | + }catch{ |
| 214 | + Console.WriteLine("\nFailed to write query output to " + filePath); |
| 215 | + } |
| 216 | + } |
| 217 | + } |
| 218 | + catch(SqlException ex) |
| 219 | + { |
| 220 | + Console.WriteLine(ex.Errors[0].Message); |
| 221 | + } |
| 222 | +
|
| 223 | + RunQuery(connString,filePath); |
| 224 | + return "placeholder"; |
| 225 | + } |
| 226 | + } |
| 227 | + ]]> |
| 228 | + </Code> |
| 229 | + </Task> |
| 230 | + </UsingTask> |
| 231 | +</Project> |
0 commit comments