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
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 approachvar connectionString = Configuration.GetConnectionString("DefaultConnection");
- Get the connection string to modifyvar connectionStringBuilder = new SqlConnectionStringBuilder(connectionString){ InitialCatalog = "master" };
- Build a connection string to the master database could have used modelvar 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 pathsdynamic jsonResponse = JsonConvert.DeserializeObject(serialized);
- Deserialize from the serialized stringvar 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 outusing (var connection = new SqlConnection(connectionStringBuilder.ToString()))
- Connect to the known model/master databaseconnection.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