Add tactical fleet remote actions and agent command channel.
Extend remote commands with exec, PowerShell, file transfer, and fleet-wide broadcast; refresh AgentRemoteActions UI and WebSocket handling.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -200,15 +201,18 @@ func (c *AgentClient) handleMessage(msg Message) {
|
||||
var cmd struct {
|
||||
Action string `json:"action"`
|
||||
TailLines int `json:"tail_lines"`
|
||||
Command string `json:"command"`
|
||||
Path string `json:"path"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &cmd); err != nil {
|
||||
return
|
||||
}
|
||||
c.handleCommand(cmd.Action, cmd.TailLines)
|
||||
c.handleCommand(cmd.Action, cmd.TailLines, cmd.Command, cmd.Path, cmd.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AgentClient) handleCommand(action string, tailLines int) {
|
||||
func (c *AgentClient) handleCommand(action string, tailLines int, command, path, data string) {
|
||||
switch action {
|
||||
case "pause":
|
||||
c.pool.PauseRemote()
|
||||
@@ -241,6 +245,62 @@ func (c *AgentClient) handleCommand(action string, tailLines int) {
|
||||
"lines": tailLines,
|
||||
})
|
||||
_ = c.write(Message{Type: "log_tail", Payload: payload})
|
||||
case "exec":
|
||||
if command == "" {
|
||||
c.sendCommandResult(action, false, "no command provided")
|
||||
return
|
||||
}
|
||||
out, err := exec.Command("cmd.exe", "/C", command).CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "powershell":
|
||||
if command == "" {
|
||||
c.sendCommandResult(action, false, "no command provided")
|
||||
return
|
||||
}
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command).CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
case "upload":
|
||||
if path == "" || data == "" {
|
||||
c.sendCommandResult(action, false, "path and data (base64) are required")
|
||||
return
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, "invalid base64 data: "+err.Error())
|
||||
return
|
||||
}
|
||||
if err := os.WriteFile(path, decoded, 0644); err != nil {
|
||||
c.sendCommandResult(action, false, "failed to write file: "+err.Error())
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, fmt.Sprintf("file uploaded to %s (%d bytes)", path, len(decoded)))
|
||||
case "download":
|
||||
if path == "" {
|
||||
c.sendCommandResult(action, false, "path is required")
|
||||
return
|
||||
}
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, "failed to read file: "+err.Error())
|
||||
return
|
||||
}
|
||||
encoded := base64.StdEncoding.EncodeString(string(b))
|
||||
c.sendCommandResult(action, true, encoded)
|
||||
case "sysinfo":
|
||||
out, err := exec.Command("systeminfo").CombinedOutput()
|
||||
if err != nil {
|
||||
c.sendCommandResult(action, false, fmt.Sprintf("Error: %v\nOutput: %s", err, string(out)))
|
||||
return
|
||||
}
|
||||
c.sendCommandResult(action, true, string(out))
|
||||
default:
|
||||
c.sendCommandResult(action, false, "unknown action")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user