aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md16
-rw-r--r--cmd/scmr.go184
-rw-r--r--cmd/tsch.go36
-rw-r--r--internal/exec/tsch/exec.go22
-rw-r--r--internal/exec/tsch/tsch.go18
5 files changed, 144 insertions, 132 deletions
diff --git a/TODO.md b/TODO.md
index d4e206f..bbfbe9e 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,9 +4,17 @@
### Higher Priority
- [X] Add WMI module
+- [X] Clean up TSCH module
+
+- [ ] Clean up SCMR module
+ - [ ] add dynamic string binding support
+ - [ ] general clean up. Use TSCH & WMI as reference
+
+- [ ] Add DCOM module
+ - [ ] MMC20.Application method
+
- [ ] Add psexec module (RemComSvc)
-- [ ] Testing against different Windows machines
-- [ ] Testing from Windows (compile to PE)
+ - [ ] Add support for dynamic service executable (of course)
### Other
- [ ] Fix SCMR `change` method so that dependencies field isn't permanently overwritten
@@ -14,6 +22,10 @@
- [ ] Standardize modules to interface for future use
- [ ] Add command to tsch - update task if it already exists. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/849c131a-64e4-46ef-b015-9d4c599c5167 (`flags` argument)
+### Testing
+- [ ] Testing against different Windows machines & versions
+- [ ] Testing from Windows (compile to PE)
+
## Resolve Eventually
### Higher Priority
diff --git a/cmd/scmr.go b/cmd/scmr.go
index 8d453a5..f2f0d51 100644
--- a/cmd/scmr.go
+++ b/cmd/scmr.go
@@ -1,116 +1,116 @@
package cmd
import (
- "fmt"
- "github.com/FalconOpsLLC/goexec/internal/exec"
- scmrexec2 "github.com/FalconOpsLLC/goexec/internal/exec/scmr"
- "github.com/FalconOpsLLC/goexec/internal/windows"
- "github.com/RedTeamPentesting/adauth"
- "github.com/spf13/cobra"
+ "fmt"
+ "github.com/FalconOpsLLC/goexec/internal/exec"
+ "github.com/FalconOpsLLC/goexec/internal/windows"
+ "github.com/RedTeamPentesting/adauth"
+ "github.com/spf13/cobra"
- scmrexec "github.com/FalconOpsLLC/goexec/internal/exec/scmr"
+ scmrexec "github.com/FalconOpsLLC/goexec/internal/exec/scmr"
)
func scmrCmdInit() {
- scmrCmd.PersistentFlags().StringVarP(&executablePath, "executable-path", "f", "", "Full path to remote Windows executable")
- scmrCmd.PersistentFlags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to executable")
- scmrCmd.PersistentFlags().StringVarP(&scmrName, "service-name", "s", "", "Name of service to create or modify")
+ registerRpcFlags(scmrCmd)
+ scmrCmd.PersistentFlags().StringVarP(&executablePath, "executable-path", "f", "", "Full path to remote Windows executable")
+ scmrCmd.PersistentFlags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to executable")
+ scmrCmd.PersistentFlags().StringVarP(&scmrName, "service-name", "s", "", "Name of service to create or modify")
- scmrCmd.MarkPersistentFlagRequired("executable-path")
- scmrCmd.MarkPersistentFlagRequired("service-name")
+ scmrCmd.MarkPersistentFlagRequired("executable-path")
+ scmrCmd.MarkPersistentFlagRequired("service-name")
- scmrCmd.AddCommand(scmrChangeCmd)
- scmrCreateCmdInit()
- scmrCmd.AddCommand(scmrCreateCmd)
- scmrChangeCmdInit()
+ scmrCmd.AddCommand(scmrChangeCmd)
+ scmrCreateCmdInit()
+ scmrCmd.AddCommand(scmrCreateCmd)
+ scmrChangeCmdInit()
}
func scmrChangeCmdInit() {
- scmrChangeCmd.Flags().StringVarP(&scmrDisplayName, "display-name", "n", "", "Display name of service to create")
- scmrChangeCmd.Flags().BoolVar(&scmrNoStart, "no-start", false, "Don't start service")
+ scmrChangeCmd.Flags().StringVarP(&scmrDisplayName, "display-name", "n", "", "Display name of service to create")
+ scmrChangeCmd.Flags().BoolVar(&scmrNoStart, "no-start", false, "Don't start service")
}
func scmrCreateCmdInit() {
- scmrCreateCmd.Flags().BoolVar(&scmrNoDelete, "no-delete", false, "Don't delete service after execution")
+ scmrCreateCmd.Flags().BoolVar(&scmrNoDelete, "no-delete", false, "Don't delete service after execution")
}
var (
- // scmr arguments
- scmrName string
- scmrDisplayName string
- scmrNoDelete bool
- scmrNoStart bool
+ // scmr arguments
+ scmrName string
+ scmrDisplayName string
+ scmrNoDelete bool
+ scmrNoStart bool
- scmrArgs = func(cmd *cobra.Command, args []string) (err error) {
- if len(args) != 1 {
- return fmt.Errorf("expected exactly 1 positional argument, got %d", len(args))
- }
- if creds, target, err = authOpts.WithTarget(ctx, "cifs", args[0]); err != nil {
- return fmt.Errorf("failed to parse target: %w", err)
- }
- log.Debug().Str("target", args[0]).Msg("Resolved target")
- return nil
- }
+ scmrArgs = func(cmd *cobra.Command, args []string) (err error) {
+ if len(args) != 1 {
+ return fmt.Errorf("expected exactly 1 positional argument, got %d", len(args))
+ }
+ if creds, target, err = authOpts.WithTarget(ctx, "cifs", args[0]); err != nil {
+ return fmt.Errorf("failed to parse target: %w", err)
+ }
+ log.Debug().Str("target", args[0]).Msg("Resolved target")
+ return nil
+ }
- creds *adauth.Credential
- target *adauth.Target
+ creds *adauth.Credential
+ target *adauth.Target
- scmrCmd = &cobra.Command{
- Use: "scmr",
- Short: "Establish execution via SCMR",
- Args: cobra.NoArgs,
- }
- scmrCreateCmd = &cobra.Command{
- Use: "create [target]",
- Short: "Create & run a new Windows service to gain execution",
- Args: scmrArgs,
- RunE: func(cmd *cobra.Command, args []string) (err error) {
- if scmrNoDelete {
- log.Warn().Msg("Service will not be deleted after execution")
- }
- if scmrDisplayName == "" {
- scmrDisplayName = scmrName
- log.Warn().Msg("No display name specified, using service name as display name")
- }
- executor := scmrexec.Module{}
- execCfg := &exec.ExecutionConfig{
- ExecutablePath: executablePath,
- ExecutableArgs: executableArgs,
- ExecutionMethod: scmrexec2.MethodCreate,
+ scmrCmd = &cobra.Command{
+ Use: "scmr",
+ Short: "Establish execution via SCMR",
+ Args: cobra.NoArgs,
+ }
+ scmrCreateCmd = &cobra.Command{
+ Use: "create [target]",
+ Short: "Create & run a new Windows service to gain execution",
+ Args: scmrArgs,
+ RunE: func(cmd *cobra.Command, args []string) (err error) {
+ if scmrNoDelete {
+ log.Warn().Msg("Service will not be deleted after execution")
+ }
+ if scmrDisplayName == "" {
+ scmrDisplayName = scmrName
+ log.Warn().Msg("No display name specified, using service name as display name")
+ }
+ executor := scmrexec.Module{}
+ execCfg := &exec.ExecutionConfig{
+ ExecutablePath: executablePath,
+ ExecutableArgs: executableArgs,
+ ExecutionMethod: scmrexec.MethodCreate,
- ExecutionMethodConfig: scmrexec2.MethodCreateConfig{
- NoDelete: scmrNoDelete,
- ServiceName: scmrName,
- DisplayName: scmrDisplayName,
- ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
- StartType: windows.SERVICE_DEMAND_START,
- },
- }
- if err := executor.Exec(log.WithContext(ctx), creds, target, execCfg); err != nil {
- log.Fatal().Err(err).Msg("SCMR execution failed")
- }
- return nil
- },
- }
- scmrChangeCmd = &cobra.Command{
- Use: "change [target]",
- Short: "Change an existing Windows service to gain execution",
- Args: scmrArgs,
- Run: func(cmd *cobra.Command, args []string) {
- executor := scmrexec.Module{}
- execCfg := &exec.ExecutionConfig{
- ExecutablePath: executablePath,
- ExecutableArgs: executableArgs,
- ExecutionMethod: scmrexec2.MethodModify,
+ ExecutionMethodConfig: scmrexec.MethodCreateConfig{
+ NoDelete: scmrNoDelete,
+ ServiceName: scmrName,
+ DisplayName: scmrDisplayName,
+ ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
+ StartType: windows.SERVICE_DEMAND_START,
+ },
+ }
+ if err := executor.Exec(log.WithContext(ctx), creds, target, execCfg); err != nil {
+ log.Fatal().Err(err).Msg("SCMR execution failed")
+ }
+ return nil
+ },
+ }
+ scmrChangeCmd = &cobra.Command{
+ Use: "change [target]",
+ Short: "Change an existing Windows service to gain execution",
+ Args: scmrArgs,
+ Run: func(cmd *cobra.Command, args []string) {
+ executor := scmrexec.Module{}
+ execCfg := &exec.ExecutionConfig{
+ ExecutablePath: executablePath,
+ ExecutableArgs: executableArgs,
+ ExecutionMethod: scmrexec.MethodModify,
- ExecutionMethodConfig: scmrexec2.MethodModifyConfig{
- NoStart: scmrNoStart,
- ServiceName: scmrName,
- },
- }
- if err := executor.Exec(log.WithContext(ctx), creds, target, execCfg); err != nil {
- log.Fatal().Err(err).Msg("SCMR execution failed")
- }
- },
- }
+ ExecutionMethodConfig: scmrexec.MethodModifyConfig{
+ NoStart: scmrNoStart,
+ ServiceName: scmrName,
+ },
+ }
+ if err := executor.Exec(log.WithContext(ctx), creds, target, execCfg); err != nil {
+ log.Fatal().Err(err).Msg("SCMR execution failed")
+ }
+ },
+ }
)
diff --git a/cmd/tsch.go b/cmd/tsch.go
index 8011cf2..78d71cc 100644
--- a/cmd/tsch.go
+++ b/cmd/tsch.go
@@ -1,10 +1,12 @@
package cmd
import (
+ "fmt"
"github.com/FalconOpsLLC/goexec/internal/client/dce"
"github.com/FalconOpsLLC/goexec/internal/exec"
"github.com/FalconOpsLLC/goexec/internal/exec/tsch"
"github.com/spf13/cobra"
+ "regexp"
"time"
)
@@ -29,7 +31,7 @@ func tschDeleteCmdInit() {
func tschDemandCmdInit() {
tschDemandCmd.Flags().StringVarP(&executable, "executable", "e", "", "Remote Windows executable to invoke")
tschDemandCmd.Flags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to executable")
- tschDemandCmd.Flags().StringVarP(&tschName, "name", "n", "", "Target task name")
+ tschDemandCmd.Flags().StringVarP(&tschTaskName, "name", "n", "", "Target task name")
tschDemandCmd.Flags().BoolVar(&tschNoDelete, "no-delete", false, "Don't delete task after execution")
if err := tschDemandCmd.MarkFlagRequired("executable"); err != nil {
panic(err)
@@ -39,7 +41,7 @@ func tschDemandCmdInit() {
func tschRegisterCmdInit() {
tschRegisterCmd.Flags().StringVarP(&executable, "executable", "e", "", "Remote Windows executable to invoke")
tschRegisterCmd.Flags().StringVarP(&executableArgs, "args", "a", "", "Arguments to pass to executable")
- tschRegisterCmd.Flags().StringVarP(&tschName, "name", "n", "", "Target task name")
+ tschRegisterCmd.Flags().StringVarP(&tschTaskName, "name", "n", "", "Target task name")
tschRegisterCmd.Flags().DurationVar(&tschStopDelay, "delay-stop", time.Duration(5*time.Second), "Delay between task execution and termination. This will not stop the process spawned by the task")
tschRegisterCmd.Flags().DurationVarP(&tschDelay, "delay-start", "d", time.Duration(5*time.Second), "Delay between task registration and execution")
tschRegisterCmd.Flags().DurationVarP(&tschDeleteDelay, "delay-delete", "D", time.Duration(0*time.Second), "Delay between task termination and deletion")
@@ -55,15 +57,35 @@ func tschRegisterCmdInit() {
}
}
+func tschArgs(principal string) func(cmd *cobra.Command, args []string) error {
+ return func(cmd *cobra.Command, args []string) error {
+ if tschTaskPath != "" && !tschTaskPathRegex.MatchString(tschTaskPath) {
+ return fmt.Errorf("invalid task path: %s", tschTaskPath)
+ }
+ if tschTaskName != "" {
+ if !tschTaskNameRegex.MatchString(tschTaskName) {
+ return fmt.Errorf("invalid task name: %s", tschTaskName)
+
+ } else if tschTaskPath == "" {
+ tschTaskPath = `\` + tschTaskName
+ }
+ }
+ return needsRpcTarget(principal)(cmd, args)
+ }
+}
+
var (
tschNoDelete bool
tschCallDelete bool
tschDeleteDelay time.Duration
tschStopDelay time.Duration
tschDelay time.Duration
- tschName string
+ tschTaskName string
tschTaskPath string
+ tschTaskPathRegex = regexp.MustCompile(`^\\[^ :/\\][^:/]*$`)
+ tschTaskNameRegex = regexp.MustCompile(`^[^ :/\\][^:/\\]*$`)
+
tschCmd = &cobra.Command{
Use: "tsch",
Short: "Establish execution via TSCH (ITaskSchedulerService)",
@@ -84,7 +106,7 @@ References:
SchRpcDelete - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/360bb9b1-dd2a-4b36-83ee-21f12cb97cff
DeleteExpiredTaskAfter - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/6bfde6fe-440e-4ddd-b4d6-c8fc0bc06fae
`,
- Args: needsRpcTarget("cifs"),
+ Args: tschArgs("cifs"),
Run: func(cmd *cobra.Command, args []string) {
log = log.With().
@@ -137,7 +159,7 @@ References:
SchRpcRegisterTask - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/849c131a-64e4-46ef-b015-9d4c599c5167
SchRpcRun - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/77f2250d-500a-40ee-be18-c82f7079c4f0
`,
- Args: needsTarget("cifs"),
+ Args: tschArgs("cifs"),
Run: func(cmd *cobra.Command, args []string) {
log = log.With().
@@ -163,7 +185,7 @@ References:
ExecutionMethodConfig: tschexec.MethodDemandConfig{
NoDelete: tschNoDelete,
- TaskName: tschName,
+ TaskPath: tschTaskPath,
},
}
if err := module.Connect(log.WithContext(ctx), creds, target, connCfg); err != nil {
@@ -182,7 +204,7 @@ References:
References:
SchRpcDelete - https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/360bb9b1-dd2a-4b36-83ee-21f12cb97cff
`,
- Args: needsTarget("cifs"),
+ Args: tschArgs("cifs"),
Run: func(cmd *cobra.Command, args []string) {
log = log.With().
Str("module", "tsch").
diff --git a/internal/exec/tsch/exec.go b/internal/exec/tsch/exec.go
index d205776..c238f41 100644
--- a/internal/exec/tsch/exec.go
+++ b/internal/exec/tsch/exec.go
@@ -25,23 +25,13 @@ const (
var (
TschRpcUuid = uuid.MustParse("86D35949-83C9-4044-B424-DB363231FD0C")
SupportedEndpointProtocols = []string{"ncacn_np", "ncacn_ip_tcp"}
-
- defaultStringBinding *dcerpc.StringBinding
- initErr error
)
-func init() {
- if defaultStringBinding, initErr = dcerpc.ParseStringBinding(DefaultEndpoint); initErr != nil {
- panic(initErr)
- }
-}
-
// Connect to the target & initialize DCE & TSCH clients
func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target *adauth.Target, ccfg *exec.ConnectionConfig) (err error) {
//var port uint16
var endpoint string = DefaultEndpoint
- //var stringBinding = defaultStringBinding
var epmOpts []dcerpc.Option
var dceOpts []dcerpc.Option
@@ -82,17 +72,7 @@ func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target
}
log = log.With().Str("endpoint", endpoint).Logger()
log.Info().Msg("Connecting to target")
- /*
- if !cfg.NoEpm {
- mapperOpts := append(dceOpts, ao...)
- dceOpts = append(dceOpts,
- epm.EndpointMapper(ctx, target.AddressWithoutPort(), mapperOpts...),
- dcerpc.WithEndpoint(fmt.Sprintf("%s:", stringBinding.ProtocolSequence.String())))
-
- } else {
- dceOpts = append(dceOpts, dcerpc.WithEndpoint(stringBinding.String()))
- }
- */
+
// Create DCERPC dialer
mod.dce, err = dcerpc.Dial(ctx, target.AddressWithoutPort(), append(dceOpts, ao...)...)
if err != nil {
diff --git a/internal/exec/tsch/tsch.go b/internal/exec/tsch/tsch.go
index f2476f1..d47e513 100644
--- a/internal/exec/tsch/tsch.go
+++ b/internal/exec/tsch/tsch.go
@@ -55,7 +55,7 @@ type settings struct {
type actionExec struct {
XMLName xml.Name `xml:"Exec"`
Command string `xml:"Command"`
- Arguments string `xml:"Arguments"`
+ Arguments string `xml:"Arguments,omitempty"`
}
type actions struct {
@@ -77,14 +77,13 @@ type principal struct {
}
type task struct {
- XMLName xml.Name `xml:"Task"`
- TaskVersion string `xml:"version,attr"`
- TaskNamespace string `xml:"xmlns,attr"`
- //TimeTriggers []taskTimeTrigger `xml:"Triggers>TimeTrigger,omitempty"` // TODO: triggers type
- Triggers triggers `xml:"Triggers"`
- Actions actions `xml:"Actions"`
- Principals principals `xml:"Principals"`
- Settings settings `xml:"Settings"`
+ XMLName xml.Name `xml:"Task"`
+ TaskVersion string `xml:"version,attr"`
+ TaskNamespace string `xml:"xmlns,attr"`
+ Triggers triggers `xml:"Triggers"`
+ Actions actions `xml:"Actions"`
+ Principals principals `xml:"Principals"`
+ Settings settings `xml:"Settings"`
}
// registerTask serializes and submits the provided task structure
@@ -106,7 +105,6 @@ func (mod *Module) registerTask(ctx context.Context, taskDef task, taskPath stri
taskXml = TaskXMLHeader + string(doc)
log.Debug().Str("content", taskXml).Msg("Generated task XML")
}
-
// Submit task
{
response, err := mod.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{