aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorBryan McNulty <bryanmcnulty@protonmail.com>2025-04-19 10:24:13 -0500
committerBryan McNulty <bryanmcnulty@protonmail.com>2025-04-19 10:24:13 -0500
commit763ff79790dbca8b0b600abc8948411c795674c5 (patch)
treef3c3b5e5e7b4e9e3f5d837fcc4f9c27bb243494d /cmd
parent3c35b6d80a2d9faf71205492bba20f6694449c4b (diff)
downloadgoexec-763ff79790dbca8b0b600abc8948411c795674c5.tar.gz
goexec-763ff79790dbca8b0b600abc8948411c795674c5.zip
Major update to the CLI cosmetics
Diffstat (limited to 'cmd')
-rw-r--r--cmd/args.go187
-rw-r--r--cmd/dcom.go29
-rw-r--r--cmd/root.go123
-rw-r--r--cmd/scmr.go93
-rw-r--r--cmd/tsch.go79
-rw-r--r--cmd/wmi.go103
6 files changed, 371 insertions, 243 deletions
diff --git a/cmd/args.go b/cmd/args.go
index 4936784..8af8598 100644
--- a/cmd/args.go
+++ b/cmd/args.go
@@ -1,139 +1,134 @@
package cmd
import (
- "context"
- "errors"
- "fmt"
- "github.com/spf13/cobra"
- "github.com/spf13/pflag"
- "os"
+ "context"
+ "errors"
+ "fmt"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+ "os"
)
-func registerRpcFlags(cmd *cobra.Command) {
- rpcFlags := pflag.NewFlagSet("RPC", pflag.ExitOnError)
-
- rpcFlags.BoolVar(&rpcClient.NoEpm, "no-epm", false, "Do not use EPM to automatically detect endpoints")
- //rpcFlags.BoolVar(&rpcClient.Options.EpmAuto, "epm-auto", false, "Automatically detect endpoints instead of using the module defaults")
- rpcFlags.BoolVar(&rpcClient.NoSign, "no-sign", false, "Disable signing on DCE messages")
- rpcFlags.BoolVar(&rpcClient.NoSeal, "no-seal", false, "Disable packet stub encryption on DCE messages")
- rpcFlags.StringVar(&rpcClient.Filter, "epm-filter", "", "String binding to filter endpoints returned by EPM")
- rpcFlags.StringVar(&rpcClient.Endpoint, "endpoint", "", "Explicit RPC endpoint definition")
-
- cmd.PersistentFlags().AddFlagSet(rpcFlags)
-
- cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter")
- cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter")
+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")
}
-func registerProcessExecutionArgs(cmd *cobra.Command) {
- group := pflag.NewFlagSet("Execution", pflag.ExitOnError)
-
- group.StringVarP(&exec.Input.Arguments, "args", "a", "", "Command line arguments")
- group.StringVarP(&exec.Input.Command, "command", "c", "", "Windows process command line (executable & arguments)")
- group.StringVarP(&exec.Input.Executable, "executable", "e", "", "Windows executable to invoke")
+func registerNetworkFlags(fs *pflag.FlagSet) {
+ fs.StringVarP(&proxy, "proxy", "x", "", "Proxy `URI`")
+ fs.StringVar(&rpcClient.Endpoint, "endpoint", "", "Explicit RPC endpoint definition")
+ fs.StringVar(&rpcClient.Filter, "epm-filter", "", "String binding to filter endpoints returned by the RPC endpoint mapper (EPM)")
+ 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.PersistentFlags().AddFlagSet(group)
-
- cmd.MarkFlagsOneRequired("executable", "command")
- cmd.MarkFlagsMutuallyExclusive("executable", "command")
+ //cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter")
+ //cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter")
}
-func registerExecutionOutputArgs(cmd *cobra.Command) {
- group := pflag.NewFlagSet("Output", pflag.ExitOnError)
+func registerExecutionFlags(fs *pflag.FlagSet) {
+ fs.StringVarP(&exec.Input.Executable, "executable", "e", "", "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)")
- group.StringVarP(&outputPath, "output", "o", "", `Fetch execution output to file or "-" for standard output`)
- group.StringVarP(&outputMethod, "output-method", "m", "smb", "Method to fetch execution output")
- group.StringVar(&exec.Output.RemotePath, "remote-output", "", "Location to temporarily store output on remote filesystem")
- group.BoolVar(&exec.Output.NoDelete, "no-delete-output", false, "Preserve output file on remote filesystem")
+ //cmd.MarkFlagsOneRequired("executable", "command")
+ //cmd.MarkFlagsMutuallyExclusive("executable", "command")
+}
- cmd.PersistentFlags().AddFlagSet(group)
+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")
}
func args(reqs ...func(*cobra.Command, []string) error) (fn func(*cobra.Command, []string) error) {
-
- return func(cmd *cobra.Command, args []string) (err error) {
-
- for _, req := range reqs {
- if err = req(cmd, args); err != nil {
- return
- }
- }
- return
- }
+ return func(cmd *cobra.Command, args []string) (err error) {
+
+ for _, req := range reqs {
+ if err = req(cmd, args); err != nil {
+ return
+ }
+ }
+ return
+ }
}
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
+ })...)
}
diff --git a/cmd/dcom.go b/cmd/dcom.go
index f4e4f91..9b94043 100644
--- a/cmd/dcom.go
+++ b/cmd/dcom.go
@@ -9,17 +9,36 @@ import (
)
func dcomCmdInit() {
- registerRpcFlags(dcomCmd)
+ cmdFlags[dcomCmd] = []*flagSet{
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
dcomMmcCmdInit()
+
+ dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
+ dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
+ dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
dcomCmd.AddCommand(dcomMmcCmd)
}
func dcomMmcCmdInit() {
- dcomMmcCmd.Flags().StringVarP(&dcomMmc.WorkingDirectory, "directory", "d", `C:\`, "Working directory")
- dcomMmcCmd.Flags().StringVar(&dcomMmc.WindowState, "window", "Minimized", "Window state")
+ dcomMmcExecFlags := newFlagSet("Execution")
+
+ registerExecutionFlags(dcomMmcExecFlags.Flags)
+ registerExecutionOutputFlags(dcomMmcExecFlags.Flags)
+
+ 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,
+ }
- registerProcessExecutionArgs(dcomMmcCmd)
- registerExecutionOutputArgs(dcomMmcCmd)
+ dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags)
}
var (
diff --git a/cmd/root.go b/cmd/root.go
index 5e2a66d..08fcaed 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -17,8 +17,43 @@ import (
"os"
)
+type flagSet struct {
+ Label string
+ Flags *pflag.FlagSet
+}
+
+const helpTemplate = `Usage:{{if .Runnable}}
+ {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
+ {{.CommandPath}} [command] [flags]{{end}}{{if gt (len .Aliases) 0}}
+
+Aliases:
+{{.NameAndAliases}}{{end}}{{if .HasExample}}
+
+Examples:
+{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
+
+Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
+
+{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
+
+Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
+ {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if (ne .Name "completion")}}{{range $_, $v := cmdFlags .}}
+
+{{$v.Label|trimTrailingWhitespaces}}:
+{{flags $v.Flags|trimTrailingWhitespaces}}{{end}}{{end}}{{if .HasHelpSubCommands}}
+
+Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
+{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
+
+Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
+`
+
var (
- flagGroups = map[string]*pflag.FlagSet{}
+ cmdFlags = make(map[*cobra.Command][]*flagSet)
+
+ defaultAuthFlags, defaultLogFlags, defaultNetRpcFlags *flagSet
returnCode int
outputMethod string
@@ -105,28 +140,13 @@ var (
}
)
-func addFlagSet(fs *pflag.FlagSet) {
- flagGroups[fs.Name()] = fs
-}
-
-func moduleFlags(cmd *cobra.Command, module string) (fs *pflag.FlagSet) {
- fs, _ = flagGroups[module]
- return
-}
-
-// Uses the users terminal size or width of 80 if cannot determine users width
-// Based on https://github.com/spf13/cobra/issues/1805#issuecomment-1246192724
-func wrappedFlagUsages(cmd *pflag.FlagSet) string {
- fd := int(os.Stdout.Fd())
- width := 80
-
- // Get the terminal width and dynamically set
- termWidth, _, err := term.GetSize(fd)
- if err == nil {
- width = termWidth
+func newFlagSet(name string) *flagSet {
+ flags := pflag.NewFlagSet(name, pflag.ExitOnError)
+ flags.SortFlags = false
+ return &flagSet{
+ Label: name,
+ Flags: flags,
}
-
- return cmd.FlagUsagesWrapped(width - 1)
}
func init() {
@@ -140,9 +160,21 @@ func init() {
// Cobra init
{
cobra.EnableCommandSorting = false
-
- rootCmd.InitDefaultVersionFlag()
- rootCmd.InitDefaultHelpCmd()
+ {
+ defaultNetRpcFlags = newFlagSet("Network")
+ registerNetworkFlags(defaultNetRpcFlags.Flags)
+ }
+ {
+ defaultLogFlags = newFlagSet("Logging")
+ registerLoggingFlags(defaultLogFlags.Flags)
+ }
+ {
+ defaultAuthFlags = newFlagSet("Authentication")
+ adAuthOpts = &adauth.Options{
+ Debug: log.Debug().Msgf,
+ }
+ adAuthOpts.RegisterFlags(defaultAuthFlags.Flags)
+ }
modules := &cobra.Group{
ID: "module",
@@ -150,33 +182,26 @@ func init() {
}
rootCmd.AddGroup(modules)
- // Logging flags
- {
- logOpts := pflag.NewFlagSet("Logging", pflag.ExitOnError)
- logOpts.BoolVar(&logDebug, "debug", false, "Enable debug logging")
- logOpts.BoolVar(&logJson, "json", false, "Write logging output in JSON lines")
- logOpts.BoolVar(&logQuiet, "quiet", false, "Disable info logging")
- logOpts.StringVarP(&logOutput, "log-file", "O", "", "Write JSON logging output to `file`")
- rootCmd.PersistentFlags().AddFlagSet(logOpts)
- flagGroups["Logging"] = logOpts
+ cmdFlags[rootCmd] = []*flagSet{
+ defaultLogFlags,
+ defaultAuthFlags,
}
- // Global networking flags
- {
- netOpts := pflag.NewFlagSet("Network", pflag.ExitOnError)
- netOpts.StringVarP(&proxy, "proxy", "x", "", "Proxy `URI`")
- rootCmd.PersistentFlags().AddFlagSet(netOpts)
- }
-
- // Authentication flags
- {
- adAuthOpts = &adauth.Options{
- Debug: log.Debug().Msgf,
+ cobra.AddTemplateFunc("flags", func(fs *pflag.FlagSet) string {
+ if width, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil {
+ return fs.FlagUsagesWrapped(width - 1)
}
- authOpts := pflag.NewFlagSet("Authentication", pflag.ExitOnError)
- adAuthOpts.RegisterFlags(authOpts)
- rootCmd.PersistentFlags().AddFlagSet(authOpts)
- }
+ return fs.FlagUsagesWrapped(80 - 1)
+ })
+
+ cobra.AddTemplateFunc("cmdFlags", func(cmd *cobra.Command) []*flagSet {
+ return cmdFlags[cmd]
+ })
+
+ rootCmd.InitDefaultVersionFlag()
+ rootCmd.InitDefaultHelpCmd()
+ rootCmd.SetHelpTemplate("{{if (ne .Long \"\")}}{{.Long}}\n{{end}}" + helpTemplate)
+ rootCmd.SetUsageTemplate(helpTemplate)
// Modules init
{
diff --git a/cmd/scmr.go b/cmd/scmr.go
index b67b2a6..5dde36f 100644
--- a/cmd/scmr.go
+++ b/cmd/scmr.go
@@ -10,43 +10,90 @@ import (
)
func scmrCmdInit() {
- registerRpcFlags(scmrCmd)
+ cmdFlags[scmrCmd] = []*flagSet{
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
scmrCreateCmdInit()
- scmrCmd.AddCommand(scmrCreateCmd)
scmrChangeCmdInit()
- scmrCmd.AddCommand(scmrChangeCmd)
scmrDeleteCmdInit()
- scmrCmd.AddCommand(scmrDeleteCmd)
+
+ scmrCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
+ scmrCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
+ scmrCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
+ scmrCmd.AddCommand(scmrCreateCmd, scmrChangeCmd, scmrDeleteCmd)
}
func scmrCreateCmdInit() {
- scmrCreateCmd.Flags().StringVarP(&scmrCreate.DisplayName, "display-name", "n", "", "Display name of service to create")
- scmrCreateCmd.Flags().StringVarP(&scmrCreate.ServiceName, "service-name", "s", "", "Name of service to create")
- scmrCreateCmd.Flags().BoolVar(&scmrCreate.NoDelete, "no-delete", false, "Don't delete service after execution")
- scmrCreateCmd.Flags().BoolVar(&scmrCreate.NoStart, "no-start", false, "Don't start service")
+ scmrCreateFlags := newFlagSet("Service")
- scmrCreateCmd.Flags().StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to a remote Windows executable")
- scmrCreateCmd.Flags().StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to the executable")
+ scmrCreateFlags.Flags.StringVarP(&scmrCreate.DisplayName, "display-name", "n", "", "Display name of service to create")
+ scmrCreateFlags.Flags.StringVarP(&scmrCreate.ServiceName, "service", "s", "", "Name of service to create")
+ scmrCreateFlags.Flags.BoolVar(&scmrCreate.NoDelete, "no-delete", false, "Don't delete service after execution")
+ scmrCreateFlags.Flags.BoolVar(&scmrCreate.NoStart, "no-start", false, "Don't start service")
- scmrCreateCmd.MarkFlagsMutuallyExclusive("no-delete", "no-start")
+ scmrCreateExecFlags := newFlagSet("Execution")
- if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil {
- panic(err)
+ // TODO: SCMR output
+ //registerExecutionOutputFlags(scmrCreateExecFlags.Flags)
+
+ scmrCreateExecFlags.Flags.StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to a remote Windows executable")
+ scmrCreateExecFlags.Flags.StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to the executable")
+
+ scmrCreateCmd.Flags().AddFlagSet(scmrCreateFlags.Flags)
+ scmrCreateCmd.Flags().AddFlagSet(scmrCreateExecFlags.Flags)
+
+ cmdFlags[scmrCreateCmd] = []*flagSet{
+ scmrCreateExecFlags,
+ scmrCreateFlags,
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
+
+ // Constraints
+ {
+ scmrCreateCmd.MarkFlagsMutuallyExclusive("no-delete", "no-start")
+ if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil {
+ panic(err)
+ }
}
}
func scmrChangeCmdInit() {
- scmrChangeCmd.Flags().BoolVar(&scmrChange.NoStart, "no-start", false, "Don't start service")
- scmrChangeCmd.Flags().StringVarP(&scmrChange.ServiceName, "service-name", "s", "", "Name of service to modify")
+ scmrChangeFlags := newFlagSet("Service Control")
- scmrChangeCmd.Flags().StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to remote Windows executable")
- scmrChangeCmd.Flags().StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to executable")
+ scmrChangeFlags.Flags.StringVarP(&scmrChange.ServiceName, "service-name", "s", "", "Name of service to modify")
+ scmrChangeFlags.Flags.BoolVar(&scmrChange.NoStart, "no-start", false, "Don't start service")
- if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil {
- panic(err)
+ scmrChangeExecFlags := newFlagSet("Execution")
+
+ scmrChangeExecFlags.Flags.StringVarP(&exec.Input.ExecutablePath, "executable-path", "f", "", "Full path to remote Windows executable")
+ scmrChangeExecFlags.Flags.StringVarP(&exec.Input.Arguments, "args", "a", "", "Arguments to pass to executable")
+
+ // TODO: SCMR output
+ //registerExecutionOutputFlags(scmrChangeExecFlags.Flags)
+
+ cmdFlags[scmrChangeCmd] = []*flagSet{
+ scmrChangeFlags,
+ scmrChangeExecFlags,
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
}
- if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil {
- panic(err)
+
+ scmrChangeCmd.Flags().AddFlagSet(scmrChangeFlags.Flags)
+ scmrChangeCmd.Flags().AddFlagSet(scmrChangeExecFlags.Flags)
+
+ // Constraints
+ {
+ if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil {
+ panic(err)
+ }
+ if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil {
+ panic(err)
+ }
}
}
@@ -94,7 +141,7 @@ References:
WithContext(ctx)
if scmrCreate.ServiceName == "" {
- log.Warn().Msg("No service name was provided. Using a random string")
+ log.Warn().Msg("No service Label was provided. Using a random string")
scmrCreate.ServiceName = util.RandomString()
}
@@ -103,7 +150,7 @@ References:
}
if scmrCreate.DisplayName == "" {
- log.Debug().Msg("No display name specified, using service name as display name")
+ log.Debug().Msg("No display Label specified, using service Label as display Label")
scmrCreate.DisplayName = scmrCreate.ServiceName
}
diff --git a/cmd/tsch.go b/cmd/tsch.go
index 0ed5d9f..d8c8853 100644
--- a/cmd/tsch.go
+++ b/cmd/tsch.go
@@ -12,36 +12,71 @@ import (
)
func tschCmdInit() {
- registerRpcFlags(tschCmd)
-
+ cmdFlags[tschCmd] = []*flagSet{
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
tschDemandCmdInit()
- tschCmd.AddCommand(tschDemandCmd)
-
tschCreateCmdInit()
- tschCmd.AddCommand(tschCreateCmd)
+
+ tschCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
+ tschCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
+ tschCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
+ tschCmd.AddCommand(tschDemandCmd, tschCreateCmd)
}
func tschDemandCmdInit() {
- tschDemandCmd.Flags().StringVarP(&tschTask, "task", "t", "", "Name or path of the new task")
- tschDemandCmd.Flags().Uint32Var(&tschDemand.SessionId, "session", 0, "Hijack existing session given the session ID")
- tschDemandCmd.Flags().BoolVar(&tschDemand.NoDelete, "no-delete", false, "Don't delete task after execution")
- tschDemandCmd.Flags().StringVar(&tschDemand.UserSid, "sid", "S-1-5-18", "User SID to impersonate")
+ tschDemandFlags := newFlagSet("Task Scheduler")
+
+ tschDemandFlags.Flags.StringVarP(&tschTask, "task", "t", "", "Name or path of the new task")
+ tschDemandFlags.Flags.Uint32Var(&tschDemand.SessionId, "session", 0, "Hijack existing session given the session ID")
+ tschDemandFlags.Flags.StringVar(&tschDemand.UserSid, "sid", "S-1-5-18", "User SID to impersonate")
+ tschDemandFlags.Flags.BoolVar(&tschDemand.NoDelete, "no-delete", false, "Don't delete task after execution")
+
+ tschDemandExecFlags := newFlagSet("Execution")
- registerProcessExecutionArgs(tschDemandCmd)
- registerExecutionOutputArgs(tschDemandCmd)
+ registerExecutionFlags(tschDemandExecFlags.Flags)
+ registerExecutionOutputFlags(tschDemandExecFlags.Flags)
+
+ cmdFlags[tschDemandCmd] = []*flagSet{
+ tschDemandFlags,
+ tschDemandExecFlags,
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
+
+ tschDemandCmd.Flags().AddFlagSet(tschDemandFlags.Flags)
+ tschDemandCmd.Flags().AddFlagSet(tschDemandExecFlags.Flags)
}
func tschCreateCmdInit() {
- tschCreateCmd.Flags().StringVarP(&tschTask, "task", "t", "", "Name or path of the new task")
- tschCreateCmd.Flags().DurationVar(&tschCreate.StopDelay, "delay-stop", 5*time.Second, "Delay between task execution and termination. This won't stop the spawned process")
- tschCreateCmd.Flags().DurationVar(&tschCreate.StartDelay, "start-delay", 5*time.Second, "Delay between task registration and execution")
- tschCreateCmd.Flags().DurationVar(&tschCreate.DeleteDelay, "delete-delay", 0*time.Second, "Delay between task termination and deletion")
- tschCreateCmd.Flags().BoolVar(&tschCreate.NoDelete, "no-delete", false, "Don't delete task after execution")
- tschCreateCmd.Flags().BoolVar(&tschCreate.CallDelete, "call-delete", false, "Directly call SchRpcDelete to delete task")
- tschCreateCmd.Flags().StringVar(&tschCreate.UserSid, "sid", "S-1-5-18", "User SID to impersonate")
-
- registerProcessExecutionArgs(tschCreateCmd)
- registerExecutionOutputArgs(tschCreateCmd)
+ tschCreateFlags := newFlagSet("Task Scheduler")
+
+ tschCreateFlags.Flags.StringVarP(&tschTask, "task", "t", "", "Name or path of the new task")
+ tschCreateFlags.Flags.DurationVar(&tschCreate.StopDelay, "delay-stop", 5*time.Second, "Delay between task execution and termination. This won't stop the spawned process")
+ tschCreateFlags.Flags.DurationVar(&tschCreate.StartDelay, "start-delay", 5*time.Second, "Delay between task registration and execution")
+ tschCreateFlags.Flags.DurationVar(&tschCreate.DeleteDelay, "delete-delay", 0*time.Second, "Delay between task termination and deletion")
+ tschCreateFlags.Flags.BoolVar(&tschCreate.NoDelete, "no-delete", false, "Don't delete task after execution")
+ tschCreateFlags.Flags.BoolVar(&tschCreate.CallDelete, "call-delete", false, "Directly call SchRpcDelete to delete task")
+ tschCreateFlags.Flags.StringVar(&tschCreate.UserSid, "sid", "S-1-5-18", "User `SID` to impersonate")
+
+ tschCreateExecFlags := newFlagSet("Execution")
+
+ registerExecutionFlags(tschCreateExecFlags.Flags)
+ registerExecutionOutputFlags(tschCreateExecFlags.Flags)
+
+ cmdFlags[tschCreateCmd] = []*flagSet{
+ tschCreateFlags,
+ tschCreateExecFlags,
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
+
+ tschCreateCmd.Flags().AddFlagSet(tschCreateFlags.Flags)
+ tschCreateCmd.Flags().AddFlagSet(tschCreateExecFlags.Flags)
}
func argsTask(*cobra.Command, []string) error {
@@ -52,7 +87,7 @@ func argsTask(*cobra.Command, []string) error {
case tschexec.ValidateTaskName(tschTask) == nil:
tschTask = `\` + tschTask
default:
- return fmt.Errorf("invalid task name or path: %q", tschTask)
+ return fmt.Errorf("invalid task Label or path: %q", tschTask)
}
return nil
}
diff --git a/cmd/wmi.go b/cmd/wmi.go
index 5e2d279..c4d48a8 100644
--- a/cmd/wmi.go
+++ b/cmd/wmi.go
@@ -3,33 +3,44 @@ package cmd
import (
"context"
"encoding/json"
- "fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
wmiexec "github.com/FalconOpsLLC/goexec/pkg/goexec/wmi"
"github.com/oiweiwei/go-msrpc/ssp/gssapi"
"github.com/spf13/cobra"
+ "os"
)
func wmiCmdInit() {
- registerRpcFlags(wmiCmd)
-
+ cmdFlags[wmiCmd] = []*flagSet{
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
wmiCallCmdInit()
- wmiCmd.AddCommand(wmiCallCmd)
-
wmiProcCmdInit()
- wmiCmd.AddCommand(wmiProcCmd)
-}
-func wmiCallArgs(_ *cobra.Command, _ []string) error {
- return json.Unmarshal([]byte(wmiArguments), &wmiCall.Args)
+ wmiCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
+ wmiCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
+ wmiCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
+ wmiCmd.AddCommand(wmiProcCmd, wmiCallCmd)
}
func wmiCallCmdInit() {
- wmiCallCmd.Flags().StringVarP(&wmiCall.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace")
- wmiCallCmd.Flags().StringVarP(&wmiCall.Class, "class", "C", "", `WMI class to instantiate (i.e. "Win32_Process")`)
- wmiCallCmd.Flags().StringVarP(&wmiCall.Method, "method", "m", "", `WMI Method to call (i.e. "Create")`)
- wmiCallCmd.Flags().StringVarP(&wmiArguments, "args", "A", "{}", `WMI Method argument(s) in JSON dictionary format (i.e. {"Command":"calc.exe"})`)
+ wmiCallFlags := newFlagSet("WMI")
+
+ wmiCallFlags.Flags.StringVarP(&wmiCall.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace")
+ wmiCallFlags.Flags.StringVarP(&wmiCall.Class, "class", "C", "", `WMI class to instantiate (i.e. "Win32_Process")`)
+ wmiCallFlags.Flags.StringVarP(&wmiCall.Method, "method", "m", "", `WMI Method to call (i.e. "Create")`)
+ wmiCallFlags.Flags.StringVarP(&wmiArguments, "args", "A", "{}", `WMI Method argument(s) in JSON dictionary format (i.e. {"Command":"calc.exe"})`)
+
+ wmiCallCmd.Flags().AddFlagSet(wmiCallFlags.Flags)
+ cmdFlags[wmiCallCmd] = []*flagSet{
+ wmiCallFlags,
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
if err := wmiCallCmd.MarkFlagRequired("class"); err != nil {
panic(err)
}
@@ -39,11 +50,26 @@ func wmiCallCmdInit() {
}
func wmiProcCmdInit() {
- wmiProcCmd.Flags().StringVarP(&wmiProc.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace")
- wmiProcCmd.Flags().StringVarP(&wmiProc.WorkingDirectory, "directory", "d", `C:\`, "Working directory")
+ wmiProcFlags := newFlagSet("WMI")
+
+ wmiProcFlags.Flags.StringVarP(&wmiProc.Resource, "namespace", "n", "//./root/cimv2", "WMI namespace")
+ wmiProcFlags.Flags.StringVarP(&wmiProc.WorkingDirectory, "directory", "d", `C:\`, "Working directory")
+
+ wmiProcExecFlags := newFlagSet("Execution")
- registerProcessExecutionArgs(wmiProcCmd)
- registerExecutionOutputArgs(wmiProcCmd)
+ registerExecutionFlags(wmiProcExecFlags.Flags)
+ registerExecutionOutputFlags(wmiProcExecFlags.Flags)
+
+ cmdFlags[wmiProcCmd] = []*flagSet{
+ wmiProcExecFlags,
+ wmiProcFlags,
+ defaultAuthFlags,
+ defaultLogFlags,
+ defaultNetRpcFlags,
+ }
+
+ wmiProcCmd.Flags().AddFlagSet(wmiProcFlags.Flags)
+ wmiProcCmd.Flags().AddFlagSet(wmiProcExecFlags.Flags)
}
var (
@@ -69,43 +95,24 @@ var (
References:
https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-classes
`,
- Args: args(argsRpcClient("host"), wmiCallArgs),
+ Args: args(
+ argsRpcClient("cifs"),
+ func(cmd *cobra.Command, args []string) error {
+ return json.Unmarshal([]byte(wmiArguments), &wmiCall.Args)
+ }),
Run: func(cmd *cobra.Command, args []string) {
- var err error
-
- ctx := gssapi.NewSecurityContext(context.Background())
+ wmiCall.Client = &rpcClient
+ wmiCall.Out = os.Stdout
- ctx = log.With().
+ ctx := log.With().
Str("module", "wmi").
Str("method", "call").
- Logger().
- WithContext(ctx)
-
- if err = rpcClient.Connect(ctx); err != nil {
- log.Fatal().Err(err).Msg("Connection failed")
- }
-
- defer func() {
- closeErr := rpcClient.Close(ctx)
- if closeErr != nil {
- log.Error().Err(closeErr).Msg("Failed to close connection")
- }
- }()
-
- if err = wmiCall.Init(ctx); err != nil {
- log.Error().Err(err).Msg("Module initialization failed")
- returnCode = 2
- return
- }
+ Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
- out, err := wmiCall.Call(ctx)
- if err != nil {
- log.Error().Err(err).Msg("Call failed")
- returnCode = 4
- return
+ if err := goexec.ExecuteAuxiliaryMethod(ctx, &wmiCall); err != nil {
+ log.Fatal().Err(err).Msg("Operation failed")
}
- fmt.Println(string(out))
},
}
@@ -121,8 +128,8 @@ References:
https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-process
`,
Args: args(
+ argsRpcClient("cifs"),
argsOutput("smb"),
- argsRpcClient("host"),
),
Run: func(cmd *cobra.Command, args []string) {