Update indent style

This commit is contained in:
Bryan McNulty
2025-07-12 15:24:02 -05:00
parent 3b8b29203e
commit 18c417feb4
22 changed files with 1685 additions and 1685 deletions
+98 -98
View File
@@ -1,33 +1,33 @@
package cmd
import (
"context"
"encoding/json"
"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
@@ -39,120 +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
}
}
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
})...)
}
+135 -135
View File
@@ -1,184 +1,184 @@
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()
dcomShellWindowsCmdInit()
dcomShellBrowserWindowCmdInit()
cmdFlags[dcomCmd] = []*flagSet{
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
dcomMmcCmdInit()
dcomShellWindowsCmdInit()
dcomShellBrowserWindowCmdInit()
dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd, dcomShellBrowserWindowCmd)
dcomCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
dcomCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
dcomCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
dcomCmd.AddCommand(dcomMmcCmd, dcomShellWindowsCmd, dcomShellBrowserWindowCmd)
}
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,
}
dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags)
cmdFlags[dcomMmcCmd] = []*flagSet{
dcomMmcExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
dcomMmcCmd.Flags().AddFlagSet(dcomMmcExecFlags.Flags)
// Constraints
dcomMmcCmd.MarkFlagsOneRequired("command", "exec")
// Constraints
dcomMmcCmd.MarkFlagsOneRequired("command", "exec")
}
func dcomShellWindowsCmdInit() {
dcomShellWindowsExecFlags := newFlagSet("Execution")
dcomShellWindowsExecFlags := newFlagSet("Execution")
registerExecutionFlags(dcomShellWindowsExecFlags.Flags)
registerExecutionOutputFlags(dcomShellWindowsExecFlags.Flags)
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`")
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)
cmdFlags[dcomShellWindowsCmd] = []*flagSet{
dcomShellWindowsExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
dcomShellWindowsCmd.Flags().AddFlagSet(dcomShellWindowsExecFlags.Flags)
// Constraints
dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec")
// Constraints
dcomShellWindowsCmd.MarkFlagsOneRequired("command", "exec")
}
func dcomShellBrowserWindowCmdInit() {
dcomShellBrowserWindowExecFlags := newFlagSet("Execution")
dcomShellBrowserWindowExecFlags := newFlagSet("Execution")
registerExecutionFlags(dcomShellBrowserWindowExecFlags.Flags)
registerExecutionOutputFlags(dcomShellBrowserWindowExecFlags.Flags)
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`")
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)
cmdFlags[dcomShellBrowserWindowCmd] = []*flagSet{
dcomShellBrowserWindowExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
dcomShellBrowserWindowCmd.Flags().AddFlagSet(dcomShellBrowserWindowExecFlags.Flags)
// Constraints
dcomShellBrowserWindowCmd.MarkFlagsOneRequired("command", "exec")
// Constraints
dcomShellBrowserWindowCmd.MarkFlagsOneRequired("command", "exec")
}
var (
dcomMmc dcomexec.DcomMmc
dcomShellWindows dcomexec.DcomShellWindows
dcomShellBrowserWindow dcomexec.DcomShellBrowserWindow
dcomMmc dcomexec.DcomMmc
dcomShellWindows dcomexec.DcomShellWindows
dcomShellBrowserWindow dcomexec.DcomShellBrowserWindow
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 MMC20.Application DCOM 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"),
argsAcceptValues("window", &dcomMmc.WindowState, "Minimized", "Maximized", "Restored"),
),
Run: func(cmd *cobra.Command, args []string) {
dcomMmc.Client = &rpcClient
dcomMmc.IO = exec
dcomMmc.ClassID = dcomexec.Mmc20Uuid
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", dcomexec.ModuleName).
Str("method", dcomexec.MethodMmc).
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:
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 provided 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
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()))
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")
}
},
}
if err := goexec.ExecuteCleanMethod(ctx, &dcomShellWindows, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
dcomShellBrowserWindowCmd = &cobra.Command{
Use: "shellbrowserwindow [target]",
Short: "Execute with the ShellBrowserWindow DCOM object",
Long: `Description:
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
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()))
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")
}
},
}
if err := goexec.ExecuteCleanMethod(ctx, &dcomShellBrowserWindow, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
)
+161 -161
View File
@@ -1,222 +1,222 @@
package cmd
import (
"context"
"github.com/FalconOpsLLC/goexec/internal/util"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/ssp/gssapi"
"github.com/spf13/cobra"
"context"
"github.com/FalconOpsLLC/goexec/internal/util"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/ssp/gssapi"
"github.com/spf13/cobra"
scmrexec "github.com/FalconOpsLLC/goexec/pkg/goexec/scmr"
scmrexec "github.com/FalconOpsLLC/goexec/pkg/goexec/scmr"
)
func scmrCmdInit() {
cmdFlags[scmrCmd] = []*flagSet{
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
scmrCreateCmdInit()
scmrChangeCmdInit()
scmrDeleteCmdInit()
cmdFlags[scmrCmd] = []*flagSet{
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
scmrCreateCmdInit()
scmrChangeCmdInit()
scmrDeleteCmdInit()
scmrCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
scmrCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
scmrCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
scmrCmd.AddCommand(scmrCreateCmd, scmrChangeCmd, scmrDeleteCmd)
scmrCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
scmrCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
scmrCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
scmrCmd.AddCommand(scmrCreateCmd, scmrChangeCmd, scmrDeleteCmd)
}
func scmrCreateCmdInit() {
scmrCreateFlags := newFlagSet("Service")
scmrCreateFlags := newFlagSet("Service")
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")
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")
scmrCreateExecFlags := newFlagSet("Execution")
scmrCreateExecFlags := newFlagSet("Execution")
// TODO: SCMR output
//registerExecutionOutputFlags(scmrCreateExecFlags.Flags)
// 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")
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)
scmrCreateCmd.Flags().AddFlagSet(scmrCreateFlags.Flags)
scmrCreateCmd.Flags().AddFlagSet(scmrCreateExecFlags.Flags)
cmdFlags[scmrCreateCmd] = []*flagSet{
scmrCreateExecFlags,
scmrCreateFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
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)
}
}
// Constraints
{
//scmrCreateCmd.MarkFlagsMutuallyExclusive("no-delete", "no-start")
if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil {
panic(err)
}
}
}
func scmrChangeCmdInit() {
scmrChangeFlags := newFlagSet("Service Control")
scmrChangeFlags := newFlagSet("Service Control")
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")
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")
scmrChangeExecFlags := newFlagSet("Execution")
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")
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)
//registerStageFlags(scmrChangeExecFlags.Flags)
// TODO: SCMR output
//registerExecutionOutputFlags(scmrChangeExecFlags.Flags)
//registerStageFlags(scmrChangeExecFlags.Flags)
cmdFlags[scmrChangeCmd] = []*flagSet{
scmrChangeFlags,
scmrChangeExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
cmdFlags[scmrChangeCmd] = []*flagSet{
scmrChangeFlags,
scmrChangeExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
scmrChangeCmd.Flags().AddFlagSet(scmrChangeFlags.Flags)
scmrChangeCmd.Flags().AddFlagSet(scmrChangeExecFlags.Flags)
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)
}
}
// Constraints
{
if err := scmrChangeCmd.MarkFlagRequired("service-name"); err != nil {
panic(err)
}
if err := scmrCreateCmd.MarkFlagRequired("executable-path"); err != nil {
panic(err)
}
}
}
func scmrDeleteCmdInit() {
scmrDeleteFlags := newFlagSet("Service Control")
scmrDeleteFlags.Flags.StringVarP(&scmrDelete.ServiceName, "service-name", "s", scmrDelete.ServiceName, "Name of service to delete")
scmrDeleteFlags := newFlagSet("Service Control")
scmrDeleteFlags.Flags.StringVarP(&scmrDelete.ServiceName, "service-name", "s", scmrDelete.ServiceName, "Name of service to delete")
cmdFlags[scmrDeleteCmd] = []*flagSet{
scmrDeleteFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
cmdFlags[scmrDeleteCmd] = []*flagSet{
scmrDeleteFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
scmrDeleteCmd.Flags().AddFlagSet(scmrDeleteFlags.Flags)
scmrDeleteCmd.Flags().AddFlagSet(scmrDeleteFlags.Flags)
if err := scmrDeleteCmd.MarkFlagRequired("service-name"); err != nil {
panic(err)
}
if err := scmrDeleteCmd.MarkFlagRequired("service-name"); err != nil {
panic(err)
}
}
var (
scmrCreate = scmrexec.ScmrCreate{}
scmrChange = scmrexec.ScmrChange{}
scmrDelete = scmrexec.ScmrDelete{}
scmrCreate = scmrexec.ScmrCreate{}
scmrChange = scmrexec.ScmrChange{}
scmrDelete = scmrexec.ScmrDelete{}
scmrCmd = &cobra.Command{
Use: "scmr",
Short: "Execute with Service Control Manager Remote (MS-SCMR)",
Long: `Description:
scmrCmd = &cobra.Command{
Use: "scmr",
Short: "Execute with Service Control Manager Remote (MS-SCMR)",
Long: `Description:
The SCMR module works a lot like Impacket's smbexec.py, but it provides additional RPC transports
to evade network monitoring or firewall rules, and some minor OPSEC improvements overall.`,
GroupID: "module",
Args: cobra.NoArgs,
}
GroupID: "module",
Args: cobra.NoArgs,
}
scmrCreateCmd = &cobra.Command{
Use: "create [target]",
Short: "Spawn a remote process by creating & running a Windows service",
Long: `Description:
scmrCreateCmd = &cobra.Command{
Use: "create [target]",
Short: "Spawn a remote process by creating & running a Windows service",
Long: `Description:
The create method calls RCreateServiceW to create a new Windows service on the
remote target with the provided executable & arguments as the lpBinaryPathName`,
Args: args(
argsRpcClient("cifs"),
argsSmbClient(),
),
Args: args(
argsRpcClient("cifs"),
argsSmbClient(),
),
Run: func(cmd *cobra.Command, args []string) {
scmrCreate.Client = &rpcClient
scmrCreate.IO = exec
Run: func(cmd *cobra.Command, args []string) {
scmrCreate.Client = &rpcClient
scmrCreate.IO = exec
log = log.With().
Str("module", "scmr").
Str("method", "create").
Logger()
log = log.With().
Str("module", "scmr").
Str("method", "create").
Logger()
// Warnings
{
if scmrCreate.ServiceName == "" {
log.Warn().Msg("No service name was provided. Using a random string")
scmrCreate.ServiceName = util.RandomString()
}
if scmrCreate.NoDelete {
log.Warn().Msg("Service will not be deleted after execution")
}
if scmrCreate.DisplayName == "" {
log.Debug().Msg("No display name specified, using service name as display name")
scmrCreate.DisplayName = scmrCreate.ServiceName
}
}
// Warnings
{
if scmrCreate.ServiceName == "" {
log.Warn().Msg("No service name was provided. Using a random string")
scmrCreate.ServiceName = util.RandomString()
}
if scmrCreate.NoDelete {
log.Warn().Msg("Service will not be deleted after execution")
}
if scmrCreate.DisplayName == "" {
log.Debug().Msg("No display name specified, using service name as display name")
scmrCreate.DisplayName = scmrCreate.ServiceName
}
}
ctx := log.WithContext(gssapi.NewSecurityContext(context.Background()))
ctx := log.WithContext(gssapi.NewSecurityContext(context.Background()))
if err := goexec.ExecuteCleanMethod(ctx, &scmrCreate, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
if err := goexec.ExecuteCleanMethod(ctx, &scmrCreate, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
scmrChangeCmd = &cobra.Command{
Use: "change [target]",
Short: "Change an existing Windows service to spawn an arbitrary process",
Long: `Description:
scmrChangeCmd = &cobra.Command{
Use: "change [target]",
Short: "Change an existing Windows service to spawn an arbitrary process",
Long: `Description:
The change method executes programs by modifying existing Windows services
using the RChangeServiceConfigW method rather than calling RCreateServiceW
like scmr create. The modified service is restored to its original state
after execution`,
Args: argsRpcClient("cifs"),
Args: argsRpcClient("cifs"),
Run: func(cmd *cobra.Command, args []string) {
scmrChange.Client = &rpcClient
scmrChange.IO = exec
Run: func(cmd *cobra.Command, args []string) {
scmrChange.Client = &rpcClient
scmrChange.IO = exec
ctx := log.With().
Str("module", "scmr").
Str("method", "change").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
ctx := log.With().
Str("module", "scmr").
Str("method", "change").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
if err := goexec.ExecuteCleanMethod(ctx, &scmrChange, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
scmrDeleteCmd = &cobra.Command{
Use: "delete [target]",
Short: "Delete an existing Windows service",
Long: `Description:
if err := goexec.ExecuteCleanMethod(ctx, &scmrChange, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
scmrDeleteCmd = &cobra.Command{
Use: "delete [target]",
Short: "Delete an existing Windows service",
Long: `Description:
The delete method will simply delete the provided service.`,
Args: argsRpcClient("cifs"),
Run: func(cmd *cobra.Command, args []string) {
scmrDelete.Client = &rpcClient
Args: argsRpcClient("cifs"),
Run: func(cmd *cobra.Command, args []string) {
scmrDelete.Client = &rpcClient
ctx := log.With().
Str("module", "scmr").
Str("method", "delete").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
ctx := log.With().
Str("module", "scmr").
Str("method", "delete").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &scmrDelete); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &scmrDelete); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
)
+99 -99
View File
@@ -1,140 +1,140 @@
package cmd
import (
"context"
"encoding/json"
"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"
"context"
"encoding/json"
"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() {
cmdFlags[wmiCmd] = []*flagSet{
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
wmiCallCmdInit()
wmiProcCmdInit()
cmdFlags[wmiCmd] = []*flagSet{
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
wmiCallCmdInit()
wmiProcCmdInit()
wmiCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
wmiCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
wmiCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
wmiCmd.AddCommand(wmiProcCmd, wmiCallCmd)
wmiCmd.PersistentFlags().AddFlagSet(defaultAuthFlags.Flags)
wmiCmd.PersistentFlags().AddFlagSet(defaultLogFlags.Flags)
wmiCmd.PersistentFlags().AddFlagSet(defaultNetRpcFlags.Flags)
wmiCmd.AddCommand(wmiProcCmd, wmiCallCmd)
}
func wmiCallCmdInit() {
wmiCallFlags := newFlagSet("WMI")
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"})`)
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)
wmiCallCmd.Flags().AddFlagSet(wmiCallFlags.Flags)
cmdFlags[wmiCallCmd] = []*flagSet{
wmiCallFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
if err := wmiCallCmd.MarkFlagRequired("class"); err != nil {
panic(err)
}
if err := wmiCallCmd.MarkFlagRequired("method"); err != nil {
panic(err)
}
cmdFlags[wmiCallCmd] = []*flagSet{
wmiCallFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
if err := wmiCallCmd.MarkFlagRequired("class"); err != nil {
panic(err)
}
if err := wmiCallCmd.MarkFlagRequired("method"); err != nil {
panic(err)
}
}
func wmiProcCmdInit() {
wmiProcExecFlags := newFlagSet("Execution")
wmiProcExecFlags := newFlagSet("Execution")
registerExecutionFlags(wmiProcExecFlags.Flags)
registerExecutionOutputFlags(wmiProcExecFlags.Flags)
registerExecutionFlags(wmiProcExecFlags.Flags)
registerExecutionOutputFlags(wmiProcExecFlags.Flags)
wmiProcExecFlags.Flags.StringVarP(&wmiProc.WorkingDirectory, "directory", "d", `C:\`, "Working directory")
wmiProcExecFlags.Flags.StringVarP(&wmiProc.WorkingDirectory, "directory", "d", `C:\`, "Working directory")
cmdFlags[wmiProcCmd] = []*flagSet{
wmiProcExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
cmdFlags[wmiProcCmd] = []*flagSet{
wmiProcExecFlags,
defaultAuthFlags,
defaultLogFlags,
defaultNetRpcFlags,
}
wmiProcCmd.Flags().AddFlagSet(wmiProcExecFlags.Flags)
wmiProcCmd.Flags().AddFlagSet(wmiProcExecFlags.Flags)
}
var (
wmiCall = wmiexec.WmiCall{}
wmiProc = wmiexec.WmiProc{}
wmiCall = wmiexec.WmiCall{}
wmiProc = wmiexec.WmiProc{}
wmiArguments string
wmiArguments string
wmiCmd = &cobra.Command{
Use: "wmi",
Short: "Execute with Windows Management Instrumentation (MS-WMI)",
Long: `Description:
wmiCmd = &cobra.Command{
Use: "wmi",
Short: "Execute with Windows Management Instrumentation (MS-WMI)",
Long: `Description:
The wmi module uses remote Windows Management Instrumentation (WMI) to
perform various operations including process creation.`,
GroupID: "module",
Args: cobra.NoArgs,
}
GroupID: "module",
Args: cobra.NoArgs,
}
wmiCallCmd = &cobra.Command{
Use: "call [target]",
Short: "Execute specified WMI method",
Long: `Description:
wmiCallCmd = &cobra.Command{
Use: "call [target]",
Short: "Execute specified WMI method",
Long: `Description:
The call method creates an instance of the specified WMI class (-c),
then calls the provided method (-m) with the provided arguments (-A).`,
Args: args(
argsRpcClient("cifs"),
func(cmd *cobra.Command, args []string) error {
return json.Unmarshal([]byte(wmiArguments), &wmiCall.Args)
}),
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) {
wmiCall.Client = &rpcClient
wmiCall.Out = os.Stdout
Run: func(cmd *cobra.Command, args []string) {
wmiCall.Client = &rpcClient
wmiCall.Out = os.Stdout
ctx := log.With().
Str("module", "wmi").
Str("method", "call").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
ctx := log.With().
Str("module", "wmi").
Str("method", "call").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &wmiCall); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
if err := goexec.ExecuteCleanAuxiliaryMethod(ctx, &wmiCall); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
wmiProcCmd = &cobra.Command{
Use: "proc [target]",
Short: "Start a Windows process",
Long: `Description:
wmiProcCmd = &cobra.Command{
Use: "proc [target]",
Short: "Start a Windows process",
Long: `Description:
The proc method creates an instance of the Win32_Process WMI class, then
calls the Win32_Process.Create method with the provided command (-c),
and optional working directory (-d).`,
Args: args(
argsRpcClient("cifs"),
argsOutput("smb"),
),
Args: args(
argsRpcClient("cifs"),
argsOutput("smb"),
),
Run: func(cmd *cobra.Command, args []string) {
wmiProc.Client = &rpcClient
wmiProc.IO = exec
wmiProc.Resource = "//./root/cimv2"
Run: func(cmd *cobra.Command, args []string) {
wmiProc.Client = &rpcClient
wmiProc.IO = exec
wmiProc.Resource = "//./root/cimv2"
ctx := log.With().
Str("module", "wmi").
Str("method", "proc").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
ctx := log.With().
Str("module", "wmi").
Str("method", "proc").
Logger().WithContext(gssapi.NewSecurityContext(context.Background()))
if err := goexec.ExecuteCleanMethod(ctx, &wmiProc, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
if err := goexec.ExecuteCleanMethod(ctx, &wmiProc, &exec); err != nil {
log.Fatal().Err(err).Msg("Operation failed")
}
},
}
)
+19 -19
View File
@@ -1,41 +1,41 @@
package util
import (
"github.com/google/uuid"
"math/rand" // not crypto secure
"regexp"
"strings"
"github.com/google/uuid"
"math/rand" // not crypto secure
"regexp"
"strings"
)
const randHostnameCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"
const randStringCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var (
// Up to 15 characters; only letters, digits, and hyphens (with hyphens not at the start or end).
randHostnameRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{0,14}[a-zA-Z0-9]$`)
// Up to 15 characters; only letters, digits, and hyphens (with hyphens not at the start or end).
randHostnameRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{0,14}[a-zA-Z0-9]$`)
)
func RandomHostname() (hostname string) {
for {
// between 2 and 10 characters
if hostname = RandomStringFromCharset(randHostnameCharset, rand.Intn(8)+2); randHostnameRegex.MatchString(hostname) {
return
}
}
for {
// between 2 and 10 characters
if hostname = RandomStringFromCharset(randHostnameCharset, rand.Intn(8)+2); randHostnameRegex.MatchString(hostname) {
return
}
}
}
func RandomWindowsTempFile() string {
return `\Windows\Temp\` + strings.ToUpper(uuid.New().String())
return `\Windows\Temp\` + strings.ToUpper(uuid.New().String())
}
func RandomString() string {
return RandomStringFromCharset(randStringCharset, rand.Intn(10)+6)
return RandomStringFromCharset(randStringCharset, rand.Intn(10)+6)
}
func RandomStringFromCharset(charset string, length int) string {
b := make([]byte, length)
for i := range length {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
b := make([]byte, length)
for i := range length {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
+1 -1
View File
@@ -3,5 +3,5 @@ package main
import "github.com/FalconOpsLLC/goexec/cmd"
func main() {
cmd.Execute()
cmd.Execute()
}
+23 -23
View File
@@ -1,34 +1,34 @@
package dcomexec
import (
googleUUID "github.com/google/uuid"
"github.com/oiweiwei/go-msrpc/midl/uuid"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dtyp"
googleUUID "github.com/google/uuid"
"github.com/oiweiwei/go-msrpc/midl/uuid"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dtyp"
)
const (
LcEnglishUs uint32 = 0x409
LcEnglishUs uint32 = 0x409
)
var (
ShellBrowserWindowUuid = uuid.MustParse("C08AFD90-F2A1-11D1-8455-00A0C91F3880")
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{
Data1: 0x20400,
Data2: 0x0,
Data3: 0x0,
Data4: []byte{0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46},
}
ComVersion = &dcom.COMVersion{
MajorVersion: 5,
MinorVersion: 7,
}
ORPCThis = &dcom.ORPCThis{
Version: ComVersion,
CID: &RandCid,
}
RandCid = dcom.CID(*dtyp.GUIDFromUUID(uuid.MustParse(googleUUID.NewString())))
IDispatchIID = &dcom.IID{
Data1: 0x20400,
Data2: 0x0,
Data3: 0x0,
Data4: []byte{0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46},
}
ComVersion = &dcom.COMVersion{
MajorVersion: 5,
MinorVersion: 7,
}
ORPCThis = &dcom.ORPCThis{
Version: ComVersion,
CID: &RandCid,
}
)
+31 -31
View File
@@ -1,52 +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,
nil,
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
}
+108 -108
View File
@@ -1,147 +1,147 @@
package dcomexec
import (
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/midl/uuid"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/iremotescmactivator/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dtyp"
"github.com/rs/zerolog"
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/midl/uuid"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/iremotescmactivator/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dtyp"
"github.com/rs/zerolog"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
const (
ModuleName = "DCOM"
ModuleName = "DCOM"
)
type Dcom struct {
goexec.Cleaner
goexec.Executor
goexec.Cleaner
goexec.Executor
Client *dce.Client
ClassID *uuid.UUID
Client *dce.Client
ClassID *uuid.UUID
dispatchClient idispatch.DispatchClient
dispatchClient idispatch.DispatchClient
}
func (m *Dcom) Connect(ctx context.Context) (err error) {
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
}
func (m *Dcom) Init(ctx context.Context) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).Logger()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).Logger()
if m.Client == nil || m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
if m.Client == nil || m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
if m.ClassID == nil {
return errors.New("CLSID not specified")
}
if m.ClassID == nil {
return errors.New("CLSID not specified")
}
class := dcom.ClassID(*dtyp.GUIDFromUUID(m.ClassID))
class := dcom.ClassID(*dtyp.GUIDFromUUID(m.ClassID))
if class.GUID() == nil {
return fmt.Errorf("invalid class ID: %s", m.ClassID)
}
if class.GUID() == nil {
return fmt.Errorf("invalid class ID: %s", m.ClassID)
}
opts := []dcerpc.Option{
dcerpc.WithSign(),
}
opts := []dcerpc.Option{
dcerpc.WithSign(),
}
inst := &dcom.InstantiationInfoData{
ClassID: &class,
IID: []*dcom.IID{IDispatchIID},
ClientCOMVersion: ComVersion,
}
ac := &dcom.ActivationContextInfoData{}
loc := &dcom.LocationInfoData{}
scm := &dcom.SCMRequestInfoData{
RemoteRequest: &dcom.CustomRemoteRequestSCMInfo{
RequestedProtocolSequences: []uint16{7},
},
}
inst := &dcom.InstantiationInfoData{
ClassID: &class,
IID: []*dcom.IID{IDispatchIID},
ClientCOMVersion: ComVersion,
}
ac := &dcom.ActivationContextInfoData{}
loc := &dcom.LocationInfoData{}
scm := &dcom.SCMRequestInfoData{
RemoteRequest: &dcom.CustomRemoteRequestSCMInfo{
RequestedProtocolSequences: []uint16{7},
},
}
ap := &dcom.ActivationProperties{
DestinationContext: 2,
Properties: []dcom.ActivationProperty{inst, ac, loc, scm},
}
ap := &dcom.ActivationProperties{
DestinationContext: 2,
Properties: []dcom.ActivationProperty{inst, ac, loc, scm},
}
apin, err := ap.ActivationPropertiesIn()
if err != nil {
return err
}
apin, err := ap.ActivationPropertiesIn()
if err != nil {
return err
}
act, err := iremotescmactivator.NewRemoteSCMActivatorClient(ctx, m.Client.Dce())
if err != nil {
return err
}
act, err := iremotescmactivator.NewRemoteSCMActivatorClient(ctx, m.Client.Dce())
if err != nil {
return err
}
cr, err := act.RemoteCreateInstance(ctx, &iremotescmactivator.RemoteCreateInstanceRequest{
ORPCThis: &dcom.ORPCThis{
Version: ComVersion,
Flags: 1,
CID: &RandCid,
},
ActPropertiesIn: apin,
})
if err != nil {
return err
}
log.Info().Msg("RemoteCreateInstance succeeded")
cr, err := act.RemoteCreateInstance(ctx, &iremotescmactivator.RemoteCreateInstanceRequest{
ORPCThis: &dcom.ORPCThis{
Version: ComVersion,
Flags: 1,
CID: &RandCid,
},
ActPropertiesIn: apin,
})
if err != nil {
return err
}
log.Info().Msg("RemoteCreateInstance succeeded")
apout := new(dcom.ActivationProperties)
if err = apout.Parse(cr.ActPropertiesOut); err != nil {
return err
}
si := apout.SCMReplyInfoData()
pi := apout.PropertiesOutInfo()
apout := new(dcom.ActivationProperties)
if err = apout.Parse(cr.ActPropertiesOut); err != nil {
return err
}
si := apout.SCMReplyInfoData()
pi := apout.PropertiesOutInfo()
if si == nil {
return fmt.Errorf("remote create instance response: SCMReplyInfoData is nil")
}
if si == nil {
return fmt.Errorf("remote create instance response: SCMReplyInfoData is nil")
}
if pi == nil {
return fmt.Errorf("remote create instance response: PropertiesOutInfo is nil")
}
if pi == nil {
return fmt.Errorf("remote create instance response: PropertiesOutInfo is nil")
}
// Ensure that the string bindings don't contain the target hostname
for _, bind := range si.RemoteReply.OXIDBindings.GetStringBindings() {
stringBinding, err := dcerpc.ParseStringBinding("ncacn_ip_tcp:" + bind.NetworkAddr) // TODO: try bind.String()
// Ensure that the string bindings don't contain the target hostname
for _, bind := range si.RemoteReply.OXIDBindings.GetStringBindings() {
stringBinding, err := dcerpc.ParseStringBinding("ncacn_ip_tcp:" + bind.NetworkAddr) // TODO: try bind.String()
if err != nil {
log.Debug().Err(err).Msg("Failed to parse string binding")
continue
}
stringBinding.NetworkAddress = ""
opts = append(opts, dcerpc.WithEndpoint(stringBinding.String()))
}
if err != nil {
log.Debug().Err(err).Msg("Failed to parse string binding")
continue
}
stringBinding.NetworkAddress = ""
opts = append(opts, dcerpc.WithEndpoint(stringBinding.String()))
}
err = m.Client.Reconnect(ctx, opts...)
if err != nil {
return err
}
log.Info().Msg("created new DCERPC dialer")
err = m.Client.Reconnect(ctx, opts...)
if err != nil {
return err
}
log.Info().Msg("created new DCERPC dialer")
m.dispatchClient, err = idispatch.NewDispatchClient(ctx, m.Client.Dce(), dcom.WithIPID(pi.InterfaceData[0].IPID()))
if err != nil {
return err
}
log.Info().Msg("created IDispatch Client")
m.dispatchClient, err = idispatch.NewDispatchClient(ctx, m.Client.Dce(), dcom.WithIPID(pi.InterfaceData[0].IPID()))
if err != nil {
return err
}
log.Info().Msg("created IDispatch Client")
return
return
}
+31 -31
View File
@@ -1,52 +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 (
MethodShellBrowserWindow = "ShellBrowserWindow" // ShellBrowserWindow::Document.Application.ShellExecute
MethodShellBrowserWindow = "ShellBrowserWindow" // ShellBrowserWindow::Document.Application.ShellExecute
)
type DcomShellBrowserWindow struct {
Dcom
Dcom
IO goexec.ExecutionIO
IO goexec.ExecutionIO
WorkingDirectory string
WindowState string
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()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodShellBrowserWindow).
Logger()
method := "Document.Application.ShellExecute"
method := "Document.Application.ShellExecute"
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,
nil,
method,
stringToVariant(m.WindowState),
stringToVariant(""), // FUTURE?
stringToVariant(m.WorkingDirectory),
stringToVariant(args),
stringToVariant(proc)); err != nil {
// 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
log.Error().Err(err).Msg("Failed to call method")
return fmt.Errorf("call %q: %w", method, err)
}
log.Info().Msg("Method call successful")
return
}
+48 -48
View File
@@ -1,73 +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"
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut"
"github.com/rs/zerolog"
)
const (
MethodShellWindows = "ShellWindows" // ShellWindows::Item().Document.Application.ShellExecute
MethodShellWindows = "ShellWindows" // ShellWindows::Item().Document.Application.ShellExecute
)
type DcomShellWindows struct {
Dcom
Dcom
IO goexec.ExecutionIO
IO goexec.ExecutionIO
WorkingDirectory string
WindowState string
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", MethodShellWindows).
Logger()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodShellWindows).
Logger()
method := "Item"
method := "Item"
cmdline := execIO.CommandLine()
proc := cmdline[0]
args := cmdline[1]
cmdline := execIO.CommandLine()
proc := cmdline[0]
args := cmdline[1]
iv, err := callComMethod(ctx,
m.dispatchClient,
nil,
"Item")
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)
}
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()")
}
item, ok := iv.VarResult.VarUnion.GetValue().(*oaut.Dispatch)
if !ok {
return errors.New("failed to get dispatch from ShellWindows::Item()")
}
method = "Document.Application.ShellExecute"
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 {
// 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
log.Error().Err(err).Msg("Failed to call method")
return fmt.Errorf("call %q: %w", method, err)
}
log.Info().Msg("Method call successful")
return
}
+61 -61
View File
@@ -1,85 +1,85 @@
package dcomexec
import (
"context"
"fmt"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0"
"strings"
"context"
"fmt"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0"
"strings"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
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, ".")
parts := strings.Split(method, ".")
for i, obj := range parts {
for i, obj := range parts {
var opts []dcerpc.CallOption
var opts []dcerpc.CallOption
if id != nil {
opts = append(opts, dcom.WithIPID(id))
}
if id != nil {
opts = append(opts, dcom.WithIPID(id))
}
gr, err := dc.GetIDsOfNames(ctx, &idispatch.GetIDsOfNamesRequest{
This: ORPCThis,
IID: &dcom.IID{},
LocaleID: LcEnglishUs,
gr, err := dc.GetIDsOfNames(ctx, &idispatch.GetIDsOfNamesRequest{
This: ORPCThis,
IID: &dcom.IID{},
LocaleID: LcEnglishUs,
Names: []string{obj + "\x00"},
}, opts...)
Names: []string{obj + "\x00"},
}, opts...)
if err != nil {
return nil, fmt.Errorf("get dispatch ID of name %q: %w", obj, err)
}
if err != nil {
return nil, fmt.Errorf("get dispatch ID of name %q: %w", obj, err)
}
if len(gr.DispatchID) < 1 {
return nil, fmt.Errorf("dispatch ID of name %q not found", obj)
}
if len(gr.DispatchID) < 1 {
return nil, fmt.Errorf("dispatch ID of name %q not found", obj)
}
irq := &idispatch.InvokeRequest{
This: ORPCThis,
IID: &dcom.IID{},
LocaleID: LcEnglishUs,
irq := &idispatch.InvokeRequest{
This: ORPCThis,
IID: &dcom.IID{},
LocaleID: LcEnglishUs,
DispatchIDMember: gr.DispatchID[0],
}
DispatchIDMember: gr.DispatchID[0],
}
if i >= len(parts)-1 {
irq.Flags = 1
irq.DispatchParams = &oaut.DispatchParams{ArgsCount: uint32(len(args)), Args: args}
return dc.Invoke(ctx, irq, opts...)
}
irq.Flags = 2
if i >= len(parts)-1 {
irq.Flags = 1
irq.DispatchParams = &oaut.DispatchParams{ArgsCount: uint32(len(args)), Args: args}
return dc.Invoke(ctx, irq, opts...)
}
irq.Flags = 2
ir, err = dc.Invoke(ctx, irq, opts...)
if err != nil {
return nil, fmt.Errorf("get properties of object %q: %w", obj, err)
}
ir, err = dc.Invoke(ctx, irq, opts...)
if err != nil {
return nil, fmt.Errorf("get properties of object %q: %w", obj, err)
}
di, ok := ir.VarResult.VarUnion.GetValue().(*oaut.Dispatch)
if !ok {
return nil, fmt.Errorf("invalid dispatch object for %q", obj)
}
id = di.InterfacePointer().GetStandardObjectReference().Std.IPID
}
return
di, ok := ir.VarResult.VarUnion.GetValue().(*oaut.Dispatch)
if !ok {
return nil, fmt.Errorf("invalid dispatch object for %q", obj)
}
id = di.InterfacePointer().GetStandardObjectReference().Std.IPID
}
return
}
func stringToVariant(s string) *oaut.Variant {
return &oaut.Variant{
Size: 5,
VT: 8,
VarUnion: &oaut.Variant_VarUnion{
Value: &oaut.Variant_VarUnion_BSTR{
BSTR: &oaut.String{
Data: s,
},
},
},
}
return &oaut.Variant{
Size: 5,
VT: 8,
VarUnion: &oaut.Variant_VarUnion{
Value: &oaut.Variant_VarUnion_BSTR{
BSTR: &oaut.String{
Data: s,
},
},
},
}
}
+57 -57
View File
@@ -1,99 +1,99 @@
package goexec
import (
"context"
"fmt"
"io"
"strings"
"context"
"fmt"
"io"
"strings"
)
type OutputProvider interface {
GetOutput(ctx context.Context, writer io.Writer) (err error)
Clean(ctx context.Context) (err error)
GetOutput(ctx context.Context, writer io.Writer) (err error)
Clean(ctx context.Context) (err error)
}
type ExecutionIO struct {
Cleaner
Cleaner
Input *ExecutionInput
Output *ExecutionOutput
Input *ExecutionInput
Output *ExecutionOutput
}
type ExecutionOutput struct {
NoDelete bool
RemotePath string
Provider OutputProvider
Writer io.WriteCloser
NoDelete bool
RemotePath string
Provider OutputProvider
Writer io.WriteCloser
}
type ExecutionInput struct {
StageFile io.ReadCloser
Executable string
ExecutablePath string
Arguments string
Command string
StageFile io.ReadCloser
Executable string
ExecutablePath string
Arguments string
Command string
}
func (execIO *ExecutionIO) GetOutput(ctx context.Context) (err error) {
if execIO.Output.Provider != nil {
return execIO.Output.Provider.GetOutput(ctx, execIO.Output.Writer)
}
return nil
if execIO.Output.Provider != nil {
return execIO.Output.Provider.GetOutput(ctx, execIO.Output.Writer)
}
return nil
}
func (execIO *ExecutionIO) Clean(ctx context.Context) (err error) {
if execIO.Output.Provider != nil {
return execIO.Output.Provider.Clean(ctx)
}
return nil
if execIO.Output.Provider != nil {
return execIO.Output.Provider.Clean(ctx)
}
return nil
}
func (execIO *ExecutionIO) CommandLine() (cmd []string) {
if execIO.Output.Provider != nil && execIO.Output.RemotePath != "" {
return []string{
`C:\Windows\System32\cmd.exe`,
fmt.Sprintf(`/C %s > %s 2>&1`, execIO.Input.String(), execIO.Output.RemotePath),
}
}
return execIO.Input.CommandLine()
if execIO.Output.Provider != nil && execIO.Output.RemotePath != "" {
return []string{
`C:\Windows\System32\cmd.exe`,
fmt.Sprintf(`/C %s > %s 2>&1`, execIO.Input.String(), execIO.Output.RemotePath),
}
}
return execIO.Input.CommandLine()
}
func (execIO *ExecutionIO) String() (str string) {
cmd := execIO.CommandLine()
// Ensure that executable paths are quoted
if strings.Contains(cmd[0], " ") {
str = fmt.Sprintf(`%q %s`, cmd[0], strings.Join(cmd[1:], " "))
} else {
str = strings.Join(cmd, " ")
}
return strings.Trim(str, " \t\n\r") // trim whitespace
cmd := execIO.CommandLine()
// Ensure that executable paths are quoted
if strings.Contains(cmd[0], " ") {
str = fmt.Sprintf(`%q %s`, cmd[0], strings.Join(cmd[1:], " "))
} else {
str = strings.Join(cmd, " ")
}
return strings.Trim(str, " \t\n\r") // trim whitespace
}
func (i *ExecutionInput) CommandLine() (cmd []string) {
cmd = make([]string, 2)
cmd[1] = i.Arguments
cmd = make([]string, 2)
cmd[1] = i.Arguments
switch {
case i.Command != "":
return strings.SplitN(i.Command, " ", 2)
switch {
case i.Command != "":
return strings.SplitN(i.Command, " ", 2)
case i.ExecutablePath != "":
cmd[0] = i.ExecutablePath
case i.ExecutablePath != "":
cmd[0] = i.ExecutablePath
case i.Executable != "":
cmd[0] = i.Executable
}
case i.Executable != "":
cmd[0] = i.Executable
}
return cmd
return cmd
}
func (i *ExecutionInput) String() string {
return strings.Join(i.CommandLine(), " ")
return strings.Join(i.CommandLine(), " ")
}
func (i *ExecutionInput) Reader() (reader io.Reader) {
if i.StageFile != nil {
return i.StageFile
}
return strings.NewReader(i.String())
if i.StageFile != nil {
return i.StageFile
}
return strings.NewReader(i.String())
}
+113 -113
View File
@@ -1,154 +1,154 @@
package scmrexec
import (
"context"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"github.com/rs/zerolog"
"context"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"github.com/rs/zerolog"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
const (
MethodChange = "Change"
MethodChange = "Change"
)
type ScmrChange struct {
Scmr
goexec.Cleaner
goexec.Executor
Scmr
goexec.Cleaner
goexec.Executor
IO goexec.ExecutionIO
IO goexec.ExecutionIO
NoStart bool
NoRevert bool
ServiceName string
NoStart bool
NoRevert bool
ServiceName string
}
func (m *ScmrChange) Execute(ctx context.Context, in *goexec.ExecutionIO) (err error) {
log := zerolog.Ctx(ctx).With().
Str("service", m.ServiceName).
Logger()
log := zerolog.Ctx(ctx).With().
Str("service", m.ServiceName).
Logger()
svc := &service{name: m.ServiceName}
svc := &service{name: m.ServiceName}
openResponse, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{
ServiceManager: m.scm,
ServiceName: svc.name,
DesiredAccess: ServiceAllAccess,
})
openResponse, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{
ServiceManager: m.scm,
ServiceName: svc.name,
DesiredAccess: ServiceAllAccess,
})
if err != nil {
log.Error().Err(err).Msg("Failed to open service handle")
return fmt.Errorf("open service request: %w", err)
}
if openResponse.Return != 0 {
log.Error().Err(err).Msg("Failed to open service handle")
return fmt.Errorf("create service: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to open service handle")
return fmt.Errorf("open service request: %w", err)
}
if openResponse.Return != 0 {
log.Error().Err(err).Msg("Failed to open service handle")
return fmt.Errorf("create service: %w", err)
}
svc.handle = openResponse.Service
log.Info().Msg("Opened service handle")
svc.handle = openResponse.Service
log.Info().Msg("Opened service handle")
defer m.AddCleaners(func(ctxInner context.Context) error {
return m.closeService(ctxInner, svc)
})
defer m.AddCleaners(func(ctxInner context.Context) error {
return m.closeService(ctxInner, svc)
})
// Note the original service configuration
queryResponse, err := m.ctl.QueryServiceConfigW(ctx, &svcctl.QueryServiceConfigWRequest{
Service: svc.handle,
BufferLength: 8 * 1024,
})
// Note the original service configuration
queryResponse, err := m.ctl.QueryServiceConfigW(ctx, &svcctl.QueryServiceConfigWRequest{
Service: svc.handle,
BufferLength: 8 * 1024,
})
if err != nil {
log.Error().Err(err).Msg("Failed to fetch service configuration")
return fmt.Errorf("get service config: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to fetch service configuration")
return fmt.Errorf("get service config: %w", err)
}
log.Info().Str("binaryPath", queryResponse.ServiceConfig.BinaryPathName).Msg("Fetched original service configuration")
svc.originalConfig = queryResponse.ServiceConfig
log.Info().Str("binaryPath", queryResponse.ServiceConfig.BinaryPathName).Msg("Fetched original service configuration")
svc.originalConfig = queryResponse.ServiceConfig
stopResponse, err := m.ctl.ControlService(ctx, &svcctl.ControlServiceRequest{
Service: svc.handle,
Control: ServiceControlStop,
})
stopResponse, err := m.ctl.ControlService(ctx, &svcctl.ControlServiceRequest{
Service: svc.handle,
Control: ServiceControlStop,
})
if err != nil {
if stopResponse == nil || stopResponse.Return != ErrorServiceNotActive {
if err != nil {
if stopResponse == nil || stopResponse.Return != ErrorServiceNotActive {
log.Error().Err(err).Msg("Failed to stop existing service")
return fmt.Errorf("stop service: %w", err)
}
log.Error().Err(err).Msg("Failed to stop existing service")
return fmt.Errorf("stop service: %w", err)
}
log.Debug().Msg("Service is not running")
log.Debug().Msg("Service is not running")
// FEATURE: restore state
/*
defer m.AddCleaners(func(ctxInner context.Context) error {
// ...
return nil
})
*/
// FEATURE: restore state
/*
defer m.AddCleaners(func(ctxInner context.Context) error {
// ...
return nil
})
*/
} else {
log.Info().Msg("Stopped existing service")
}
} else {
log.Info().Msg("Stopped existing service")
}
req := &svcctl.ChangeServiceConfigWRequest{
Service: svc.handle,
BinaryPathName: in.String(),
DisplayName: svc.originalConfig.DisplayName,
ServiceType: svc.originalConfig.ServiceType,
StartType: ServiceDemandStart,
ErrorControl: svc.originalConfig.ErrorControl,
LoadOrderGroup: svc.originalConfig.LoadOrderGroup,
ServiceStartName: svc.originalConfig.ServiceStartName,
TagID: svc.originalConfig.TagID,
Dependencies: parseDependencies(svc.originalConfig.Dependencies),
}
req := &svcctl.ChangeServiceConfigWRequest{
Service: svc.handle,
BinaryPathName: in.String(),
DisplayName: svc.originalConfig.DisplayName,
ServiceType: svc.originalConfig.ServiceType,
StartType: ServiceDemandStart,
ErrorControl: svc.originalConfig.ErrorControl,
LoadOrderGroup: svc.originalConfig.LoadOrderGroup,
ServiceStartName: svc.originalConfig.ServiceStartName,
TagID: svc.originalConfig.TagID,
Dependencies: parseDependencies(svc.originalConfig.Dependencies),
}
bpn := svc.originalConfig.BinaryPathName
bpn := svc.originalConfig.BinaryPathName
_, err = m.ctl.ChangeServiceConfigW(ctx, req)
_, err = m.ctl.ChangeServiceConfigW(ctx, req)
if err != nil {
log.Error().Err(err).Msg("Failed to request service configuration change")
return fmt.Errorf("change service config request: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to request service configuration change")
return fmt.Errorf("change service config request: %w", err)
}
if !m.NoStart {
err = m.startService(ctx, svc)
if err != nil {
log.Error().Err(err).Msg("Failed to start service")
}
}
if !m.NoStart {
err = m.startService(ctx, svc)
if err != nil {
log.Error().Err(err).Msg("Failed to start service")
}
}
if !m.NoRevert {
if svc.handle == nil {
if !m.NoRevert {
if svc.handle == nil {
if err = m.Reconnect(ctx); err != nil {
return err
}
svc, err = m.openService(ctx, svc.name)
if err = m.Reconnect(ctx); err != nil {
return err
}
svc, err = m.openService(ctx, svc.name)
if err != nil {
log.Error().Err(err).Msg("Failed to reopen service handle")
return fmt.Errorf("reopen service: %w", err)
}
}
req.BinaryPathName = bpn
req.Service = svc.handle
_, err := m.ctl.ChangeServiceConfigW(ctx, req)
if err != nil {
log.Error().Err(err).Msg("Failed to reopen service handle")
return fmt.Errorf("reopen service: %w", err)
}
}
req.BinaryPathName = bpn
req.Service = svc.handle
_, err := m.ctl.ChangeServiceConfigW(ctx, req)
if err != nil {
log.Error().Err(err).Msg("Failed to restore original service configuration")
return fmt.Errorf("restore service config: %w", err)
}
log.Info().Msg("Restored original service configuration")
}
if err != nil {
log.Error().Err(err).Msg("Failed to restore original service configuration")
return fmt.Errorf("restore service config: %w", err)
}
log.Info().Msg("Restored original service configuration")
}
return
return
}
+92 -92
View File
@@ -1,129 +1,129 @@
package scmrexec
import (
"context"
"fmt"
"github.com/FalconOpsLLC/goexec/internal/util"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"github.com/rs/zerolog"
"context"
"fmt"
"github.com/FalconOpsLLC/goexec/internal/util"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"github.com/rs/zerolog"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
const (
MethodCreate = "Create"
MethodCreate = "Create"
)
type ScmrCreate struct {
Scmr
goexec.Cleaner
goexec.Executor
Scmr
goexec.Cleaner
goexec.Executor
IO goexec.ExecutionIO
IO goexec.ExecutionIO
NoDelete bool
NoStart bool
ServiceName string
DisplayName string
NoDelete bool
NoStart bool
ServiceName string
DisplayName string
}
func (m *ScmrCreate) ensure() {
if m.ServiceName == "" {
m.ServiceName = util.RandomString()
}
if m.DisplayName == "" {
m.DisplayName = m.ServiceName
}
if m.ServiceName == "" {
m.ServiceName = util.RandomString()
}
if m.DisplayName == "" {
m.DisplayName = m.ServiceName
}
}
func (m *ScmrCreate) Execute(ctx context.Context, in *goexec.ExecutionIO) (err error) {
m.ensure()
m.ensure()
log := zerolog.Ctx(ctx).With().
Str("service", m.ServiceName).Logger()
log := zerolog.Ctx(ctx).With().
Str("service", m.ServiceName).Logger()
svc := &service{name: m.ServiceName}
svc := &service{name: m.ServiceName}
resp, err := m.ctl.CreateServiceW(ctx, &svcctl.CreateServiceWRequest{
ServiceManager: m.scm,
ServiceName: m.ServiceName,
DisplayName: m.DisplayName,
BinaryPathName: in.String(),
ServiceType: ServiceWin32OwnProcess,
StartType: ServiceDemandStart,
DesiredAccess: ServiceAllAccess, // TODO: Replace
})
resp, err := m.ctl.CreateServiceW(ctx, &svcctl.CreateServiceWRequest{
ServiceManager: m.scm,
ServiceName: m.ServiceName,
DisplayName: m.DisplayName,
BinaryPathName: in.String(),
ServiceType: ServiceWin32OwnProcess,
StartType: ServiceDemandStart,
DesiredAccess: ServiceAllAccess, // TODO: Replace
})
if err != nil {
log.Error().Err(err).Msg("Create service request failed")
return fmt.Errorf("create service request: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Create service request failed")
return fmt.Errorf("create service request: %w", err)
}
if resp.Return != 0 {
log.Error().Err(err).Msg("Failed to create service")
return fmt.Errorf("create service returned non-zero exit code: %02x", resp.Return)
}
if resp.Return != 0 {
log.Error().Err(err).Msg("Failed to create service")
return fmt.Errorf("create service returned non-zero exit code: %02x", resp.Return)
}
if !m.NoDelete {
m.AddCleaners(func(ctxInner context.Context) error {
if !m.NoDelete {
m.AddCleaners(func(ctxInner context.Context) error {
r, errInner := m.ctl.DeleteService(ctxInner, &svcctl.DeleteServiceRequest{
Service: svc.handle,
})
if errInner != nil {
return fmt.Errorf("delete service: %w", errInner)
}
if r.Return != 0 {
return fmt.Errorf("delete service returned non-zero exit code: %02x", r.Return)
}
log.Info().Msg("Deleted service")
r, errInner := m.ctl.DeleteService(ctxInner, &svcctl.DeleteServiceRequest{
Service: svc.handle,
})
if errInner != nil {
return fmt.Errorf("delete service: %w", errInner)
}
if r.Return != 0 {
return fmt.Errorf("delete service returned non-zero exit code: %02x", r.Return)
}
log.Info().Msg("Deleted service")
return nil
})
}
return nil
})
}
m.AddCleaners(func(ctxInner context.Context) error {
m.AddCleaners(func(ctxInner context.Context) error {
r, errInner := m.ctl.CloseService(ctxInner, &svcctl.CloseServiceRequest{
ServiceObject: svc.handle,
})
if errInner != nil {
return fmt.Errorf("close service: %w", errInner)
}
if r.Return != 0 {
return fmt.Errorf("close service returned non-zero exit code: %02x", r.Return)
}
log.Info().Msg("Closed service handle")
r, errInner := m.ctl.CloseService(ctxInner, &svcctl.CloseServiceRequest{
ServiceObject: svc.handle,
})
if errInner != nil {
return fmt.Errorf("close service: %w", errInner)
}
if r.Return != 0 {
return fmt.Errorf("close service returned non-zero exit code: %02x", r.Return)
}
log.Info().Msg("Closed service handle")
return nil
})
return nil
})
log.Info().Msg("Created service")
svc.handle = resp.Service
log.Info().Msg("Created service")
svc.handle = resp.Service
if !m.NoStart {
if !m.NoStart {
err = m.startService(ctx, svc)
err = m.startService(ctx, svc)
if err != nil {
log.Error().Err(err).Msg("Failed to start service")
return fmt.Errorf("start service: %w", err)
}
}
if svc.handle == nil {
if err != nil {
log.Error().Err(err).Msg("Failed to start service")
return fmt.Errorf("start service: %w", err)
}
}
if svc.handle == nil {
if err = m.Reconnect(ctx); err != nil {
return err
}
svc, err = m.openService(ctx, svc.name)
if err = m.Reconnect(ctx); err != nil {
return err
}
svc, err = m.openService(ctx, svc.name)
if err != nil {
log.Error().Err(err).Msg("Failed to reopen service handle")
return fmt.Errorf("reopen service: %w", err)
}
}
if err != nil {
log.Error().Err(err).Msg("Failed to reopen service handle")
return fmt.Errorf("reopen service: %w", err)
}
}
return
return
}
+127 -127
View File
@@ -1,195 +1,195 @@
package scmrexec
import (
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/internal/util"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/midl/uuid"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"github.com/rs/zerolog"
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/internal/util"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/midl/uuid"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"github.com/rs/zerolog"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
type Scmr struct {
goexec.Cleaner
goexec.Cleaner
Client *dce.Client
ctl svcctl.SvcctlClient
scm *svcctl.Handle
Client *dce.Client
ctl svcctl.SvcctlClient
scm *svcctl.Handle
hostname string
hostname string
}
const (
ModuleName = "SCMR"
ModuleName = "SCMR"
DefaultEndpoint = "ncacn_np:[svcctl]"
ScmrUuid = "367ABB81-9844-35F1-AD32-98F038001003"
DefaultEndpoint = "ncacn_np:[svcctl]"
ScmrUuid = "367ABB81-9844-35F1-AD32-98F038001003"
)
func (m *Scmr) Connect(ctx context.Context) (err error) {
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
}
func (m *Scmr) Init(ctx context.Context) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).Logger()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).Logger()
if m.Client == nil || m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
if m.Client == nil || m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
m.hostname, err = m.Client.Target.Hostname(ctx)
if err != nil {
log.Debug().Err(err).Msg("Failed to determine target hostname")
}
if m.hostname == "" {
m.hostname = util.RandomHostname()
}
m.hostname, err = m.Client.Target.Hostname(ctx)
if err != nil {
log.Debug().Err(err).Msg("Failed to determine target hostname")
}
if m.hostname == "" {
m.hostname = util.RandomHostname()
}
svcctlOpts := []dcerpc.Option{dcerpc.WithObjectUUID(uuid.MustParse(ScmrUuid))}
svcctlOpts := []dcerpc.Option{dcerpc.WithObjectUUID(uuid.MustParse(ScmrUuid))}
if m.Client.Smb {
svcctlOpts = append(svcctlOpts, dcerpc.WithInsecure())
}
if m.Client.Smb {
svcctlOpts = append(svcctlOpts, dcerpc.WithInsecure())
}
m.ctl, err = svcctl.NewSvcctlClient(ctx, m.Client.Dce(), svcctlOpts...)
if err != nil {
log.Error().Err(err).Msg("Failed to initialize SVCCTL client")
return fmt.Errorf("create SVCCTL client: %w", err)
}
log.Info().Msg("Created SVCCTL client")
m.ctl, err = svcctl.NewSvcctlClient(ctx, m.Client.Dce(), svcctlOpts...)
if err != nil {
log.Error().Err(err).Msg("Failed to initialize SVCCTL client")
return fmt.Errorf("create SVCCTL client: %w", err)
}
log.Info().Msg("Created SVCCTL client")
resp, err := m.ctl.OpenSCMW(ctx, &svcctl.OpenSCMWRequest{
MachineName: m.hostname,
DatabaseName: "ServicesActive",
DesiredAccess: ServiceAllAccess,
})
if err != nil {
log.Debug().Err(err).Msg("Failed to open SCM handle")
return fmt.Errorf("open SCM handle: %w", err)
}
log.Info().Msg("Opened SCM handle")
resp, err := m.ctl.OpenSCMW(ctx, &svcctl.OpenSCMWRequest{
MachineName: m.hostname,
DatabaseName: "ServicesActive",
DesiredAccess: ServiceAllAccess,
})
if err != nil {
log.Debug().Err(err).Msg("Failed to open SCM handle")
return fmt.Errorf("open SCM handle: %w", err)
}
log.Info().Msg("Opened SCM handle")
m.scm = resp.SCM
m.scm = resp.SCM
return
return
}
func (m *Scmr) Reconnect(ctx context.Context) (err error) {
if err = m.Client.Reconnect(ctx); err != nil {
return fmt.Errorf("reconnect: %w", err)
}
if err = m.Init(ctx); err != nil {
return fmt.Errorf("reconnect SCMR: %w", err)
}
return
if err = m.Client.Reconnect(ctx); err != nil {
return fmt.Errorf("reconnect: %w", err)
}
if err = m.Init(ctx); err != nil {
return fmt.Errorf("reconnect SCMR: %w", err)
}
return
}
// openService will a handle to the desired service
func (m *Scmr) openService(ctx context.Context, name string) (svc *service, err error) {
log := zerolog.Ctx(ctx)
log := zerolog.Ctx(ctx)
resp, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{
ServiceManager: m.scm,
ServiceName: name,
DesiredAccess: ServiceAllAccess, // TODO: dynamic
})
if err != nil {
log.Error().Err(err).Msg("Failed to open service handle")
return nil, fmt.Errorf("open service: %w", err)
}
resp, err := m.ctl.OpenServiceW(ctx, &svcctl.OpenServiceWRequest{
ServiceManager: m.scm,
ServiceName: name,
DesiredAccess: ServiceAllAccess, // TODO: dynamic
})
if err != nil {
log.Error().Err(err).Msg("Failed to open service handle")
return nil, fmt.Errorf("open service: %w", err)
}
log.Info().Msg("Opened service handle")
log.Info().Msg("Opened service handle")
svc = new(service)
svc.name = name
svc.handle = resp.Service
svc = new(service)
svc.name = name
svc.handle = resp.Service
return
return
}
func (m *Scmr) startService(ctx context.Context, svc *service) error {
log := zerolog.Ctx(ctx).With().
Str("service", svc.name).Logger()
log := zerolog.Ctx(ctx).With().
Str("service", svc.name).Logger()
sr, err := m.ctl.StartServiceW(ctx, &svcctl.StartServiceWRequest{Service: svc.handle})
sr, err := m.ctl.StartServiceW(ctx, &svcctl.StartServiceWRequest{Service: svc.handle})
if err != nil {
if err != nil {
if errors.Is(err, context.DeadlineExceeded) { // Check if execution timed out (execute "cmd.exe /c notepad" for test case)
log.Warn().Msg("Service execution deadline exceeded")
svc.handle = nil
return nil
if errors.Is(err, context.DeadlineExceeded) { // Check if execution timed out (execute "cmd.exe /c notepad" for test case)
log.Warn().Msg("Service execution deadline exceeded")
svc.handle = nil
return nil
} else if sr.Return == ErrorServiceRequestTimeout {
log.Info().Msg("Received request timeout. Execution was likely successful")
return nil
}
} else if sr.Return == ErrorServiceRequestTimeout {
log.Info().Msg("Received request timeout. Execution was likely successful")
return nil
}
log.Error().Err(err).Msg("Failed to start service")
return fmt.Errorf("start service: %w", err)
}
log.Info().Msg("Service started successfully")
return nil
log.Error().Err(err).Msg("Failed to start service")
return fmt.Errorf("start service: %w", err)
}
log.Info().Msg("Service started successfully")
return nil
}
func (m *Scmr) deleteService(ctx context.Context, svc *service) (err error) {
log := zerolog.Ctx(ctx).With().
Str("service", svc.name).Logger()
log := zerolog.Ctx(ctx).With().
Str("service", svc.name).Logger()
deleteResponse, err := m.ctl.DeleteService(ctx, &svcctl.DeleteServiceRequest{
Service: svc.handle,
})
deleteResponse, err := m.ctl.DeleteService(ctx, &svcctl.DeleteServiceRequest{
Service: svc.handle,
})
if err != nil {
log.Error().Err(err).Msg("Failed to delete service")
return fmt.Errorf("delete service: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to delete service")
return fmt.Errorf("delete service: %w", err)
}
if deleteResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", deleteResponse.Return)).Msg("Failed to delete service")
return fmt.Errorf("delete service returned non-zero exit code: 0x%02x", deleteResponse.Return)
}
if deleteResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", deleteResponse.Return)).Msg("Failed to delete service")
return fmt.Errorf("delete service returned non-zero exit code: 0x%02x", deleteResponse.Return)
}
log.Info().Msg("Deleted service")
return
log.Info().Msg("Deleted service")
return
}
func (m *Scmr) closeService(ctx context.Context, svc *service) (err error) {
log := zerolog.Ctx(ctx).With().
Str("service", svc.name).Logger()
log := zerolog.Ctx(ctx).With().
Str("service", svc.name).Logger()
closResponse, err := m.ctl.CloseService(ctx, &svcctl.CloseServiceRequest{
ServiceObject: svc.handle,
})
closResponse, err := m.ctl.CloseService(ctx, &svcctl.CloseServiceRequest{
ServiceObject: svc.handle,
})
if err != nil {
log.Error().Err(err).Msg("Failed to close service handle")
return fmt.Errorf("close service: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to close service handle")
return fmt.Errorf("close service: %w", err)
}
if closResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", closResponse.Return)).Msg("Failed to close service handle")
return fmt.Errorf("close service returned non-zero exit code: 0x%02x", closResponse.Return)
}
if closResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", closResponse.Return)).Msg("Failed to close service handle")
return fmt.Errorf("close service returned non-zero exit code: 0x%02x", closResponse.Return)
}
log.Info().Msg("Closed service handle")
return
log.Info().Msg("Closed service handle")
return
}
+48 -48
View File
@@ -1,59 +1,59 @@
package scmrexec
import (
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"golang.org/x/text/encoding/unicode"
"strings"
"github.com/oiweiwei/go-msrpc/msrpc/scmr/svcctl/v2"
"golang.org/x/text/encoding/unicode"
"strings"
)
const (
ErrorServiceRequestTimeout uint32 = 0x0000041d
ErrorServiceNotActive uint32 = 0x00000426
ErrorServiceRequestTimeout uint32 = 0x0000041d
ErrorServiceNotActive uint32 = 0x00000426
ServiceDemandStart uint32 = 0x00000003
ServiceWin32OwnProcess uint32 = 0x00000010
ServiceDemandStart uint32 = 0x00000003
ServiceWin32OwnProcess uint32 = 0x00000010
// https://learn.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights
// https://learn.microsoft.com/en-us/windows/win32/services/service-security-and-access-rights
ServiceQueryConfig uint32 = 0x00000001
ServiceChangeConfig uint32 = 0x00000002
ServiceStart uint32 = 0x00000010
ServiceStop uint32 = 0x00000020
ServiceDelete uint32 = 0x00010000 // special permission
ServiceControlStop uint32 = 0x00000001
ScManagerCreateService uint32 = 0x00000002
ServiceQueryConfig uint32 = 0x00000001
ServiceChangeConfig uint32 = 0x00000002
ServiceStart uint32 = 0x00000010
ServiceStop uint32 = 0x00000020
ServiceDelete uint32 = 0x00010000 // special permission
ServiceControlStop uint32 = 0x00000001
ScManagerCreateService uint32 = 0x00000002
/*
// Windows error codes
ERROR_FILE_NOT_FOUND uint32 = 0x00000002
ERROR_SERVICE_DOES_NOT_EXIST uint32 = 0x00000424
/*
// Windows error codes
ERROR_FILE_NOT_FOUND uint32 = 0x00000002
ERROR_SERVICE_DOES_NOT_EXIST uint32 = 0x00000424
// Windows service/scm constants
SERVICE_BOOT_START uint32 = 0x00000000
SERVICE_SYSTEM_START uint32 = 0x00000001
SERVICE_AUTO_START uint32 = 0x00000002
SERVICE_DISABLED uint32 = 0x00000004
// Windows service/scm constants
SERVICE_BOOT_START uint32 = 0x00000000
SERVICE_SYSTEM_START uint32 = 0x00000001
SERVICE_AUTO_START uint32 = 0x00000002
SERVICE_DISABLED uint32 = 0x00000004
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/4e91ff36-ab5f-49ed-a43d-a308e72b0b3c
SERVICE_CONTINUE_PENDING uint32 = 0x00000005
SERVICE_PAUSE_PENDING uint32 = 0x00000006
SERVICE_PAUSED uint32 = 0x00000007
SERVICE_RUNNING uint32 = 0x00000004
SERVICE_START_PENDING uint32 = 0x00000002
SERVICE_STOP_PENDING uint32 = 0x00000003
SERVICE_STOPPED uint32 = 0x00000001
*/
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/4e91ff36-ab5f-49ed-a43d-a308e72b0b3c
SERVICE_CONTINUE_PENDING uint32 = 0x00000005
SERVICE_PAUSE_PENDING uint32 = 0x00000006
SERVICE_PAUSED uint32 = 0x00000007
SERVICE_RUNNING uint32 = 0x00000004
SERVICE_START_PENDING uint32 = 0x00000002
SERVICE_STOP_PENDING uint32 = 0x00000003
SERVICE_STOPPED uint32 = 0x00000001
*/
ServiceDeleteAccess = ServiceDelete
ServiceModifyAccess = ServiceQueryConfig | ServiceChangeConfig | ServiceStop | ServiceStart | ServiceDelete
ServiceCreateAccess = ScManagerCreateService | ServiceStart | ServiceStop | ServiceDelete
ServiceAllAccess = ServiceCreateAccess | ServiceModifyAccess
ServiceDeleteAccess = ServiceDelete
ServiceModifyAccess = ServiceQueryConfig | ServiceChangeConfig | ServiceStop | ServiceStart | ServiceDelete
ServiceCreateAccess = ScManagerCreateService | ServiceStart | ServiceStop | ServiceDelete
ServiceAllAccess = ServiceCreateAccess | ServiceModifyAccess
)
type service struct {
name string
handle *svcctl.Handle
originalConfig *svcctl.QueryServiceConfigW
name string
handle *svcctl.Handle
originalConfig *svcctl.QueryServiceConfigW
}
// parseDependencies will parse the dependencies returned from a RQueryServiceConfigW
@@ -61,13 +61,13 @@ type service struct {
// the lpDependencies field as defined in the microsoft docs.
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-scmr/3ab258d6-87b0-459e-8d83-a2cdd8038b78
func parseDependencies(deps string) (out []byte) {
if deps != "" && deps != "/" {
if deps != "" && deps != "/" {
if out, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder().Bytes(
[]byte(strings.ReplaceAll(deps, "/", "\x00") + "\x00"),
); err == nil {
return out
}
}
return nil
if out, err := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder().Bytes(
[]byte(strings.ReplaceAll(deps, "/", "\x00") + "\x00"),
); err == nil {
return out
}
}
return nil
}
+110 -110
View File
@@ -1,155 +1,155 @@
package tschexec
import (
"context"
"encoding/xml"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/tsch/task"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
"regexp"
"time"
"context"
"encoding/xml"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/tsch/task"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
"regexp"
"time"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
const (
FlagTaskUpdate uint32 = 0b_00000000_00000000_00000000_00000100
MethodChange = "Change"
DefaultWaitTime = 1 * time.Second
FlagTaskUpdate uint32 = 0b_00000000_00000000_00000000_00000100
MethodChange = "Change"
DefaultWaitTime = 1 * time.Second
)
type TschChange struct {
Tsch
goexec.Executor
goexec.Cleaner
Tsch
goexec.Executor
goexec.Cleaner
IO goexec.ExecutionIO
IO goexec.ExecutionIO
WorkingDirectory string
NoStart bool
NoRevert bool
WaitTime time.Duration
WorkingDirectory string
NoStart bool
NoRevert bool
WaitTime time.Duration
}
func (m *TschChange) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodChange).
Str("task", m.TaskPath).
Logger()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodChange).
Str("task", m.TaskPath).
Logger()
retrieveResponse, err := m.tsch.RetrieveTask(ctx, &itaskschedulerservice.RetrieveTaskRequest{
Path: m.TaskPath,
})
retrieveResponse, err := m.tsch.RetrieveTask(ctx, &itaskschedulerservice.RetrieveTaskRequest{
Path: m.TaskPath,
})
if err != nil {
log.Error().Err(err).Msg("Failed to retrieve task")
return fmt.Errorf("retrieve task: %w", err)
}
if retrieveResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", retrieveResponse.Return)).
Msg("Failed to retrieve task")
return fmt.Errorf("retrieve task returned non-zero exit code: %02x", retrieveResponse.Return)
}
if err != nil {
log.Error().Err(err).Msg("Failed to retrieve task")
return fmt.Errorf("retrieve task: %w", err)
}
if retrieveResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", retrieveResponse.Return)).
Msg("Failed to retrieve task")
return fmt.Errorf("retrieve task returned non-zero exit code: %02x", retrieveResponse.Return)
}
log.Info().Msg("Successfully retrieved existing task definition")
log.Debug().Str("xml", retrieveResponse.XML).Msg("Got task definition")
log.Info().Msg("Successfully retrieved existing task definition")
log.Debug().Str("xml", retrieveResponse.XML).Msg("Got task definition")
tk := task.Task{}
tk := task.Task{}
enc := regexp.MustCompile(`(?i)^<\?xml .*?\?>`)
tkStr := enc.ReplaceAllString(retrieveResponse.XML, `<?xml version="1.0" encoding="utf-8"?>`)
enc := regexp.MustCompile(`(?i)^<\?xml .*?\?>`)
tkStr := enc.ReplaceAllString(retrieveResponse.XML, `<?xml version="1.0" encoding="utf-8"?>`)
if err = xml.Unmarshal([]byte(tkStr), &tk); err != nil {
log.Error().Err(err).Msg("Failed to unmarshal task XML")
if err = xml.Unmarshal([]byte(tkStr), &tk); err != nil {
log.Error().Err(err).Msg("Failed to unmarshal task XML")
return fmt.Errorf("unmarshal task XML: %w", err)
}
return fmt.Errorf("unmarshal task XML: %w", err)
}
cmd := execIO.CommandLine()
cmd := execIO.CommandLine()
tk.Actions.Exec = append(tk.Actions.Exec, task.ExecAction{
Command: cmd[0],
Arguments: cmd[1],
WorkingDirectory: m.WorkingDirectory,
})
tk.Actions.Exec = append(tk.Actions.Exec, task.ExecAction{
Command: cmd[0],
Arguments: cmd[1],
WorkingDirectory: m.WorkingDirectory,
})
doc, err := xml.Marshal(tk)
doc, err := xml.Marshal(tk)
if err != nil {
log.Error().Err(err).Msg("failed to marshal task XML")
return fmt.Errorf("marshal task: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("failed to marshal task XML")
return fmt.Errorf("marshal task: %w", err)
}
taskXml := TaskXmlHeader + string(doc)
log.Debug().Str("xml", taskXml).Msg("Serialized new task")
taskXml := TaskXmlHeader + string(doc)
log.Debug().Str("xml", taskXml).Msg("Serialized new task")
registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: m.TaskPath,
XML: taskXml,
Flags: FlagTaskUpdate,
})
registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: m.TaskPath,
XML: taskXml,
Flags: FlagTaskUpdate,
})
if !m.NoRevert {
if !m.NoRevert {
m.AddCleaners(func(ctxInner context.Context) error {
m.AddCleaners(func(ctxInner context.Context) error {
revertResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: m.TaskPath,
XML: retrieveResponse.XML,
Flags: FlagTaskUpdate,
})
revertResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: m.TaskPath,
XML: retrieveResponse.XML,
Flags: FlagTaskUpdate,
})
if err != nil {
return err
}
if revertResponse.Return != 0 {
return fmt.Errorf("revert task definition returned non-zero exit code: %02x", revertResponse.Return)
}
return nil
})
}
if err != nil {
return err
}
if revertResponse.Return != 0 {
return fmt.Errorf("revert task definition returned non-zero exit code: %02x", revertResponse.Return)
}
return nil
})
}
if err != nil {
log.Error().Err(err).Msg("Failed to update task")
if err != nil {
log.Error().Err(err).Msg("Failed to update task")
return fmt.Errorf("update task: %w", err)
}
if registerResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", registerResponse.Return)).Msg("Failed to update task definition")
return fmt.Errorf("update task: %w", err)
}
if registerResponse.Return != 0 {
log.Error().Err(err).Str("code", fmt.Sprintf("0x%02x", registerResponse.Return)).Msg("Failed to update task definition")
return fmt.Errorf("update task returned non-zero exit code: %02x", registerResponse.Return)
}
log.Info().Msg("Successfully updated task definition")
return fmt.Errorf("update task returned non-zero exit code: %02x", registerResponse.Return)
}
log.Info().Msg("Successfully updated task definition")
if !m.NoStart {
if !m.NoStart {
runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{
Path: m.TaskPath,
})
runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{
Path: m.TaskPath,
})
if err != nil {
log.Error().Err(err).Msg("Failed to run modified task")
if err != nil {
log.Error().Err(err).Msg("Failed to run modified task")
return fmt.Errorf("run task: %w", err)
}
return fmt.Errorf("run task: %w", err)
}
if ret := uint32(runResponse.Return); ret != 0 {
log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Run task returned non-zero exit code")
if ret := uint32(runResponse.Return); ret != 0 {
log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Run task returned non-zero exit code")
return fmt.Errorf("run task returned non-zero exit code: 0x%08x", ret)
}
return fmt.Errorf("run task returned non-zero exit code: 0x%08x", ret)
}
log.Info().Msg("Successfully started modified task")
}
log.Info().Msg("Successfully started modified task")
}
if m.WaitTime <= 0 {
m.WaitTime = DefaultWaitTime
}
time.Sleep(m.WaitTime)
return
if m.WaitTime <= 0 {
m.WaitTime = DefaultWaitTime
}
time.Sleep(m.WaitTime)
return
}
+59 -59
View File
@@ -1,84 +1,84 @@
package tschexec
import (
"context"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
"context"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
const (
MethodDemand = "Demand"
MethodDemand = "Demand"
)
type TschDemand struct {
Tsch
goexec.Executor
goexec.Cleaner
Tsch
goexec.Executor
goexec.Cleaner
IO goexec.ExecutionIO
IO goexec.ExecutionIO
NoDelete bool
NoStart bool
SessionId uint32
NoDelete bool
NoStart bool
SessionId uint32
}
func (m *TschDemand) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodDemand).
Str("task", m.TaskPath).
Logger()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodDemand).
Str("task", m.TaskPath).
Logger()
path, err := m.registerTask(ctx,
&registerOptions{
AllowStartOnDemand: true,
AllowHardTerminate: true,
Hidden: !m.NotHidden,
triggers: taskTriggers{},
},
execIO,
)
if err != nil {
return err
}
path, err := m.registerTask(ctx,
&registerOptions{
AllowStartOnDemand: true,
AllowHardTerminate: true,
Hidden: !m.NotHidden,
triggers: taskTriggers{},
},
execIO,
)
if err != nil {
return err
}
log.Info().Msg("Task registered")
log.Info().Msg("Task registered")
if !m.NoDelete {
m.AddCleaners(func(ctxInner context.Context) error {
return m.deleteTask(ctxInner, path)
})
}
if !m.NoDelete {
m.AddCleaners(func(ctxInner context.Context) error {
return m.deleteTask(ctxInner, path)
})
}
if !m.NoStart {
if !m.NoStart {
var flags uint32
if m.SessionId != 0 {
flags |= 4
}
var flags uint32
if m.SessionId != 0 {
flags |= 4
}
runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{
Path: path,
Flags: flags,
SessionID: m.SessionId,
})
runResponse, err := m.tsch.Run(ctx, &itaskschedulerservice.RunRequest{
Path: path,
Flags: flags,
SessionID: m.SessionId,
})
if err != nil {
log.Error().Err(err).Msg("Failed to run task")
return fmt.Errorf("run task: %w", err)
}
if ret := uint32(runResponse.Return); ret != 0 {
log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Task returned non-zero exit code")
return fmt.Errorf("task returned non-zero exit code: 0x%08x", ret)
}
if err != nil {
log.Error().Err(err).Msg("Failed to run task")
return fmt.Errorf("run task: %w", err)
}
if ret := uint32(runResponse.Return); ret != 0 {
log.Error().Str("code", fmt.Sprintf("0x%08x", ret)).Msg("Task returned non-zero exit code")
return fmt.Errorf("task returned non-zero exit code: 0x%08x", ret)
}
log.Info().Msg("Task started successfully")
}
return
log.Info().Msg("Task started successfully")
}
return
}
+121 -121
View File
@@ -1,176 +1,176 @@
package tschexec
import (
"context"
"encoding/xml"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
"strings"
"context"
"encoding/xml"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/msrpc/tsch/itaskschedulerservice/v1"
"github.com/rs/zerolog"
"strings"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
)
const (
ModuleName = "TSCH"
ModuleName = "TSCH"
)
type Tsch struct {
goexec.Cleaner
goexec.Cleaner
Client *dce.Client
tsch itaskschedulerservice.TaskSchedulerServiceClient
Client *dce.Client
tsch itaskschedulerservice.TaskSchedulerServiceClient
TaskPath string
UserSid string
NotHidden bool
TaskPath string
UserSid string
NotHidden bool
}
type registerOptions struct {
AllowStartOnDemand bool
AllowHardTerminate bool
StartWhenAvailable bool
Hidden bool
DeleteAfter string
AllowStartOnDemand bool
AllowHardTerminate bool
StartWhenAvailable bool
Hidden bool
DeleteAfter string
triggers taskTriggers
triggers taskTriggers
}
func (m *Tsch) Connect(ctx context.Context) (err error) {
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
}
func (m *Tsch) Init(ctx context.Context) (err error) {
if m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
if m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
// Create ITaskSchedulerService Client
m.tsch, err = itaskschedulerservice.NewTaskSchedulerServiceClient(ctx, m.Client.Dce(), dcerpc.WithSeal())
return
// Create ITaskSchedulerService Client
m.tsch, err = itaskschedulerservice.NewTaskSchedulerServiceClient(ctx, m.Client.Dce(), dcerpc.WithSeal())
return
}
func (m *Tsch) registerTask(ctx context.Context, opts *registerOptions, in *goexec.ExecutionIO) (path string, err error) {
log := zerolog.Ctx(ctx).With().
Str("task", m.TaskPath).
Logger()
log := zerolog.Ctx(ctx).With().
Str("task", m.TaskPath).
Logger()
ctx = log.WithContext(ctx)
ctx = log.WithContext(ctx)
principalId := "LocalSystem"
principalId := "LocalSystem"
settings := taskSettings{
MultipleInstancesPolicy: "IgnoreNew",
IdleSettings: taskIdleSettings{
StopOnIdleEnd: true,
RestartOnIdle: false,
},
Enabled: true,
Priority: 7, // a pretty standard value for scheduled tasks
AllowHardTerminate: opts.AllowHardTerminate,
AllowStartOnDemand: opts.AllowStartOnDemand,
Hidden: opts.Hidden,
StartWhenAvailable: opts.StartWhenAvailable,
DeleteExpiredTaskAfter: opts.DeleteAfter,
}
settings := taskSettings{
MultipleInstancesPolicy: "IgnoreNew",
IdleSettings: taskIdleSettings{
StopOnIdleEnd: true,
RestartOnIdle: false,
},
Enabled: true,
Priority: 7, // a pretty standard value for scheduled tasks
AllowHardTerminate: opts.AllowHardTerminate,
AllowStartOnDemand: opts.AllowStartOnDemand,
Hidden: opts.Hidden,
StartWhenAvailable: opts.StartWhenAvailable,
DeleteExpiredTaskAfter: opts.DeleteAfter,
}
principals := taskPrincipals{
Principals: []taskPrincipal{
{
ID: principalId,
UserID: m.UserSid,
RunLevel: "HighestAvailable",
},
}}
principals := taskPrincipals{
Principals: []taskPrincipal{
{
ID: principalId,
UserID: m.UserSid,
RunLevel: "HighestAvailable",
},
}}
var cmd, args string
var cmd, args string
cmdline := in.CommandLine()
cmdline := in.CommandLine()
if l := len(cmdline); l >= 1 {
cmd = cmdline[0]
if l >= 2 {
args = strings.Join(cmdline[1:], " ")
}
}
if l := len(cmdline); l >= 1 {
cmd = cmdline[0]
if l >= 2 {
args = strings.Join(cmdline[1:], " ")
}
}
actions := taskActions{
Context: principalId,
Exec: []taskActionExec{
{
Command: cmd,
Arguments: args,
},
},
}
actions := taskActions{
Context: principalId,
Exec: []taskActionExec{
{
Command: cmd,
Arguments: args,
},
},
}
def := simpleTask{
TaskVersion: TaskXmlVersion,
TaskNamespace: TaskXmlNamespace,
Triggers: opts.triggers,
Actions: actions,
Principals: principals,
Settings: settings,
}
def := simpleTask{
TaskVersion: TaskXmlVersion,
TaskNamespace: TaskXmlNamespace,
Triggers: opts.triggers,
Actions: actions,
Principals: principals,
Settings: settings,
}
// Generate task XML content. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/0d6383e4-de92-43e7-b0bb-a60cfa36379f
// Generate task XML content. See https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/0d6383e4-de92-43e7-b0bb-a60cfa36379f
doc, err := xml.Marshal(def)
doc, err := xml.Marshal(def)
if err != nil {
log.Error().Err(err).Msg("failed to marshal task XML")
return "", fmt.Errorf("marshal task: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("failed to marshal task XML")
return "", fmt.Errorf("marshal task: %w", err)
}
taskXml := TaskXmlHeader + string(doc)
taskXml := TaskXmlHeader + string(doc)
log.Debug().Str("content", taskXml).Msg("Generated task XML")
log.Debug().Str("content", taskXml).Msg("Generated task XML")
registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: m.TaskPath,
XML: taskXml,
Flags: 0, // FEATURE: dynamic
SDDL: "",
LogonType: 0, // FEATURE: dynamic
CredsCount: 0,
Creds: nil,
})
registerResponse, err := m.tsch.RegisterTask(ctx, &itaskschedulerservice.RegisterTaskRequest{
Path: m.TaskPath,
XML: taskXml,
Flags: 0, // FEATURE: dynamic
SDDL: "",
LogonType: 0, // FEATURE: dynamic
CredsCount: 0,
Creds: nil,
})
if err != nil {
log.Error().Err(err).Msg("Failed to register task")
return "", fmt.Errorf("register task: %w", err)
}
log.Info().Msg("Scheduled task registered")
if err != nil {
log.Error().Err(err).Msg("Failed to register task")
return "", fmt.Errorf("register task: %w", err)
}
log.Info().Msg("Scheduled task registered")
return registerResponse.ActualPath, nil
return registerResponse.ActualPath, nil
}
func (m *Tsch) deleteTask(ctx context.Context, taskPath string) (err error) {
log := zerolog.Ctx(ctx).With().
Str("path", taskPath).Logger()
log := zerolog.Ctx(ctx).With().
Str("path", taskPath).Logger()
_, err = m.tsch.Delete(ctx, &itaskschedulerservice.DeleteRequest{
Path: taskPath,
})
_, err = m.tsch.Delete(ctx, &itaskschedulerservice.DeleteRequest{
Path: taskPath,
})
if err != nil {
log.Error().Err(err).Msg("Failed to delete task")
return fmt.Errorf("delete task: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to delete task")
return fmt.Errorf("delete task: %w", err)
}
log.Info().Msg("Task deleted")
log.Info().Msg("Task deleted")
return
return
}
+104 -104
View File
@@ -1,145 +1,145 @@
package wmiexec
import (
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/iactivation/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemlevel1login/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemservices/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmio/query"
"github.com/rs/zerolog"
"context"
"errors"
"fmt"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/FalconOpsLLC/goexec/pkg/goexec/dce"
"github.com/oiweiwei/go-msrpc/dcerpc"
"github.com/oiweiwei/go-msrpc/msrpc/dcom"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/iactivation/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemlevel1login/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmi/iwbemservices/v0"
"github.com/oiweiwei/go-msrpc/msrpc/dcom/wmio/query"
"github.com/rs/zerolog"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/wmi"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/wmi"
)
const (
ModuleName = "WMI"
DefaultEndpoint = "ncacn_ip_tcp:[135]"
ModuleName = "WMI"
DefaultEndpoint = "ncacn_ip_tcp:[135]"
)
type Wmi struct {
goexec.Cleaner
Client *dce.Client
goexec.Cleaner
Client *dce.Client
Resource string
Resource string
servicesClient iwbemservices.ServicesClient
servicesClient iwbemservices.ServicesClient
}
func (m *Wmi) Connect(ctx context.Context) (err error) {
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
if err = m.Client.Connect(ctx); err == nil {
m.AddCleaners(m.Client.Close)
}
return
}
func (m *Wmi) Init(ctx context.Context) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).Logger()
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).Logger()
if m.Client == nil || m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
if m.Client == nil || m.Client.Dce() == nil {
return errors.New("DCE connection not initialized")
}
actClient, err := iactivation.NewActivationClient(ctx, m.Client.Dce())
if err != nil {
log.Error().Err(err).Msg("Failed to initialize IActivation client")
return fmt.Errorf("create IActivation client: %w", err)
}
actClient, err := iactivation.NewActivationClient(ctx, m.Client.Dce())
if err != nil {
log.Error().Err(err).Msg("Failed to initialize IActivation client")
return fmt.Errorf("create IActivation client: %w", err)
}
actResponse, err := actClient.RemoteActivation(ctx, &iactivation.RemoteActivationRequest{
ORPCThis: ORPCThis,
ClassID: wmi.Level1LoginClassID.GUID(),
IIDs: []*dcom.IID{iwbemlevel1login.Level1LoginIID},
RequestedProtocolSequences: []uint16{ProtocolSequenceRPC}, // FEATURE: Named pipe support?
})
if err != nil {
log.Error().Err(err).Msg("Failed to activate remote object")
return fmt.Errorf("request remote activation: %w", err)
}
if actResponse.HResult != 0 {
return fmt.Errorf("remote activation failed with code %d", actResponse.HResult)
}
actResponse, err := actClient.RemoteActivation(ctx, &iactivation.RemoteActivationRequest{
ORPCThis: ORPCThis,
ClassID: wmi.Level1LoginClassID.GUID(),
IIDs: []*dcom.IID{iwbemlevel1login.Level1LoginIID},
RequestedProtocolSequences: []uint16{ProtocolSequenceRPC}, // FEATURE: Named pipe support?
})
if err != nil {
log.Error().Err(err).Msg("Failed to activate remote object")
return fmt.Errorf("request remote activation: %w", err)
}
if actResponse.HResult != 0 {
return fmt.Errorf("remote activation failed with code %d", actResponse.HResult)
}
log.Info().Msg("Remote activation succeeded")
log.Info().Msg("Remote activation succeeded")
var newOpts []dcerpc.Option
var newOpts []dcerpc.Option
for _, bind := range actResponse.OXIDBindings.GetStringBindings() {
stringBinding, err := dcerpc.ParseStringBinding(bind.String())
if err != nil {
log.Debug().Err(err).Msg("Failed to parse string binding")
continue
}
// Only consider ncacn_ip_tcp endpoints
if stringBinding.ProtocolSequence == dcerpc.ProtocolSequenceIPTCP {
stringBinding.NetworkAddress = m.Client.Target.AddressWithoutPort()
newOpts = append(newOpts, dcerpc.WithEndpoint(stringBinding.String()))
}
}
for _, bind := range actResponse.OXIDBindings.GetStringBindings() {
stringBinding, err := dcerpc.ParseStringBinding(bind.String())
if err != nil {
log.Debug().Err(err).Msg("Failed to parse string binding")
continue
}
// Only consider ncacn_ip_tcp endpoints
if stringBinding.ProtocolSequence == dcerpc.ProtocolSequenceIPTCP {
stringBinding.NetworkAddress = m.Client.Target.AddressWithoutPort()
newOpts = append(newOpts, dcerpc.WithEndpoint(stringBinding.String()))
}
}
if err = m.Client.Reconnect(ctx, newOpts...); err != nil {
log.Error().Err(err).Msg("Failed to connect to remote instance")
return fmt.Errorf("connect remote instance: %w", err)
}
if err = m.Client.Reconnect(ctx, newOpts...); err != nil {
log.Error().Err(err).Msg("Failed to connect to remote instance")
return fmt.Errorf("connect remote instance: %w", err)
}
log.Info().Msg("Connected to remote instance")
log.Info().Msg("Connected to remote instance")
ipid := actResponse.InterfaceData[0].GetStandardObjectReference().Std.IPID
loginClient, err := iwbemlevel1login.NewLevel1LoginClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid))
ipid := actResponse.InterfaceData[0].GetStandardObjectReference().Std.IPID
loginClient, err := iwbemlevel1login.NewLevel1LoginClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid))
if err != nil {
log.Error().Err(err).Msg("Failed to create IWbemLevel1Login client")
return fmt.Errorf("create IWbemLevel1Login client: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to create IWbemLevel1Login client")
return fmt.Errorf("create IWbemLevel1Login client: %w", err)
}
login, err := loginClient.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{
This: ORPCThis,
NetworkResource: m.Resource,
})
login, err := loginClient.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{
This: ORPCThis,
NetworkResource: m.Resource,
})
if err != nil {
log.Error().Err(err).Msg("Failed to login on remote instance")
return fmt.Errorf("login: IWbemLevel1Login::NTLMLogin: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to login on remote instance")
return fmt.Errorf("login: IWbemLevel1Login::NTLMLogin: %w", err)
}
log.Info().Msg("Completed NTLMLogin operation")
log.Info().Msg("Completed NTLMLogin operation")
ipid = login.Namespace.InterfacePointer().IPID()
m.servicesClient, err = iwbemservices.NewServicesClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid))
ipid = login.Namespace.InterfacePointer().IPID()
m.servicesClient, err = iwbemservices.NewServicesClient(ctx, m.Client.Dce(), dcom.WithIPID(ipid))
if err != nil {
log.Error().Err(err).Msg("Failed to create services client")
return fmt.Errorf("create IWbemServices client: %w", err)
}
if err != nil {
log.Error().Err(err).Msg("Failed to create services client")
return fmt.Errorf("create IWbemServices client: %w", err)
}
log.Info().Msg("Initialized services client")
log.Info().Msg("Initialized services client")
return
return
}
func (m *Wmi) query(ctx context.Context, class, method string, values map[string]any) (map[string]any, error) {
if m.servicesClient == nil {
return nil, errors.New("module has not been initialized")
}
if out, err := query.NewBuilder(ctx, m.servicesClient, ComVersion).
Spawn(class). // The class to instantiate (i.e., Win32_Process)
Method(method). // The method to call (i.e., Create)
Values(values). // The values to pass to method
Exec().
Object(); err == nil {
return out.Values(), err
} else {
return nil, fmt.Errorf("spawn WMI query: %w", err)
}
if m.servicesClient == nil {
return nil, errors.New("module has not been initialized")
}
if out, err := query.NewBuilder(ctx, m.servicesClient, ComVersion).
Spawn(class). // The class to instantiate (i.e., Win32_Process)
Method(method). // The method to call (i.e., Create)
Values(values). // The values to pass to method
Exec().
Object(); err == nil {
return out.Values(), err
} else {
return nil, fmt.Errorf("spawn WMI query: %w", err)
}
}
+39 -39
View File
@@ -1,59 +1,59 @@
package wmiexec
import (
"context"
"errors"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/rs/zerolog"
"context"
"errors"
"github.com/FalconOpsLLC/goexec/pkg/goexec"
"github.com/rs/zerolog"
)
const (
MethodProc = "Proc"
MethodProc = "Proc"
)
type WmiProc struct {
Wmi
IO goexec.ExecutionIO
WorkingDirectory string
Wmi
IO goexec.ExecutionIO
WorkingDirectory string
}
func (m *WmiProc) Execute(ctx context.Context, execIO *goexec.ExecutionIO) (err error) {
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodProc).
Logger()
ctx = log.WithContext(ctx)
log := zerolog.Ctx(ctx).With().
Str("module", ModuleName).
Str("method", MethodProc).
Logger()
ctx = log.WithContext(ctx)
if execIO == nil {
return errors.New("execution IO is nil")
}
if execIO == nil {
return errors.New("execution IO is nil")
}
out, err := m.query(ctx,
"Win32_Process",
"Create",
map[string]any{
"CommandLine": execIO.String(),
"WorkingDir": m.WorkingDirectory,
},
)
if err != nil {
return
}
out, err := m.query(ctx,
"Win32_Process",
"Create",
map[string]any{
"CommandLine": execIO.String(),
"WorkingDir": m.WorkingDirectory,
},
)
if err != nil {
return
}
if pid, ok := out["ProcessId"].(uint32); pid != 0 {
log = log.With().Uint32("pid", pid).Logger()
if pid, ok := out["ProcessId"].(uint32); pid != 0 {
log = log.With().Uint32("pid", pid).Logger()
} else if !ok {
return errors.New("process creation failed")
}
log.Info().Err(err).Msg("Process created")
} else if !ok {
return errors.New("process creation failed")
}
log.Info().Err(err).Msg("Process created")
if ret, ok := out["ReturnValue"].(uint32); ret != 0 {
log.Error().Err(err).Uint32("return", ret).Msg("Process returned non-zero exit code")
if ret, ok := out["ReturnValue"].(uint32); ret != 0 {
log.Error().Err(err).Uint32("return", ret).Msg("Process returned non-zero exit code")
} else if !ok {
return errors.New("invalid call response")
}
return
} else if !ok {
return errors.New("invalid call response")
}
return
}