diff --git a/agent/agent_unix.go b/agent/agent_unix.go index 0650512..993b446 100644 --- a/agent/agent_unix.go +++ b/agent/agent_unix.go @@ -162,7 +162,7 @@ func NewAgentConfig() *rmm.AgentConfig { return ret } -func (a *Agent) RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) { +func (a *Agent) RunScript(code string, shell string, args []string, timeout int, runasuser bool) (stdout, stderr string, exitcode int, e error) { code = removeWinNewLines(code) content := []byte(code) @@ -507,7 +507,7 @@ func (a *Agent) installMesh(meshbin, exe, proxy string) (string, error) { return "not implemented", nil } -func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) { +func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool, runasuser bool) (output [2]string, e error) { return [2]string{"", ""}, nil } diff --git a/agent/agent_windows.go b/agent/agent_windows.go index 6637e7b..306b969 100644 --- a/agent/agent_windows.go +++ b/agent/agent_windows.go @@ -30,6 +30,7 @@ import ( rmm "github.com/amidaware/rmmagent/shared" ps "github.com/elastic/go-sysinfo" + "github.com/fourcorelabs/wintoken" "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" "github.com/go-resty/resty/v2" @@ -81,7 +82,7 @@ func NewAgentConfig() *rmm.AgentConfig { } } -func (a *Agent) RunScript(code string, shell string, args []string, timeout int) (stdout, stderr string, exitcode int, e error) { +func (a *Agent) RunScript(code string, shell string, args []string, timeout int, runasuser bool) (stdout, stderr string, exitcode int, e error) { content := []byte(code) @@ -143,8 +144,16 @@ func (a *Agent) RunScript(code string, shell string, args []string, timeout int) ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) defer cancel() - var timedOut bool = false + var timedOut = false cmd := exec.Command(exe, cmdArgs...) + if runasuser { + token, err := wintoken.GetInteractiveToken(wintoken.TokenLinked) + if err != nil { + return "", err.Error(), 66, err + } + defer token.Close() + cmd.SysProcAttr = &syscall.SysProcAttr{Token: syscall.Token(token.Token()), HideWindow: true} + } cmd.Stdout = &outb cmd.Stderr = &errb @@ -230,7 +239,7 @@ func CMD(exe string, args []string, timeout int, detached bool) (output [2]strin return [2]string{CleanString(outb.String()), CleanString(errb.String())}, nil } -func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool) (output [2]string, e error) { +func CMDShell(shell string, cmdArgs []string, command string, timeout int, detached bool, runasuser bool) (output [2]string, e error) { var ( outb bytes.Buffer errb bytes.Buffer @@ -241,6 +250,8 @@ func CMDShell(shell string, cmdArgs []string, command string, timeout int, detac ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) defer cancel() + sysProcAttr := &windows.SysProcAttr{} + if len(cmdArgs) > 0 && command == "" { switch shell { case "cmd": @@ -254,9 +265,7 @@ func CMDShell(shell string, cmdArgs []string, command string, timeout int, detac switch shell { case "cmd": cmd = exec.Command("cmd.exe") - cmd.SysProcAttr = &windows.SysProcAttr{ - CmdLine: fmt.Sprintf("cmd.exe /C %s", command), - } + sysProcAttr.CmdLine = fmt.Sprintf("cmd.exe /C %s", command) case "powershell": cmd = exec.Command("Powershell", "-NonInteractive", "-NoProfile", command) } @@ -264,10 +273,20 @@ func CMDShell(shell string, cmdArgs []string, command string, timeout int, detac // https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags if detached { - cmd.SysProcAttr = &windows.SysProcAttr{ - CreationFlags: windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP, - } + sysProcAttr.CreationFlags = windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP } + + if runasuser { + token, err := wintoken.GetInteractiveToken(wintoken.TokenLinked) + if err != nil { + return [2]string{"", CleanString(err.Error())}, err + } + defer token.Close() + sysProcAttr.Token = syscall.Token(token.Token()) + sysProcAttr.HideWindow = true + } + + cmd.SysProcAttr = sysProcAttr cmd.Stdout = &outb cmd.Stderr = &errb cmd.Start() @@ -449,7 +468,7 @@ func (a *Agent) PlatVer() (string, error) { func EnablePing() { args := make([]string, 0) cmd := `netsh advfirewall firewall add rule name="ICMP Allow incoming V4 echo request" protocol=icmpv4:8,any dir=in action=allow` - _, err := CMDShell("cmd", args, cmd, 10, false) + _, err := CMDShell("cmd", args, cmd, 10, false, false) if err != nil { fmt.Println(err) } @@ -470,7 +489,7 @@ func EnableRDP() { args := make([]string, 0) cmd := `netsh advfirewall firewall set rule group="remote desktop" new enable=Yes` - _, cerr := CMDShell("cmd", args, cmd, 10, false) + _, cerr := CMDShell("cmd", args, cmd, 10, false, false) if cerr != nil { fmt.Println(cerr) } @@ -497,15 +516,15 @@ func DisableSleepHibernate() { wg.Add(1) go func(c string) { defer wg.Done() - _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /set%svalueindex scheme_current sub_buttons lidaction 0", c), 5, false) - _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -standby-timeout-%s 0", c), 5, false) - _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -hibernate-timeout-%s 0", c), 5, false) - _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -disk-timeout-%s 0", c), 5, false) - _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -monitor-timeout-%s 0", c), 5, false) + _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /set%svalueindex scheme_current sub_buttons lidaction 0", c), 5, false, false) + _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -standby-timeout-%s 0", c), 5, false, false) + _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -hibernate-timeout-%s 0", c), 5, false, false) + _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -disk-timeout-%s 0", c), 5, false, false) + _, _ = CMDShell("cmd", args, fmt.Sprintf("powercfg /x -monitor-timeout-%s 0", c), 5, false, false) }(i) } wg.Wait() - _, _ = CMDShell("cmd", args, "powercfg -S SCHEME_CURRENT", 5, false) + _, _ = CMDShell("cmd", args, "powercfg -S SCHEME_CURRENT", 5, false, false) } // NewCOMObject creates a new COM object for the specifed ProgramID. @@ -645,7 +664,7 @@ Add-MpPreference -ExclusionPath 'C:\Windows\Temp\tacticalagent-v*.exe' Add-MpPreference -ExclusionPath 'C:\Windows\Temp\trmm\*' Add-MpPreference -ExclusionPath 'C:\Program Files\Mesh Agent\*' ` - _, _, _, err := a.RunScript(code, "powershell", []string{}, 20) + _, _, _, err := a.RunScript(code, "powershell", []string{}, 20, false) if err != nil { a.Logger.Debugln(err) } diff --git a/agent/checks.go b/agent/checks.go index 3fb9ed8..531bf74 100644 --- a/agent/checks.go +++ b/agent/checks.go @@ -169,7 +169,7 @@ type ScriptCheckResult struct { // ScriptCheck runs either bat, powershell or python script func (a *Agent) ScriptCheck(data rmm.Check, r *resty.Client) { start := time.Now() - stdout, stderr, retcode, _ := a.RunScript(data.Script.Code, data.Script.Shell, data.ScriptArgs, data.Timeout) + stdout, stderr, retcode, _ := a.RunScript(data.Script.Code, data.Script.Shell, data.ScriptArgs, data.Timeout, data.Script.RunAsUser) payload := ScriptCheckResult{ ID: data.CheckPK, diff --git a/agent/choco_windows.go b/agent/choco_windows.go index 2d13f3b..c37f025 100644 --- a/agent/choco_windows.go +++ b/agent/choco_windows.go @@ -42,7 +42,7 @@ func (a *Agent) InstallChoco() { return } - _, _, exitcode, err := a.RunScript(string(r.Body()), "powershell", []string{}, 900) + _, _, exitcode, err := a.RunScript(string(r.Body()), "powershell", []string{}, 900, false) if err != nil { a.Logger.Debugln(err) a.rClient.R().SetBody(result).Post(url) diff --git a/agent/rpc.go b/agent/rpc.go index e457ecd..ce2a89c 100644 --- a/agent/rpc.go +++ b/agent/rpc.go @@ -40,6 +40,7 @@ type NatsMsg struct { PatchMgmt bool `json:"patch_mgmt"` ID int `json:"id"` Code string `json:"code"` + RunAsUser bool `json:"run_as_user"` } var ( @@ -177,7 +178,7 @@ func (a *Agent) RunRPC() { switch runtime.GOOS { case "windows": - out, _ := CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false) + out, _ := CMDShell(p.Data["shell"], []string{}, p.Data["command"], p.Timeout, false, p.RunAsUser) a.Logger.Debugln(out) if out[1] != "" { ret.Encode(out[1]) @@ -257,7 +258,7 @@ func (a *Agent) RunRPC() { var resultData rmm.RunScriptResp ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) start := time.Now() - stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout) + stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout, p.RunAsUser) resultData.ExecTime = time.Since(start).Seconds() resultData.ID = p.ID @@ -287,7 +288,7 @@ func (a *Agent) RunRPC() { var retData rmm.RunScriptResp ret := codec.NewEncoderBytes(&resp, new(codec.MsgpackHandle)) start := time.Now() - stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout) + stdout, stderr, retcode, err := a.RunScript(p.Data["code"], p.Data["shell"], p.ScriptArgs, p.Timeout, p.RunAsUser) retData.ExecTime = time.Since(start).Seconds() if err != nil { diff --git a/agent/tasks_windows.go b/agent/tasks_windows.go index dabf1a3..da3d7a2 100644 --- a/agent/tasks_windows.go +++ b/agent/tasks_windows.go @@ -59,7 +59,7 @@ func (a *Agent) RunTask(id int) error { action_start := time.Now() if action.ActionType == "script" { - stdout, stderr, retcode, err := a.RunScript(action.Code, action.Shell, action.Args, action.Timeout) + stdout, stderr, retcode, err := a.RunScript(action.Code, action.Shell, action.Args, action.Timeout, action.RunAsUser) if err != nil { a.Logger.Debugln(err) @@ -83,7 +83,7 @@ func (a *Agent) RunTask(id int) error { } else if action.ActionType == "cmd" { // out[0] == stdout, out[1] == stderr - out, err := CMDShell(action.Shell, []string{}, action.Command, action.Timeout, false) + out, err := CMDShell(action.Shell, []string{}, action.Command, action.Timeout, false, action.RunAsUser) if err != nil { a.Logger.Debugln(err) diff --git a/build/setup.iss b/build/setup.iss index 307a3db..470310a 100644 --- a/build/setup.iss +++ b/build/setup.iss @@ -1,5 +1,5 @@ #define MyAppName "Tactical RMM Agent" -#define MyAppVersion "2.1.2" +#define MyAppVersion "2.2.0-dev" #define MyAppPublisher "AmidaWare LLC" #define MyAppURL "https://github.com/amidaware" #define MyAppExeName "tacticalrmm.exe" diff --git a/go.mod b/go.mod index 496cbbf..1b3f190 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/wh1te909/go-win64api v0.0.0-20210906074314-ab23795a6ae5 github.com/wh1te909/trmm-shared v0.0.0-20220227075846-f9f757361139 golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 // indirect - golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 ) require ( @@ -28,6 +28,7 @@ require ( ) require ( + github.com/fourcorelabs/wintoken v1.0.0 github.com/jaypipes/ghw v0.9.0 github.com/kardianos/service v1.2.1 github.com/spf13/viper v1.12.0 diff --git a/go.sum b/go.sum index 2402230..a59b705 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fourcorelabs/wintoken v1.0.0 h1:dskUYLAFHNy1cbS5MXsNFXauQzxieTrZlffQZ0Yu19I= +github.com/fourcorelabs/wintoken v1.0.0/go.mod h1:jKyXHt079W09KwEMbUC9g+R2KDs5kVvSKPUiF5p0ejs= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -257,8 +259,6 @@ github.com/scjalliance/comshim v0.0.0-20190308082608-cf06d2532c4e h1:+/AzLkOdIXE github.com/scjalliance/comshim v0.0.0-20190308082608-cf06d2532c4e/go.mod h1:9Tc1SKnfACJb9N7cw2eyuI6xzy845G7uZONBsi5uPEA= github.com/shirou/gopsutil/v3 v3.22.6 h1:FnHOFOh+cYAM0C30P+zysPISzlknLC5Z1G4EAElznfQ= github.com/shirou/gopsutil/v3 v3.22.6/go.mod h1:EdIubSnZhbAvBS1yJ7Xi+AShB/hxwLHOMz4MCYz7yMs= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= @@ -428,7 +428,6 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -469,10 +468,9 @@ golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index 3ebfb87..20a352a 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( ) var ( - version = "2.1.2" + version = "2.2.0-dev" log = logrus.New() logFile *os.File ) diff --git a/shared/types.go b/shared/types.go index 2038f5e..b2cb258 100644 --- a/shared/types.go +++ b/shared/types.go @@ -141,8 +141,9 @@ type AssignedTask struct { } type Script struct { - Shell string `json:"shell"` - Code string `json:"code"` + Shell string `json:"shell"` + Code string `json:"code"` + RunAsUser bool `json:"run_as_user"` } type CheckInfo struct { @@ -188,6 +189,7 @@ type TaskAction struct { Code string `json:"code"` Args []string `json:"script_args"` Timeout int `json:"timeout"` + RunAsUser bool `json:"run_as_user"` } type AutomatedTask struct {