mirror of
https://github.com/FalconOpsLLC/goexec
synced 2026-06-06 15:44:27 +00:00
Update indent style
This commit is contained in:
+98
-98
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -3,5 +3,5 @@ package main
|
||||
import "github.com/FalconOpsLLC/goexec/cmd"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
+23
-23
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,
|
||||
®isterOptions{
|
||||
AllowStartOnDemand: true,
|
||||
AllowHardTerminate: true,
|
||||
Hidden: !m.NotHidden,
|
||||
triggers: taskTriggers{},
|
||||
},
|
||||
execIO,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path, err := m.registerTask(ctx,
|
||||
®isterOptions{
|
||||
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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user