mirror of
https://github.com/FalconOpsLLC/goexec
synced 2026-06-06 15:44:27 +00:00
177 lines
4.1 KiB
Go
177 lines
4.1 KiB
Go
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"
|
|
|
|
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/ntstatus"
|
|
_ "github.com/oiweiwei/go-msrpc/msrpc/erref/win32"
|
|
)
|
|
|
|
const (
|
|
ModuleName = "TSCH"
|
|
)
|
|
|
|
type Tsch struct {
|
|
goexec.Cleaner
|
|
|
|
Client *dce.Client
|
|
tsch itaskschedulerservice.TaskSchedulerServiceClient
|
|
|
|
TaskPath string
|
|
UserSid string
|
|
NotHidden bool
|
|
}
|
|
|
|
type registerOptions struct {
|
|
AllowStartOnDemand bool
|
|
AllowHardTerminate bool
|
|
StartWhenAvailable bool
|
|
Hidden bool
|
|
DeleteAfter string
|
|
|
|
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
|
|
}
|
|
|
|
func (m *Tsch) Init(ctx context.Context) (err error) {
|
|
|
|
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
|
|
}
|
|
|
|
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()
|
|
|
|
ctx = log.WithContext(ctx)
|
|
|
|
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,
|
|
}
|
|
|
|
principals := taskPrincipals{
|
|
Principals: []taskPrincipal{
|
|
{
|
|
ID: principalId,
|
|
UserID: m.UserSid,
|
|
RunLevel: "HighestAvailable",
|
|
},
|
|
}}
|
|
|
|
var cmd, args string
|
|
|
|
cmdline := in.CommandLine()
|
|
|
|
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,
|
|
},
|
|
},
|
|
}
|
|
|
|
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
|
|
|
|
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)
|
|
}
|
|
|
|
taskXml := TaskXmlHeader + string(doc)
|
|
|
|
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,
|
|
})
|
|
|
|
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
|
|
}
|
|
|
|
func (m *Tsch) deleteTask(ctx context.Context, taskPath string) (err error) {
|
|
|
|
log := zerolog.Ctx(ctx).With().
|
|
Str("path", taskPath).Logger()
|
|
|
|
_, 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)
|
|
}
|
|
|
|
log.Info().Msg("Task deleted")
|
|
|
|
return
|
|
}
|