Tricking a security scanner trusted inputs

Used this code to trick a security scanner into trusting an input.

The security scanner builds a trace of an input into a function and does not trust modification of the input without revalidation.

If you have a connection string located in web.config or appsettings.json file it is trusted as an input from the file if not modified as the application startup in C# validates the connection string, so as long as you do not modify the connectionstring then it can be trusted or at least traced for all usages within the application.

So let's break the scanners ability to trace the source of where a input variable came into scope by using serialization and dynamics

In your appsettings.json file (.NET Core)

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Data Source=<Server>; Integrated Security=true;Initial Catalog=<DB>;"
  }
}

Code:

// Pass the target database in as a string (scanner will flag this if used, but we will hide the source of this with the following approach
private void OpenConnection(string databaseName)
{
	// Get the connection string to modify
	var connectionString = Configuration.GetConnectionString("DefaultConnection");
    // Build a connection string to the "master" database could have used "model"
	var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString)
	{
		InitialCatalog = "master"
	};
    
    // Now do the trickery of hiding the passed in database name into an ExpandoObject which the security scanner will lose track of because it traces paths
	var serialized = $"{{\"connectTo\":\"{databaseName}\"}}";
    // Deserialize to from the serialized string
	dynamic jsonResponse = JsonConvert.DeserializeObject<ExpandoObject>(serialized);
    // Pop out the database name as the scanner thinks this is constant string as the contents were lost for the tracer as it turned into a dynamic object and back out
	var targetDatabase = jsonResponse.connectTo as string;
	if (targetDatabase != databaseName)
	{
		return;
	}
    // Connect to the known model/master database
	using (var connection = new SqlConnection(connectionStringBuilder.ToString()))
	{
		connection.Open();
        // switch to the passed in targetDatabase which again looks like a constant to the scanner because it lost track of the source by keeping it in the same method it thinks this is a constant value and therefore confined to the method and checked ok
		connection.ChangeDatabase(targetDatabase ?? throw new InvalidOperationException());
	}
}

Explanation

  1. private void OpenConnection(string databaseName) - Pass the target database in as a string (scanner will flag this if used, but we will hide the source of this with the following approach
  2. var connectionString = Configuration.GetConnectionString("DefaultConnection"); - Get the connection string to modify
  3. var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString){ InitialCatalog = "master" }; - Build a connection string to the master database could have used model
  4. var serialized = $"{{"connectTo":"{databaseName}"}}"; - Now do the trickery of hiding the passed in database name into an ExpandoObject which the security scanner will lose track of because it traces paths
  5. dynamic jsonResponse = JsonConvert.DeserializeObject(serialized); - Deserialize from the serialized string
  6. var targetDatabase = jsonResponse.connectTo as string; - Pop out the database name as the scanner thinks this is constant string as the contents were lost for the tracer as it turned into a dynamic object and back out
  7. using (var connection = new SqlConnection(connectionStringBuilder.ToString())) - Connect to the known model/master database
  8. connection.ChangeDatabase(targetDatabase ?? throw new InvalidOperationException()); - Switch to the passed in targetDatabase which again looks like a constant to the scanner because it lost track of the source by keeping it in the same method it thinks this is a constant value and therefore confined to the method and checked ok