1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
package scmrexec
import (
"context"
"errors"
"fmt"
dcerpc2 "github.com/FalconOpsLLC/goexec/internal/client/dcerpc"
"github.com/FalconOpsLLC/goexec/internal/exec"
"github.com/FalconOpsLLC/goexec/internal/windows"
"github.com/RedTeamPentesting/adauth"
"github.com/rs/zerolog"
)
const (
MethodCreate string = "create"
MethodModify string = "modify"
ServiceModifyAccess uint32 = windows.SERVICE_QUERY_CONFIG | windows.SERVICE_CHANGE_CONFIG | windows.SERVICE_STOP | windows.SERVICE_START | windows.SERVICE_DELETE
ServiceCreateAccess uint32 = windows.SC_MANAGER_CREATE_SERVICE | windows.SERVICE_START | windows.SERVICE_STOP | windows.SERVICE_DELETE
ServiceAllAccess uint32 = ServiceCreateAccess | ServiceModifyAccess
)
func (mod *Module) createClients(ctx context.Context) (cleanup func(cCtx context.Context), err error) {
cleanup = func(context.Context) {
if mod.dce != nil {
mod.log.Debug().Msg("Cleaning up clients")
if err := mod.dce.Close(ctx); err != nil {
mod.log.Error().Err(err).Msg("Failed to destroy DCE connection")
}
}
}
cleanup(ctx)
mod.dce = dcerpc2.NewDCEClient(ctx, false, &dcerpc2.SmbConfig{Port: 445})
cleanup = func(context.Context) {}
if err = mod.dce.Connect(ctx, mod.creds, mod.target); err != nil {
return nil, fmt.Errorf("connection to DCERPC failed: %w", err)
}
mod.ctl, err = mod.dce.OpenSvcctl(ctx)
return
}
func (mod *Module) Exec(ctx context.Context, creds *adauth.Credential, target *adauth.Target, ecfg *exec.ExecutionConfig) (err error) {
vctx := context.WithoutCancel(ctx)
mod.log = zerolog.Ctx(ctx).With().
Str("module", "scmr").
Str("method", ecfg.ExecutionMethod).Logger()
mod.creds = creds
mod.target = target
if ecfg.ExecutionMethod == MethodCreate {
if cfg, ok := ecfg.ExecutionMethodConfig.(MethodCreateConfig); !ok || cfg.ServiceName == "" {
return errors.New("invalid configuration")
} else {
if cleanup, err := mod.createClients(ctx); err != nil {
return fmt.Errorf("failed to create client: %w", err)
} else {
mod.log.Debug().Msg("Created clients")
defer cleanup(ctx)
}
svc := &service{
createConfig: &cfg,
name: cfg.ServiceName,
}
scm, code, err := mod.openSCM(ctx)
if err != nil {
return fmt.Errorf("failed to open SCM with code %d: %w", code, err)
}
mod.log.Debug().Msg("Opened handle to SCM")
code, err = mod.createService(ctx, scm, svc, ecfg)
if err != nil {
return fmt.Errorf("failed to create service with code %d: %w", code, err)
}
mod.log.Info().Str("service", svc.name).Msg("Service created")
// From here on out, make sure the service is properly deleted, even if the connection drops or something fails.
if !cfg.NoDelete {
defer func() {
// TODO: stop service?
if code, err = mod.deleteService(ctx, scm, svc); err != nil {
mod.log.Error().Err(err).Msg("Failed to delete service") // TODO
}
mod.log.Info().Str("service", svc.name).Msg("Service deleted successfully")
}()
}
if code, err = mod.startService(ctx, scm, svc); err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
// In case of timeout or cancel, try to reestablish a connection to restore the service
mod.log.Info().Msg("Service start timeout/cancelled. Execution likely successful")
mod.log.Info().Msg("Reconnecting for cleanup procedure")
ctx = vctx
if _, err = mod.createClients(ctx); err != nil {
mod.log.Error().Err(err).Msg("Reconnect failed")
} else if scm, code, err = mod.openSCM(ctx); err != nil {
mod.log.Error().Err(err).Msg("Failed to reopen SCM")
} else if svc.handle, code, err = mod.openService(ctx, scm, svc.name); err != nil {
mod.log.Error().Str("service", svc.name).Err(err).Msg("Failed to reopen service handle")
} else {
mod.log.Debug().Str("service", svc.name).Msg("Reconnection successful")
}
} else {
mod.log.Error().Err(err).Msg("Failed to start service")
}
} else {
mod.log.Info().Str("service", svc.name).Msg("Execution successful")
}
}
} else if ecfg.ExecutionMethod == MethodModify {
// Use service modification method
if cfg, ok := ecfg.ExecutionMethodConfig.(MethodModifyConfig); !ok || cfg.ServiceName == "" {
return errors.New("invalid configuration")
} else {
// Ensure that a command (executable full path + args) is supplied
cmd := ecfg.GetRawCommand()
if cmd == "" {
return errors.New("no command provided")
}
// Initialize protocol clients
if cleanup, err := mod.createClients(ctx); err != nil {
return fmt.Errorf("failed to create client: %w", err)
} else {
mod.log.Debug().Msg("Created clients")
defer cleanup(ctx)
}
svc := &service{modifyConfig: &cfg, name: cfg.ServiceName}
// Open SCM handle
scm, code, err := mod.openSCM(ctx)
if err != nil {
return fmt.Errorf("failed to create service with code %d: %w", code, err)
}
mod.log.Debug().Msg("Opened handle to SCM")
// Open service handle
if svc.handle, code, err = mod.openService(ctx, scm, svc.name); err != nil {
return fmt.Errorf("failed to open service with code %d: %w", code, err)
}
mod.log.Debug().Str("service", svc.name).Msg("Opened service")
// Stop service before editing
if !cfg.NoStart {
if code, err = mod.stopService(ctx, scm, svc); err != nil {
mod.log.Warn().Err(err).Msg("Failed to stop existing service")
} else if code == windows.ERROR_SERVICE_NOT_ACTIVE {
mod.log.Debug().Str("service", svc.name).Msg("Service is not running")
} else {
mod.log.Info().Str("service", svc.name).Msg("Stopped existing service")
defer func() {
if code, err = mod.startService(ctx, scm, svc); err != nil {
mod.log.Error().Err(err).Msg("Failed to restore service state to running")
}
}()
}
}
if code, err = mod.queryServiceConfig(ctx, svc); err != nil {
return fmt.Errorf("failed to query service configuration with code %d: %w", code, err)
}
mod.log.Debug().
Str("service", svc.name).
Str("command", svc.svcConfig.BinaryPathName).Msg("Fetched existing service configuration")
// Change service configuration
if code, err = mod.changeServiceConfigBinary(ctx, svc, cmd); err != nil {
return fmt.Errorf("failed to edit service configuration with code %d: %w", code, err)
}
defer func() {
// Revert configuration
if code, err = mod.changeServiceConfigBinary(ctx, svc, svc.svcConfig.BinaryPathName); err != nil {
mod.log.Error().Err(err).Msg("Failed to restore service configuration")
} else {
mod.log.Info().Str("service", svc.name).Msg("Restored service configuration")
}
}()
mod.log.Info().
Str("service", svc.name).
Str("command", cmd).Msg("Changed service configuration")
// Start service
if !cfg.NoStart {
if code, err = mod.startService(ctx, scm, svc); err != nil {
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
// In case of timeout or cancel, try to reestablish a connection to restore the service
mod.log.Info().Msg("Service start timeout/cancelled. Execution likely successful")
mod.log.Info().Msg("Reconnecting for cleanup procedure")
ctx = vctx
if _, err = mod.createClients(ctx); err != nil {
mod.log.Error().Err(err).Msg("Reconnect failed")
} else if scm, code, err = mod.openSCM(ctx); err != nil {
mod.log.Error().Err(err).Msg("Failed to reopen SCM")
} else if svc.handle, code, err = mod.openService(ctx, scm, svc.name); err != nil {
mod.log.Error().Str("service", svc.name).Err(err).Msg("Failed to reopen service handle")
} else {
mod.log.Debug().Str("service", svc.name).Msg("Reconnection successful")
}
} else {
mod.log.Error().Err(err).Msg("Failed to start service")
}
} else {
mod.log.Info().Str("service", svc.name).Msg("Started service")
}
defer func() {
// Stop service
if code, err = mod.stopService(ctx, scm, svc); err != nil {
mod.log.Error().Err(err).Msg("Failed to stop service")
} else {
mod.log.Info().Str("service", svc.name).Msg("Stopped service")
}
}()
}
}
} else {
return fmt.Errorf("invalid method: %s", ecfg.ExecutionMethod)
}
return err
}
|