From f284a0a6e860d1a848424368038985b432ee7946 Mon Sep 17 00:00:00 2001 From: Bryan McNulty Date: Mon, 28 Apr 2025 18:54:12 -0500 Subject: `dcom`: new method: `shellbrowserwindow` --- cmd/dcom.go | 64 ++++++++++++++++++++++++++++++----- pkg/goexec/dcom/shellbrowserwindow.go | 52 ++++++++++++++++++++++++++++ pkg/goexec/dcom/shellwindows.go | 4 +-- 3 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 pkg/goexec/dcom/shellbrowserwindow.go diff --git a/cmd/dcom.go b/cmd/dcom.go index 1234b12..30c32bc 100644 --- a/cmd/dcom.go +++ b/cmd/dcom.go @@ -16,11 +16,12 @@ func dcomCmdInit() { } dcomMmcCmdInit() dcomShellWindowsCmdInit() + dcomShellBrowserWindowCmdInit() dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags) dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags) dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags) - dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd) + dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd, dcomShellBrowserWindowCmd) } func dcomMmcCmdInit() { @@ -41,9 +42,7 @@ func dcomMmcCmdInit() { dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags) // Constraints - { - dcomMmcCmd.MarkFlagsOneRequired("command", "exec") - } + dcomMmcCmd.MarkFlagsOneRequired("command", "exec") } func dcomShellWindowsCmdInit() { @@ -64,14 +63,34 @@ func dcomShellWindowsCmdInit() { dcomShellWindowsCmd.Flags().AddFlagSet(dcomShellWindowsExecFlags.Flags) // Constraints - { - dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec") + dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec") +} + +func dcomShellBrowserWindowCmdInit() { + dcomShellBrowserWindowExecFlags := newFlagSet("Execution") + + registerExecutionFlags(dcomShellBrowserWindowExecFlags.Flags) + registerExecutionOutputFlags(dcomShellBrowserWindowExecFlags.Flags) + + dcomShellBrowserWindowExecFlags.Flags.StringVar(&dcomShellBrowserWindow.WorkingDirectory, "directory", `C:\`, "Working `directory`") + dcomShellBrowserWindowExecFlags.Flags.StringVar(&dcomShellBrowserWindow.WindowState, "app-window", "0", "Application window state `ID`") + + cmdFlags[dcomShellBrowserWindowCmd] = []*flagSet{ + dcomShellBrowserWindowExecFlags, + defaultAuthFlags, + defaultLogFlags, + defaultNetRpcFlags, } + dcomShellBrowserWindowCmd.Flags().AddFlagSet(dcomShellBrowserWindowExecFlags.Flags) + + // Constraints + dcomShellBrowserWindowCmd.MarkFlagsOneRequired("command", "exec") } var ( - dcomMmc dcomexec.DcomMmc - dcomShellWindows dcomexec.DcomShellWindows + dcomMmc dcomexec.DcomMmc + dcomShellWindows dcomexec.DcomShellWindows + dcomShellBrowserWindow dcomexec.DcomShellBrowserWindow dcomCmd = &cobra.Command{ Use: "dcom", @@ -114,7 +133,7 @@ var ( Short: "Execute with the ShellWindows DCOM object", Long: `Description: The shellwindows method uses the exposed ShellWindows DCOM object on older Windows installations - to call Item().Document.Application.ShellExecute, and spawn the specified process.`, + to call Item().Document.Application.ShellExecute, and spawn the provided process.`, Args: args( argsRpcClient("host"), argsOutput("smb"), @@ -135,4 +154,31 @@ var ( } }, } + + dcomShellBrowserWindowCmd = &cobra.Command{ + Use: "shellbrowserwindow [target]", + Short: "Execute with the ShellBrowserWindow DCOM object", + Long: `Description: + The shellbrowserwindow method uses the exposed ShellBrowserWindow DCOM object on older Windows installations + to call Document.Application.ShellExecute, and spawn the provided process.`, + Args: args( + argsRpcClient("host"), + argsOutput("smb"), + argsAcceptValues("app-window", &dcomShellBrowserWindow.WindowState, "0", "1", "2", "3", "4", "5", "7", "10"), + ), + Run: func(cmd *cobra.Command, args []string) { + dcomShellBrowserWindow.Client = &rpcClient + dcomShellBrowserWindow.IO = exec + dcomShellBrowserWindow.ClassID = dcomexec.ShellBrowserWindowUuid + + ctx := log.With(). + Str("module", dcomexec.ModuleName). + Str("method", dcomexec.MethodShellBrowserWindow). + Logger().WithContext(gssapi.NewSecurityContext(context.Background())) + + if err := goexec.ExecuteCleanMethod(ctx, &dcomShellBrowserWindow, &exec); err != nil { + log.Fatal().Err(err).Msg("Operation failed") + } + }, + } ) diff --git a/pkg/goexec/dcom/shellbrowserwindow.go b/pkg/goexec/dcom/shellbrowserwindow.go new file mode 100644 index 0000000..0825250 --- /dev/null +++ b/pkg/goexec/dcom/shellbrowserwindow.go @@ -0,0 +1,52 @@ +package dcomexec + +import ( + "context" + "fmt" + "github.com/FalconOpsLLC/goexec/pkg/goexec" + "github.com/rs/zerolog" +) + +const ( + MethodShellBrowserWindow = "ShellBrowserWindow" // MMC20.Application::Document.ActiveView.ExecuteShellCommand +) + +type DcomShellBrowserWindow struct { + Dcom + + IO goexec.ExecutionIO + + WorkingDirectory string + WindowState string +} + +// Execute will perform command execution via the ShellBrowserWindow object. See https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/ +func (m *DcomShellBrowserWindow) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) { + + log := zerolog.Ctx(ctx).With(). + Str("module", ModuleName). + Str("method", MethodShellBrowserWindow). + Logger() + + method := "Document.Application.ShellExecute" + + cmdline := execIO.CommandLine() + proc := cmdline[0] + args := cmdline[1] + + // Arguments must be passed in reverse order + if _, err := callComMethod(ctx, m.dispatchClient, + nil, + method, + stringToVariant(m.WindowState), + stringToVariant(""), // FUTURE? + stringToVariant(m.WorkingDirectory), + stringToVariant(args), + stringToVariant(proc)); err != nil { + + log.Error().Err(err).Msg("Failed to call method") + return fmt.Errorf("call %q: %w", method, err) + } + log.Info().Msg("Method call successful") + return +} diff --git a/pkg/goexec/dcom/shellwindows.go b/pkg/goexec/dcom/shellwindows.go index b137d66..67537ec 100644 --- a/pkg/goexec/dcom/shellwindows.go +++ b/pkg/goexec/dcom/shellwindows.go @@ -10,7 +10,7 @@ import ( ) const ( - MethodShellWindows = "ShellWindows" // MMC20.Application::Document.ActiveView.ExecuteShellCommand + MethodShellWindows = "ShellWindows" // ShellWindows::Item().Document.Application.ShellExecute ) type DcomShellWindows struct { @@ -27,7 +27,7 @@ func (m *DcomShellWindows) Execute(ctx context.Context, execIO *goexec.Execution log := zerolog.Ctx(ctx).With(). Str("module", ModuleName). - Str("method", MethodMmc). + Str("method", MethodShellWindows). Logger() method := "Item" -- cgit v1.2.3