main
Raw Download raw file
  1package cmd
  2
  3import (
  4	"bufio"
  5	"context"
  6	"fmt"
  7	"mysh/pkg/mythic"
  8	"mysh/pkg/mythic/api"
  9	"os"
 10	"strconv"
 11	"strings"
 12
 13	"github.com/spf13/cobra"
 14)
 15
 16var interactCmd = &cobra.Command{
 17	Use:   "interact <callback_id>",
 18	Short: "Interact with a specific callback",
 19	Long:  "Start an interactive session with a callback, equivalent to right-clicking and selecting 'Interact' in the UI.",
 20	Args:  cobra.ExactArgs(1),
 21	RunE:  runInteract,
 22}
 23
 24func init() {
 25	rootCmd.AddCommand(interactCmd)
 26}
 27
 28func runInteract(cmd *cobra.Command, args []string) error {
 29	if err := validateConfig(); err != nil {
 30		return err
 31	}
 32
 33	callbackID, err := strconv.Atoi(args[0])
 34	if err != nil {
 35		return fmt.Errorf("invalid callback ID: %s", args[0])
 36	}
 37
 38	client := mythic.NewClient(mythicURL, token, insecure, socksProxy)
 39	ctx := context.Background()
 40
 41	// Verify callback exists and is active
 42	targetCallback, err := api.FindActiveCallback(ctx, client, callbackID)
 43	if err != nil {
 44		return err
 45	}
 46
 47	agentType := targetCallback.Payload.PayloadType.Name
 48	if agentType == "" {
 49		agentType = "agent" // fallback if payload type is not available
 50	}
 51
 52	fmt.Printf("Interacting with callback %d (%s@%s - %s [%s])\n",
 53		targetCallback.DisplayID,
 54		targetCallback.User,
 55		targetCallback.Host,
 56		targetCallback.ProcessName,
 57		agentType)
 58	fmt.Println("Type 'exit' to quit the interactive session")
 59	fmt.Println("---")
 60
 61	scanner := bufio.NewScanner(os.Stdin)
 62	for {
 63		fmt.Printf("%s[%d]> ", agentType, callbackID)
 64		if !scanner.Scan() {
 65			break
 66		}
 67
 68		input := strings.TrimSpace(scanner.Text())
 69		if input == "" {
 70			continue
 71		}
 72
 73		if input == "exit" {
 74			fmt.Println("Exiting interactive session")
 75			break
 76		}
 77
 78		if err := executeCommand(ctx, client, targetCallback.ID, input); err != nil {
 79			fmt.Printf("Error: %v\n", err)
 80		}
 81	}
 82
 83	return scanner.Err()
 84}
 85
 86func executeCommand(ctx context.Context, client *mythic.Client, callbackID int, input string) error {
 87	parts := strings.Fields(input)
 88	if len(parts) == 0 {
 89		return nil
 90	}
 91
 92	command := parts[0]
 93	params := ""
 94	if len(parts) > 1 {
 95		params = strings.Join(parts[1:], " ")
 96	}
 97
 98	// Configure polling for interactive mode
 99	config := api.TaskPollConfig{
100		TimeoutSeconds: int(api.DefaultTaskTimeout.Seconds()),
101		PollInterval:   api.DefaultPollInterval,
102		ShowProgress:   true,
103		RawOutput:      false,
104	}
105
106	// Execute task and wait for response
107	updatedTask, err := api.ExecuteTaskAndWait(ctx, client, callbackID, command, params, config)
108	if err != nil {
109		fmt.Printf("\n%v\n", err)
110		return nil
111	}
112
113	if updatedTask.Response != "" {
114		fmt.Printf("\nResponse:\n%s\n", updatedTask.Response)
115	} else {
116		fmt.Printf("\nTask completed with status: %s\n", updatedTask.Status)
117	}
118	return nil
119}