mirror of
https://github.com/FalconOpsLLC/goexec
synced 2026-06-06 15:44:27 +00:00
Merge dev into main (#22)
* Bump adauth to v0.5.0 * Open remote output file as RW * Removed out-of-date TODO.md * Add `--epm`; deprecate `--no-epm` * Fix parsing of `--epm`/`--no-epm` * Resolve #21
This commit is contained in:
@@ -1,80 +0,0 @@
|
||||
# TODO
|
||||
|
||||
We wanted to make development of this project as transparent as possible, so we've included our development TODOs in this file.
|
||||
|
||||
## TSCH
|
||||
|
||||
- [X] Clean up TSCH module
|
||||
- [X] Session hijacking
|
||||
- [X] Generate random name/path
|
||||
- [X] Output
|
||||
- [X] Add `tsch change`
|
||||
- [ ] Serialize XML with default indent level?
|
||||
|
||||
### `tsch change`
|
||||
|
||||
- [ ] Add `--session` (like `tsch demand`)
|
||||
- [ ] Add the option to avoid direct task start using TimeTrigger (similar to `tsch create`)
|
||||
|
||||
## SCMR
|
||||
|
||||
- [X] Clean up SCMR module
|
||||
- [X] add dynamic string binding support
|
||||
- [X] general cleanup. Use TSCH & WMI as reference
|
||||
- [ ] Output
|
||||
|
||||
## DCOM
|
||||
|
||||
- [X] Add DCOM module
|
||||
- [X] MMC20.Application method
|
||||
- [X] Output
|
||||
|
||||
## WMI
|
||||
|
||||
- [X] Add WMI module
|
||||
- [X] Clean up WMI module
|
||||
- [X] Output
|
||||
- [ ] WMI `reg` subcommand - read & edit the registry
|
||||
- [ ] File transfer functionality
|
||||
|
||||
## Other
|
||||
|
||||
- [X] Add proxy support - see https://github.com/oiweiwei/go-msrpc/issues/21
|
||||
- [X] README
|
||||
- [X] pprof integration (hidden flag(s))
|
||||
- [X] Descriptions for all modules and methods
|
||||
- [ ] Add SMB file transfer interface
|
||||
|
||||
## Bugs
|
||||
|
||||
- [X] (Fixed) SMB transport for SCMR module - `rpc_s_cannot_support: The requested operation is not supported.`
|
||||
- [X] (Fixed) Proxy - EPM doesn't use the proxy dialer
|
||||
- [X] (Fixed) Kerberos requests don't dial through proxy
|
||||
- [X] (Fixed) Panic when closing nil log file
|
||||
- [X] `scmr change` doesn't revert service cmdline
|
||||
- [X] Fix SCMR `change` method so that dependencies field isn't permanently overwritten
|
||||
|
||||
## Lower Priority
|
||||
|
||||
- [ ] `--shell` option
|
||||
- [ ] Add Go tests
|
||||
- [ ] ability to specify multiple targets
|
||||
|
||||
### TSCH
|
||||
|
||||
- [ ] Add more trigger types
|
||||
|
||||
### SCMR
|
||||
|
||||
- [ ] `psexec` with PsExeSVC.exe AND NOT Impacket's RemCom build - https://sensepost.com/blog/2025/psexecing-the-right-way-and-why-zero-trust-is-mandatory/
|
||||
|
||||
### DCOM
|
||||
|
||||
- [X] ShellWindows
|
||||
- [ ] ShellBrowserWindow
|
||||
|
||||
### WinRM
|
||||
|
||||
- [ ] Add basic WinRM module - https://github.com/bryanmcnulty/winrm
|
||||
- [ ] File transfer functionality
|
||||
- [ ] Shell functionality
|
||||
+113
-106
@@ -1,34 +1,42 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
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, "Don't use EPM to discover 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, "Don't use EPM to discover RPC endpoints")
|
||||
fs.BoolVar(&rpcClient.UseEpm, "epm", false, "Use EPM to discover available bindings")
|
||||
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")
|
||||
if err := fs.MarkHidden("no-epm"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := fs.MarkDeprecated("no-epm", "use --epm=false instead"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
//cmd.MarkFlagsMutuallyExclusive("endpoint", "epm-filter")
|
||||
//cmd.MarkFlagsMutuallyExclusive("no-epm", "epm-filter")
|
||||
}
|
||||
|
||||
// FUTURE: automatically stage & execute file
|
||||
@@ -40,127 +48,126 @@ 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, endpoint string) func(cmd *cobra.Command, args []string) error {
|
||||
return args(
|
||||
argsTarget(proto),
|
||||
return args(
|
||||
argsTarget(proto),
|
||||
|
||||
func(cmd *cobra.Command, args []string) (err error) {
|
||||
if rpcClient.Endpoint == "" {
|
||||
if endpoint != "" {
|
||||
rpcClient.Endpoint = endpoint
|
||||
rpcClient.NoEpm = true
|
||||
} else {
|
||||
rpcClient.NoEpm = false
|
||||
}
|
||||
}
|
||||
rpcClient.Target = target
|
||||
rpcClient.Credential = credential
|
||||
rpcClient.Proxy = proxy
|
||||
func(cmd *cobra.Command, args []string) (err error) {
|
||||
switch {
|
||||
case rpcClient.Endpoint != "":
|
||||
case endpoint == "":
|
||||
rpcClient.UseEpm = true
|
||||
default:
|
||||
rpcClient.Endpoint = endpoint
|
||||
}
|
||||
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
|
||||
})...)
|
||||
}
|
||||
|
||||
+67
-67
@@ -1,111 +1,111 @@
|
||||
package dce
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/RedTeamPentesting/adauth/smbauth"
|
||||
"github.com/oiweiwei/go-msrpc/dcerpc"
|
||||
"github.com/oiweiwei/go-msrpc/msrpc/epm/epm/v3"
|
||||
msrpcSMB2 "github.com/oiweiwei/go-msrpc/smb2"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/RedTeamPentesting/adauth/smbauth"
|
||||
"github.com/oiweiwei/go-msrpc/dcerpc"
|
||||
"github.com/oiweiwei/go-msrpc/msrpc/epm/epm/v3"
|
||||
msrpcSMB2 "github.com/oiweiwei/go-msrpc/smb2"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Options
|
||||
Options
|
||||
|
||||
conn dcerpc.Conn
|
||||
conn dcerpc.Conn
|
||||
}
|
||||
|
||||
func (c *Client) String() string {
|
||||
return ClientName
|
||||
return ClientName
|
||||
}
|
||||
|
||||
func (c *Client) Reconnect(ctx context.Context, opts ...dcerpc.Option) (err error) {
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, opts...)
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, opts...)
|
||||
|
||||
return c.Connect(ctx)
|
||||
return c.Connect(ctx)
|
||||
}
|
||||
|
||||
func (c *Client) Dce() (dce dcerpc.Conn) {
|
||||
return c.conn
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func (c *Client) Logger(ctx context.Context) (log zerolog.Logger) {
|
||||
return zerolog.Ctx(ctx).With().
|
||||
Str("client", c.String()).Logger()
|
||||
return zerolog.Ctx(ctx).With().
|
||||
Str("client", c.String()).Logger()
|
||||
}
|
||||
|
||||
func (c *Client) Connect(ctx context.Context) (err error) {
|
||||
|
||||
log := c.Logger(ctx)
|
||||
ctx = log.WithContext(ctx)
|
||||
log := c.Logger(ctx)
|
||||
ctx = log.WithContext(ctx)
|
||||
|
||||
var do, eo []dcerpc.Option
|
||||
var do, eo []dcerpc.Option
|
||||
|
||||
do = append(do, c.DcerpcOptions...)
|
||||
do = append(do, c.authOptions...)
|
||||
do = append(do, c.DcerpcOptions...)
|
||||
do = append(do, c.authOptions...)
|
||||
|
||||
if c.Smb {
|
||||
var so []msrpcSMB2.DialerOption
|
||||
if c.Smb {
|
||||
var so []msrpcSMB2.DialerOption
|
||||
|
||||
if !c.NoSign {
|
||||
so = append(so, msrpcSMB2.WithSign())
|
||||
eo = append(eo, dcerpc.WithSign())
|
||||
}
|
||||
if !c.NoSeal {
|
||||
so = append(so, msrpcSMB2.WithSeal())
|
||||
eo = append(eo, dcerpc.WithSeal())
|
||||
}
|
||||
if !c.NoSign {
|
||||
so = append(so, msrpcSMB2.WithSign())
|
||||
eo = append(eo, dcerpc.WithSign())
|
||||
}
|
||||
if !c.NoSeal {
|
||||
so = append(so, msrpcSMB2.WithSeal())
|
||||
eo = append(eo, dcerpc.WithSeal())
|
||||
}
|
||||
|
||||
if smbDialer, err := smbauth.Dialer(ctx, c.Credential, c.Target, &smbauth.Options{
|
||||
SMBOptions: so,
|
||||
KerberosDialer: c.dialer,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("parse smb auth: %w", err)
|
||||
if smbDialer, err := smbauth.Dialer(ctx, c.Credential, c.Target, &smbauth.Options{
|
||||
SMBOptions: so,
|
||||
KerberosDialer: c.dialer,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("parse smb auth: %w", err)
|
||||
|
||||
} else {
|
||||
do = append(do, dcerpc.WithSMBDialer(smbDialer))
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
do = append(do, dcerpc.WithSMBDialer(smbDialer))
|
||||
}
|
||||
} else {
|
||||
|
||||
if !c.NoSign {
|
||||
do = append(do, dcerpc.WithSign())
|
||||
eo = append(eo, dcerpc.WithSign())
|
||||
}
|
||||
if !c.NoSeal {
|
||||
do = append(do, dcerpc.WithSeal())
|
||||
eo = append(eo, dcerpc.WithSeal())
|
||||
}
|
||||
}
|
||||
if !c.NoSign {
|
||||
do = append(do, dcerpc.WithSign())
|
||||
eo = append(eo, dcerpc.WithSign())
|
||||
}
|
||||
if !c.NoSeal {
|
||||
do = append(do, dcerpc.WithSeal())
|
||||
eo = append(eo, dcerpc.WithSeal())
|
||||
}
|
||||
}
|
||||
|
||||
if !c.NoLog {
|
||||
do = append(do, dcerpc.WithLogger(log))
|
||||
eo = append(eo, dcerpc.WithLogger(log))
|
||||
}
|
||||
if !c.NoLog {
|
||||
do = append(do, dcerpc.WithLogger(log))
|
||||
eo = append(eo, dcerpc.WithLogger(log))
|
||||
}
|
||||
|
||||
if !c.NoEpm {
|
||||
log.Debug().Msg("Using endpoint mapper")
|
||||
if c.UseEpm && !c.NoEpm {
|
||||
log.Debug().Msg("Using endpoint mapper")
|
||||
|
||||
eo = append(eo, c.EpmOptions...)
|
||||
eo = append(eo, c.authOptions...)
|
||||
eo = append(eo, c.EpmOptions...)
|
||||
eo = append(eo, c.authOptions...)
|
||||
|
||||
do = append(do, epm.EndpointMapper(ctx, c.Host, eo...))
|
||||
}
|
||||
do = append(do, epm.EndpointMapper(ctx, c.Host, eo...))
|
||||
}
|
||||
|
||||
for _, e := range c.stringBindings {
|
||||
do = append(do, dcerpc.WithEndpoint(e.String()))
|
||||
}
|
||||
for _, e := range c.stringBindings {
|
||||
do = append(do, dcerpc.WithEndpoint(e.String()))
|
||||
}
|
||||
|
||||
if c.conn, err = dcerpc.Dial(ctx, c.Host, do...); err != nil {
|
||||
if c.conn, err = dcerpc.Dial(ctx, c.Host, do...); err != nil {
|
||||
|
||||
log.Error().Err(err).Msgf("Failed to connect to %s endpoint", c.String())
|
||||
return fmt.Errorf("dial %s: %w", c.String(), err)
|
||||
}
|
||||
log.Error().Err(err).Msgf("Failed to connect to %s endpoint", c.String())
|
||||
return fmt.Errorf("dial %s: %w", c.String(), err)
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) Close(ctx context.Context) (err error) {
|
||||
return c.conn.Close(ctx)
|
||||
return c.conn.Close(ctx)
|
||||
}
|
||||
|
||||
+92
-89
@@ -1,118 +1,121 @@
|
||||
package dce
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
"github.com/RedTeamPentesting/adauth"
|
||||
"github.com/RedTeamPentesting/adauth/dcerpcauth"
|
||||
"github.com/oiweiwei/go-msrpc/dcerpc"
|
||||
"github.com/FalconOpsLLC/goexec/pkg/goexec"
|
||||
"github.com/RedTeamPentesting/adauth"
|
||||
"github.com/RedTeamPentesting/adauth/dcerpcauth"
|
||||
"github.com/oiweiwei/go-msrpc/dcerpc"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
goexec.ClientOptions
|
||||
goexec.AuthOptions
|
||||
goexec.ClientOptions
|
||||
goexec.AuthOptions
|
||||
|
||||
// NoSign disables packet signing by omitting dcerpc.WithSign()
|
||||
NoSign bool `json:"no_sign" yaml:"no_sign"`
|
||||
// NoSign disables packet signing by omitting dcerpc.WithSign()
|
||||
NoSign bool `json:"no_sign" yaml:"no_sign"`
|
||||
|
||||
// NoSeal disables packet stub encryption by omitting dcerpc.WithSeal()
|
||||
NoSeal bool `json:"no_seal" yaml:"no_seal"`
|
||||
// NoSeal disables packet stub encryption by omitting dcerpc.WithSeal()
|
||||
NoSeal bool `json:"no_seal" yaml:"no_seal"`
|
||||
|
||||
// NoLog disables logging by omitting dcerpc.WithLogger(...)
|
||||
NoLog bool `json:"no_log" yaml:"no_log"`
|
||||
// NoLog disables logging by omitting dcerpc.WithLogger(...)
|
||||
NoLog bool `json:"no_log" yaml:"no_log"`
|
||||
|
||||
// NoEpm disables DCE endpoint mapper communications
|
||||
NoEpm bool `json:"no_epm" yaml:"no_epm"`
|
||||
// UseEpm enables DCE endpoint mapper communications
|
||||
UseEpm bool `json:"use_epm" yaml:"use_epm"`
|
||||
|
||||
// Endpoint stores the explicit DCE string binding to use
|
||||
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
// NoEpm disables DCE endpoint mapper communications
|
||||
NoEpm bool `json:"no_epm" yaml:"no_epm"` // Deprecated in favor of UseEpm
|
||||
|
||||
// Filter stores the filter for returned endpoints from an endpoint mapper
|
||||
Filter string `json:"filter,omitempty" yaml:"filter,omitempty"`
|
||||
// Endpoint stores the explicit DCE string binding to use
|
||||
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
|
||||
// Smb enables SMB transport for DCE/RPC
|
||||
Smb bool `json:"use_smb" yaml:"use_smb"`
|
||||
// Filter stores the filter for returned endpoints from an endpoint mapper
|
||||
Filter string `json:"filter,omitempty" yaml:"filter,omitempty"`
|
||||
|
||||
stringBindings []*dcerpc.StringBinding
|
||||
dialer goexec.Dialer
|
||||
authOptions []dcerpc.Option
|
||||
DcerpcOptions []dcerpc.Option
|
||||
EpmOptions []dcerpc.Option
|
||||
// Smb enables SMB transport for DCE/RPC
|
||||
Smb bool `json:"use_smb" yaml:"use_smb"`
|
||||
|
||||
stringBindings []*dcerpc.StringBinding
|
||||
dialer goexec.Dialer
|
||||
authOptions []dcerpc.Option
|
||||
DcerpcOptions []dcerpc.Option
|
||||
EpmOptions []dcerpc.Option
|
||||
}
|
||||
|
||||
func (c *Client) Parse(ctx context.Context) (err error) {
|
||||
|
||||
// Reset internals
|
||||
{
|
||||
c.dialer = nil
|
||||
c.stringBindings = []*dcerpc.StringBinding{}
|
||||
c.authOptions = []dcerpc.Option{}
|
||||
c.DcerpcOptions = []dcerpc.Option{}
|
||||
c.EpmOptions = []dcerpc.Option{
|
||||
dcerpc.WithSign(), // Require signing for EPM
|
||||
}
|
||||
}
|
||||
// Reset internals
|
||||
{
|
||||
c.dialer = nil
|
||||
c.stringBindings = []*dcerpc.StringBinding{}
|
||||
c.authOptions = []dcerpc.Option{}
|
||||
c.DcerpcOptions = []dcerpc.Option{}
|
||||
c.EpmOptions = []dcerpc.Option{
|
||||
dcerpc.WithSign(), // Require signing for EPM
|
||||
}
|
||||
}
|
||||
|
||||
if !c.NoSeal {
|
||||
// Enable encryption
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy))
|
||||
c.EpmOptions = append(c.EpmOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy))
|
||||
}
|
||||
if !c.NoSign {
|
||||
// Enable signing
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSign())
|
||||
}
|
||||
if !c.NoSeal {
|
||||
// Enable encryption
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy))
|
||||
c.EpmOptions = append(c.EpmOptions, dcerpc.WithSeal(), dcerpc.WithSecurityLevel(dcerpc.AuthLevelPktPrivacy))
|
||||
}
|
||||
if !c.NoSign {
|
||||
// Enable signing
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithSign())
|
||||
}
|
||||
|
||||
// Parse DCERPC endpoint
|
||||
if c.Endpoint != "" {
|
||||
sb, err := dcerpc.ParseStringBinding(c.Endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe {
|
||||
c.Smb = true
|
||||
}
|
||||
c.stringBindings = append(c.stringBindings, sb)
|
||||
}
|
||||
// Parse DCERPC endpoint
|
||||
if c.Endpoint != "" {
|
||||
sb, err := dcerpc.ParseStringBinding(c.Endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe {
|
||||
c.Smb = true
|
||||
}
|
||||
c.stringBindings = append(c.stringBindings, sb)
|
||||
}
|
||||
|
||||
// Parse EPM filter
|
||||
if c.Filter != "" {
|
||||
sb, err := dcerpc.ParseStringBinding(c.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe {
|
||||
c.Smb = true
|
||||
}
|
||||
c.stringBindings = append(c.stringBindings, sb)
|
||||
}
|
||||
// Parse EPM filter
|
||||
if c.Filter != "" {
|
||||
sb, err := dcerpc.ParseStringBinding(c.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sb.ProtocolSequence == dcerpc.ProtocolSequenceNamedPipe {
|
||||
c.Smb = true
|
||||
}
|
||||
c.stringBindings = append(c.stringBindings, sb)
|
||||
}
|
||||
|
||||
if c.Proxy != "" {
|
||||
// Parse proxy URL
|
||||
c.dialer, err = goexec.ParseProxyURI(c.Proxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Proxy != "" {
|
||||
// Parse proxy URL
|
||||
c.dialer, err = goexec.ParseProxyURI(c.Proxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d := adauth.AsContextDialer(c.dialer)
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithDialer(d))
|
||||
c.EpmOptions = append(c.EpmOptions, dcerpc.WithDialer(d))
|
||||
d := adauth.AsContextDialer(c.dialer)
|
||||
c.DcerpcOptions = append(c.DcerpcOptions, dcerpc.WithDialer(d))
|
||||
c.EpmOptions = append(c.EpmOptions, dcerpc.WithDialer(d))
|
||||
|
||||
} else {
|
||||
c.dialer = &net.Dialer{}
|
||||
}
|
||||
} else {
|
||||
c.dialer = &net.Dialer{}
|
||||
}
|
||||
|
||||
// Parse authentication parameters
|
||||
if c.authOptions, err = dcerpcauth.AuthenticationOptions(ctx, c.Credential, c.Target, &dcerpcauth.Options{
|
||||
KerberosDialer: c.dialer, // Use the same net dialer as dcerpc
|
||||
}); err != nil {
|
||||
return fmt.Errorf("parse auth c: %w", err)
|
||||
}
|
||||
// Parse authentication parameters
|
||||
if c.authOptions, err = dcerpcauth.AuthenticationOptions(ctx, c.Credential, c.Target, &dcerpcauth.Options{
|
||||
KerberosDialer: c.dialer, // Use the same net dialer as dcerpc
|
||||
}); err != nil {
|
||||
return fmt.Errorf("parse auth c: %w", err)
|
||||
}
|
||||
|
||||
c.Host = c.Target.AddressWithoutPort()
|
||||
c.Host = c.Target.AddressWithoutPort()
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
+57
-59
@@ -1,99 +1,97 @@
|
||||
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 != "":
|
||||
copy(cmd, strings.SplitN(i.Command, " ", 2))
|
||||
case i.ExecutablePath != "":
|
||||
cmd[0] = i.ExecutablePath
|
||||
case i.Executable != "":
|
||||
cmd[0] = i.Executable
|
||||
}
|
||||
|
||||
case i.ExecutablePath != "":
|
||||
cmd[0] = i.ExecutablePath
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user