mirror of
https://github.com/FalconOpsLLC/goexec
synced 2026-06-06 15:44:27 +00:00
dcom: new method: shellwindows
This commit is contained in:
+101
-85
@@ -1,32 +1,33 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"os"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"os"
|
||||
)
|
||||
|
||||
func registerLoggingFlags(fs *pflag.FlagSet) {
|
||||
fs.SortFlags = false
|
||||
fs.BoolVarP(&logDebug, "debug", "D", false, "Enable debug logging")
|
||||
fs.StringVarP(&logOutput, "log-file", "O", "", "Write JSON logging output to `file`")
|
||||
fs.BoolVarP(&logJson, "json", "j", false, "Write logging output in JSON lines")
|
||||
fs.BoolVarP(&logQuiet, "quiet", "q", false, "Disable info logging")
|
||||
fs.SortFlags = false
|
||||
fs.BoolVarP(&logDebug, "debug", "D", false, "Enable debug logging")
|
||||
fs.StringVarP(&logOutput, "log-file", "O", "", "Write JSON logging output to `file`")
|
||||
fs.BoolVarP(&logJson, "json", "j", false, "Write logging output in JSON lines")
|
||||
fs.BoolVarP(&logQuiet, "quiet", "q", false, "Disable info logging")
|
||||
}
|
||||
|
||||
func registerNetworkFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVarP(&proxy, "proxy", "x", "", "Proxy `URI`")
|
||||
fs.StringVarP(&rpcClient.Filter, "epm-filter", "F", "", "String binding to filter endpoints returned by the RPC endpoint mapper (EPM)")
|
||||
fs.StringVar(&rpcClient.Endpoint, "endpoint", "", "Explicit RPC endpoint definition")
|
||||
fs.BoolVar(&rpcClient.NoEpm, "no-epm", false, "Do not use EPM to automatically detect RPC endpoints")
|
||||
fs.BoolVar(&rpcClient.NoSign, "no-sign", false, "Disable signing on DCERPC messages")
|
||||
fs.BoolVar(&rpcClient.NoSeal, "no-seal", false, "Disable packet stub encryption on DCERPC messages")
|
||||
fs.StringVarP(&proxy, "proxy", "x", "", "Proxy `URI`")
|
||||
fs.StringVarP(&rpcClient.Filter, "epm-filter", "F", "", "String binding to filter endpoints returned by the RPC endpoint mapper (EPM)")
|
||||
fs.StringVar(&rpcClient.Endpoint, "endpoint", "", "Explicit RPC endpoint definition")
|
||||
fs.BoolVar(&rpcClient.NoEpm, "no-epm", false, "Do not use EPM to automatically detect RPC endpoints")
|
||||
fs.BoolVar(&rpcClient.NoSign, "no-sign", false, "Disable signing on DCERPC messages")
|
||||
fs.BoolVar(&rpcClient.NoSeal, "no-seal", false, "Disable packet stub encryption on DCERPC messages")
|
||||
|
||||
//cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter")
|
||||
//cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter")
|
||||
//cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter")
|
||||
//cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter")
|
||||
}
|
||||
|
||||
// FUTURE: automatically stage & execute file
|
||||
@@ -38,105 +39,120 @@ func registerStageFlags(fs *pflag.FlagSet) {
|
||||
*/
|
||||
|
||||
func registerExecutionFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVarP(&exec.Input.Executable, "exec", "e", "", "Remote Windows executable to invoke")
|
||||
fs.StringVarP(&exec.Input.Arguments, "args", "a", "", "Process command line arguments")
|
||||
fs.StringVarP(&exec.Input.Command, "command", "c", "", "Windows process command line (executable & arguments)")
|
||||
fs.StringVarP(&exec.Input.Executable, "exec", "e", "", "Remote Windows executable to invoke")
|
||||
fs.StringVarP(&exec.Input.Arguments, "args", "a", "", "Process command line arguments")
|
||||
fs.StringVarP(&exec.Input.Command, "command", "c", "", "Windows process command line (executable & arguments)")
|
||||
|
||||
//cmd.MarkFlagsOneRequired("executable", "command")
|
||||
//cmd.MarkFlagsMutuallyExclusive("executable", "command")
|
||||
//cmd.MarkFlagsOneRequired("executable", "command")
|
||||
//cmd.MarkFlagsMutuallyExclusive("executable", "command")
|
||||
}
|
||||
|
||||
func registerExecutionOutputFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVarP(&outputPath, "out", "o", "", `Fetch execution output to file or "-" for standard output`)
|
||||
fs.StringVarP(&outputMethod, "out-method", "m", "smb", "Method to fetch execution output")
|
||||
//fs.StringVar(&exec.Output.RemotePath, "out-remote", "", "Location to temporarily store output on remote filesystem")
|
||||
fs.BoolVar(&exec.Output.NoDelete, "no-delete-out", false, "Preserve output file on remote filesystem")
|
||||
fs.StringVarP(&outputPath, "out", "o", "", `Fetch execution output to file or "-" for standard output`)
|
||||
fs.StringVarP(&outputMethod, "out-method", "m", "smb", "Method to fetch execution output")
|
||||
//fs.StringVar(&exec.Output.RemotePath, "out-remote", "", "Location to temporarily store output on remote filesystem")
|
||||
fs.BoolVar(&exec.Output.NoDelete, "no-delete-out", false, "Preserve output file on remote filesystem")
|
||||
}
|
||||
|
||||
func args(reqs ...func(*cobra.Command, []string) error) (fn func(*cobra.Command, []string) error) {
|
||||
return func(cmd *cobra.Command, args []string) (err error) {
|
||||
return func(cmd *cobra.Command, args []string) (err error) {
|
||||
|
||||
for _, req := range reqs {
|
||||
if err = req(cmd, args); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, req := range reqs {
|
||||
if err = req(cmd, args); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func argsAcceptValues(name string, in *string, valid ...string) func(*cobra.Command, []string) error {
|
||||
return func(*cobra.Command, []string) error {
|
||||
for _, v := range valid {
|
||||
if *in == v {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if j, err := json.Marshal(valid); err == nil {
|
||||
return fmt.Errorf("parse %s: %q doesn't match any accepted values: %s", name, *in, string(j))
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func argsTarget(proto string) func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
return func(cmd *cobra.Command, args []string) (err error) {
|
||||
return func(cmd *cobra.Command, args []string) (err error) {
|
||||
|
||||
if len(args) != 1 {
|
||||
return errors.New("command require exactly one positional argument: [target]")
|
||||
}
|
||||
if len(args) != 1 {
|
||||
return errors.New("command require exactly one positional argument: [target]")
|
||||
}
|
||||
|
||||
if credential, target, err = adAuthOpts.WithTarget(context.TODO(), proto, args[0]); err != nil {
|
||||
return fmt.Errorf("failed to parse target: %w", err)
|
||||
}
|
||||
if credential, target, err = adAuthOpts.WithTarget(context.TODO(), proto, args[0]); err != nil {
|
||||
return fmt.Errorf("failed to parse target: %w", err)
|
||||
}
|
||||
|
||||
if credential == nil {
|
||||
return errors.New("no credentials supplied")
|
||||
}
|
||||
if target == nil {
|
||||
return errors.New("no target supplied")
|
||||
}
|
||||
return
|
||||
}
|
||||
if credential == nil {
|
||||
return errors.New("no credentials supplied")
|
||||
}
|
||||
if target == nil {
|
||||
return errors.New("no target supplied")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func argsSmbClient() func(cmd *cobra.Command, args []string) error {
|
||||
return args(
|
||||
argsTarget("cifs"),
|
||||
return args(
|
||||
argsTarget("cifs"),
|
||||
|
||||
func(_ *cobra.Command, _ []string) error {
|
||||
func(_ *cobra.Command, _ []string) error {
|
||||
|
||||
smbClient.Credential = credential
|
||||
smbClient.Target = target
|
||||
smbClient.Proxy = proxy
|
||||
smbClient.Credential = credential
|
||||
smbClient.Target = target
|
||||
smbClient.Proxy = proxy
|
||||
|
||||
return smbClient.Parse(context.TODO())
|
||||
},
|
||||
)
|
||||
return smbClient.Parse(context.TODO())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func argsRpcClient(proto string) func(cmd *cobra.Command, args []string) error {
|
||||
return args(
|
||||
argsTarget(proto),
|
||||
return args(
|
||||
argsTarget(proto),
|
||||
|
||||
func(cmd *cobra.Command, args []string) (err error) {
|
||||
func(cmd *cobra.Command, args []string) (err error) {
|
||||
|
||||
rpcClient.Target = target
|
||||
rpcClient.Credential = credential
|
||||
rpcClient.Proxy = proxy
|
||||
rpcClient.Target = target
|
||||
rpcClient.Credential = credential
|
||||
rpcClient.Proxy = proxy
|
||||
|
||||
return rpcClient.Parse(context.TODO())
|
||||
},
|
||||
)
|
||||
return rpcClient.Parse(context.TODO())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func argsOutput(methods ...string) func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
var as []func(*cobra.Command, []string) error
|
||||
var as []func(*cobra.Command, []string) error
|
||||
|
||||
for _, method := range methods {
|
||||
if method == "smb" {
|
||||
as = append(as, argsSmbClient())
|
||||
}
|
||||
}
|
||||
for _, method := range methods {
|
||||
if method == "smb" {
|
||||
as = append(as, argsSmbClient())
|
||||
}
|
||||
}
|
||||
|
||||
return args(append(as, func(*cobra.Command, []string) (err error) {
|
||||
return args(append(as, func(*cobra.Command, []string) (err error) {
|
||||
|
||||
if outputPath != "" {
|
||||
if outputPath == "-" {
|
||||
exec.Output.Writer = os.Stdout
|
||||
if outputPath != "" {
|
||||
if outputPath == "-" {
|
||||
exec.Output.Writer = os.Stdout
|
||||
|
||||
} else if exec.Output.Writer, err = os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to open output file")
|
||||
}
|
||||
}
|
||||
return
|
||||
})...)
|
||||
} else if exec.Output.Writer, err = os.OpenFile(outputPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to open output file")
|
||||
}
|
||||
}
|
||||
return
|
||||
})...)
|
||||
}
|
||||
|
||||
+113
-55
@@ -1,80 +1,138 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
dcomexec "github.com/FalconOpsLLC/goexec/pkg/goexec/dcom"
|
||||
"github.com/oiweiwei/go-msrpc/ssp/gssapi"
|
||||
"github.com/spf13/cobra"
|
||||
"context"
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
dcomexec "github.com/FalconOpsLLC/goexec/pkg/goexec/dcom"
|
||||
"github.com/oiweiwei/go-msrpc/ssp/gssapi"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func dcomCmdInit() {
|
||||
cmdFlags[dcomCmd] = []*flagSet{
|
||||
defaultAuthFlags,
|
||||
defaultLogFlags,
|
||||
defaultNetRpcFlags,
|
||||
}
|
||||
dcomMmcCmdInit()
|
||||
cmdFlags[dcomCmd] = []*flagSet{
|
||||
defaultAuthFlags,
|
||||
defaultLogFlags,
|
||||
defaultNetRpcFlags,
|
||||
}
|
||||
dcomMmcCmdInit()
|
||||
dcomShellWindowsCmdInit()
|
||||
|
||||
dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
|
||||
dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
|
||||
dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
|
||||
dcomCmd.AddCommand(dcomMmcCmd)
|
||||
dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
|
||||
dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
|
||||
dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
|
||||
dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd)
|
||||
}
|
||||
|
||||
func dcomMmcCmdInit() {
|
||||
dcomMmcExecFlags := newFlagSet("Execution")
|
||||
dcomMmcExecFlags := newFlagSet("Execution")
|
||||
|
||||
registerExecutionFlags(dcomMmcExecFlags.Flags)
|
||||
registerExecutionOutputFlags(dcomMmcExecFlags.Flags)
|
||||
registerExecutionFlags(dcomMmcExecFlags.Flags)
|
||||
registerExecutionOutputFlags(dcomMmcExecFlags.Flags)
|
||||
|
||||
dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WorkingDirectory, "directory", `C:\`, "Working `directory`")
|
||||
dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WindowState, "window", "Minimized", "Window state")
|
||||
dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WorkingDirectory, "directory", `C:\`, "Working `directory`")
|
||||
dcomMmcExecFlags.Flags.StringVar(&dcomMmc.WindowState, "window", "Minimized", "Window state")
|
||||
|
||||
cmdFlags[dcomMmcCmd] = []*flagSet{
|
||||
dcomMmcExecFlags,
|
||||
defaultAuthFlags,
|
||||
defaultLogFlags,
|
||||
defaultNetRpcFlags,
|
||||
}
|
||||
cmdFlags[dcomMmcCmd] = []*flagSet{
|
||||
dcomMmcExecFlags,
|
||||
defaultAuthFlags,
|
||||
defaultLogFlags,
|
||||
defaultNetRpcFlags,
|
||||
}
|
||||
dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags)
|
||||
|
||||
dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags)
|
||||
// Constraints
|
||||
{
|
||||
dcomMmcCmd.MarkFlagsOneRequired("command", "exec")
|
||||
}
|
||||
}
|
||||
|
||||
func dcomShellWindowsCmdInit() {
|
||||
dcomShellWindowsExecFlags := newFlagSet("Execution")
|
||||
|
||||
registerExecutionFlags(dcomShellWindowsExecFlags.Flags)
|
||||
registerExecutionOutputFlags(dcomShellWindowsExecFlags.Flags)
|
||||
|
||||
dcomShellWindowsExecFlags.Flags.StringVar(&dcomShellWindows.WorkingDirectory, "directory", `C:\`, "Working `directory`")
|
||||
dcomShellWindowsExecFlags.Flags.StringVar(&dcomShellWindows.WindowState, "app-window", "0", "Application window state `ID`")
|
||||
|
||||
cmdFlags[dcomShellWindowsCmd] = []*flagSet{
|
||||
dcomShellWindowsExecFlags,
|
||||
defaultAuthFlags,
|
||||
defaultLogFlags,
|
||||
defaultNetRpcFlags,
|
||||
}
|
||||
dcomShellWindowsCmd.Flags().AddFlagSet(dcomShellWindowsExecFlags.Flags)
|
||||
|
||||
// Constraints
|
||||
{
|
||||
dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
dcomMmc dcomexec.DcomMmc
|
||||
dcomMmc dcomexec.DcomMmc
|
||||
dcomShellWindows dcomexec.DcomShellWindows
|
||||
|
||||
dcomCmd = &cobra.Command{
|
||||
Use: "dcom",
|
||||
Short: "Execute with Distributed Component Object Model (MS-DCOM)",
|
||||
Long: `Description:
|
||||
dcomCmd = &cobra.Command{
|
||||
Use: "dcom",
|
||||
Short: "Execute with Distributed Component Object Model (MS-DCOM)",
|
||||
Long: `Description:
|
||||
The dcom module uses exposed Distributed Component Object Model (DCOM) objects to spawn processes.`,
|
||||
GroupID: "module",
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
GroupID: "module",
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
dcomMmcCmd = &cobra.Command{
|
||||
Use: "mmc [target]",
|
||||
Short: "Execute with the DCOM MMC20.Application object",
|
||||
Long: `Description:
|
||||
dcomMmcCmd = &cobra.Command{
|
||||
Use: "mmc [target]",
|
||||
Short: "Execute with the MMC20.Application DCOM object",
|
||||
Long: `Description:
|
||||
The mmc method uses the exposed MMC20.Application object to call Document.ActiveView.ShellExec,
|
||||
and ultimately spawn a process on the remote host.`,
|
||||
Args: args(
|
||||
argsRpcClient("host"),
|
||||
argsOutput("smb"),
|
||||
),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
dcomMmc.Client = &rpcClient
|
||||
dcomMmc.IO = exec
|
||||
Args: args(
|
||||
argsRpcClient("host"),
|
||||
argsOutput("smb"),
|
||||
argsAcceptValues("window", &dcomMmc.WindowState, "Minimized", "Maximized", "Restored"),
|
||||
),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
dcomMmc.Client = &rpcClient
|
||||
dcomMmc.IO = exec
|
||||
dcomMmc.ClassID = dcomexec.Mmc20Uuid
|
||||
|
||||
ctx := log.With().
|
||||
Str("module", "dcom").
|
||||
Str("method", "mmc").
|
||||
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
|
||||
ctx := log.With().
|
||||
Str("module", dcomexec.ModuleName).
|
||||
Str("method", dcomexec.MethodMmc).
|
||||
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
|
||||
|
||||
if err := goexec.ExecuteCleanMethod(ctx, &dcomMmc, &exec); err != nil {
|
||||
log.Fatal().Err(err).Msg("Operation failed")
|
||||
}
|
||||
},
|
||||
}
|
||||
if err := goexec.ExecuteCleanMethod(ctx, &dcomMmc, &exec); err != nil {
|
||||
log.Fatal().Err(err).Msg("Operation failed")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
dcomShellWindowsCmd = &cobra.Command{
|
||||
Use: "shellwindows [target]",
|
||||
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.`,
|
||||
Args: args(
|
||||
argsRpcClient("host"),
|
||||
argsOutput("smb"),
|
||||
argsAcceptValues("app-window", &dcomShellWindows.WindowState, "0", "1", "2", "3", "4", "5", "7", "10"),
|
||||
),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
dcomShellWindows.Client = &rpcClient
|
||||
dcomShellWindows.IO = exec
|
||||
dcomShellWindows.ClassID = dcomexec.ShellWindowsUuid
|
||||
|
||||
ctx := log.With().
|
||||
Str("module", dcomexec.ModuleName).
|
||||
Str("method", dcomexec.MethodShellWindows).
|
||||
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
|
||||
|
||||
if err := goexec.ExecuteCleanMethod(ctx, &dcomShellWindows, &exec); err != nil {
|
||||
log.Fatal().Err(err).Msg("Operation failed")
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -12,8 +12,9 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
//ShellWindowsUuid = uuid.MustParse("9BA05972-F6A8-11CF-A442-00A0C90A8F39")
|
||||
//Mmc20Uuid = uuid.MustParse("49B2791A-B1AE-4C90-9B8E-E860BA07F889")
|
||||
ShellBrowserWindowUuid = uuid.MustParse("C08AFD90-F2A1-11D1-8455-00A0C91F3880")
|
||||
ShellWindowsUuid = uuid.MustParse("9BA05972-F6A8-11CF-A442-00A0C90A8F39")
|
||||
Mmc20Uuid = uuid.MustParse("49B2791A-B1AE-4C90-9B8E-E860BA07F889")
|
||||
|
||||
RandCid = dcom.CID(*dtyp.GUIDFromUUID(uuid.MustParse(googleUUID.NewString())))
|
||||
IDispatchIID = &dcom.IID{
|
||||
|
||||
+31
-30
@@ -1,51 +1,52 @@
|
||||
package dcomexec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
"github.com/rs/zerolog"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
const (
|
||||
MethodMmc = "MMC" // MMC20.Application::Document.ActiveView.ExecuteShellCommand
|
||||
MethodMmc = "MMC" // MMC20.Application::Document.ActiveView.ExecuteShellCommand
|
||||
)
|
||||
|
||||
type DcomMmc struct {
|
||||
Dcom
|
||||
Dcom
|
||||
|
||||
IO goexec.ExecutionIO
|
||||
IO goexec.ExecutionIO
|
||||
|
||||
WorkingDirectory string
|
||||
WindowState string
|
||||
WorkingDirectory string
|
||||
WindowState string
|
||||
}
|
||||
|
||||
// Execute will perform command execution via the MMC20.Application DCOM object.
|
||||
func (m *DcomMmc) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) {
|
||||
|
||||
log := zerolog.Ctx(ctx).With().
|
||||
Str("module", ModuleName).
|
||||
Str("method", MethodMmc).
|
||||
Logger()
|
||||
log := zerolog.Ctx(ctx).With().
|
||||
Str("module", ModuleName).
|
||||
Str("method", MethodMmc).
|
||||
Logger()
|
||||
|
||||
method := "Document.ActiveView.ExecuteShellCommand"
|
||||
method := "Document.ActiveView.ExecuteShellCommand"
|
||||
|
||||
cmdline := execIO.CommandLine()
|
||||
proc := cmdline[0]
|
||||
args := cmdline[1]
|
||||
cmdline := execIO.CommandLine()
|
||||
proc := cmdline[0]
|
||||
args := cmdline[1]
|
||||
|
||||
// Arguments must be passed in reverse order
|
||||
if _, err := callComMethod(ctx,
|
||||
m.dispatchClient,
|
||||
method,
|
||||
stringToVariant(m.WindowState),
|
||||
stringToVariant(args),
|
||||
stringToVariant(m.WorkingDirectory),
|
||||
stringToVariant(proc)); err != nil {
|
||||
// Arguments must be passed in reverse order
|
||||
if _, err := callComMethod(ctx,
|
||||
m.dispatchClient,
|
||||
nil,
|
||||
method,
|
||||
stringToVariant(m.WindowState),
|
||||
stringToVariant(args),
|
||||
stringToVariant(m.WorkingDirectory),
|
||||
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
|
||||
log.Error().Err(err).Msg("Failed to call method")
|
||||
return fmt.Errorf("call %q: %w", method, err)
|
||||
}
|
||||
log.Info().Msg("Method call successful")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ type Dcom struct {
|
||||
goexec.Executor
|
||||
|
||||
Client *dce.Client
|
||||
ClassID string
|
||||
ClassID *uuid.UUID
|
||||
|
||||
dispatchClient idispatch.DispatchClient
|
||||
}
|
||||
@@ -49,9 +49,11 @@ func (m *Dcom) Init(ctx context.Context) (err error) {
|
||||
return errors.New("DCE connection not initialized")
|
||||
}
|
||||
|
||||
m.ClassID = "49B2791A-B1AE-4C90-9B8E-E860BA07F889"
|
||||
//m.ClassID = "9BA05972-F6A8-11CF-A442-00A0C90A8F39"
|
||||
class := dcom.ClassID(*dtyp.GUIDFromUUID(uuid.MustParse(m.ClassID)))
|
||||
if m.ClassID == nil {
|
||||
return errors.New("CLSID not specified")
|
||||
}
|
||||
|
||||
class := dcom.ClassID(*dtyp.GUIDFromUUID(m.ClassID))
|
||||
|
||||
if class.GUID() == nil {
|
||||
return fmt.Errorf("invalid class ID: %s", m.ClassID)
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package dcomexec
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
const (
|
||||
MethodShellWindows = "ShellWindows" // MMC20.Application::Document.ActiveView.ExecuteShellCommand
|
||||
)
|
||||
|
||||
type DcomShellWindows struct {
|
||||
Dcom
|
||||
|
||||
IO goexec.ExecutionIO
|
||||
|
||||
WorkingDirectory string
|
||||
WindowState string
|
||||
}
|
||||
|
||||
// Execute will perform command execution via the ShellWindows object. See https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/
|
||||
func (m *DcomShellWindows) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) {
|
||||
|
||||
log := zerolog.Ctx(ctx).With().
|
||||
Str("module", ModuleName).
|
||||
Str("method", MethodMmc).
|
||||
Logger()
|
||||
|
||||
method := "Item"
|
||||
|
||||
cmdline := execIO.CommandLine()
|
||||
proc := cmdline[0]
|
||||
args := cmdline[1]
|
||||
|
||||
iv, err := callComMethod(ctx,
|
||||
m.dispatchClient,
|
||||
nil,
|
||||
"Item")
|
||||
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to call method")
|
||||
return fmt.Errorf("call method %q: %w", method, err)
|
||||
}
|
||||
|
||||
item, ok := iv.VarResult.VarUnion.GetValue().(*oaut.Dispatch)
|
||||
if !ok {
|
||||
return errors.New("failed to get dispatch from ShellWindows::Item()")
|
||||
}
|
||||
|
||||
method = "Document.Application.ShellExecute"
|
||||
|
||||
// Arguments must be passed in reverse order
|
||||
if _, err := callComMethod(ctx, m.dispatchClient,
|
||||
item.InterfacePointer().
|
||||
GetStandardObjectReference().
|
||||
Std.IPID,
|
||||
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
|
||||
}
|
||||
@@ -13,13 +13,10 @@ import (
|
||||
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
|
||||
)
|
||||
|
||||
func callComMethod(ctx context.Context, dc idispatch.DispatchClient, method string, args ...*oaut.Variant) (ir *idispatch.InvokeResponse, err error) {
|
||||
func callComMethod(ctx context.Context, dc idispatch.DispatchClient, id *dcom.IPID, method string, args ...*oaut.Variant) (ir *idispatch.InvokeResponse, err error) {
|
||||
|
||||
parts := strings.Split(method, ".")
|
||||
|
||||
var id *dcom.IPID
|
||||
var gr *idispatch.GetIDsOfNamesResponse
|
||||
|
||||
for i, obj := range parts {
|
||||
|
||||
var opts []dcerpc.CallOption
|
||||
@@ -28,7 +25,7 @@ func callComMethod(ctx context.Context, dc idispatch.DispatchClient, method stri
|
||||
opts = append(opts, dcom.WithIPID(id))
|
||||
}
|
||||
|
||||
gr, err = dc.GetIDsOfNames(ctx, &idispatch.GetIDsOfNamesRequest{
|
||||
gr, err := dc.GetIDsOfNames(ctx, &idispatch.GetIDsOfNamesRequest{
|
||||
This: ORPCThis,
|
||||
IID: &dcom.IID{},
|
||||
LocaleID: LcEnglishUs,
|
||||
|
||||
Reference in New Issue
Block a user