Commit bda978f
Changed files (4)
cmd/callbacks.go
@@ -5,12 +5,25 @@ import (
"fmt"
"mysh/pkg/mythic"
"os"
+ "sort"
+ "strings"
"text/tabwriter"
"time"
"github.com/spf13/cobra"
)
+var (
+ sortByID bool
+ sortByType bool
+ sortByHost bool
+ sortByUser bool
+ sortByProcess bool
+ sortByLast bool
+ sortByDescription bool
+ sortReverse bool
+)
+
var callbacksCmd = &cobra.Command{
Use: "callbacks",
Aliases: []string{"cb", "callback"},
@@ -21,6 +34,18 @@ var callbacksCmd = &cobra.Command{
func init() {
rootCmd.AddCommand(callbacksCmd)
+
+ // Sort flags - mutually exclusive
+ callbacksCmd.Flags().BoolVar(&sortByID, "id", false, "Sort by callback ID (default)")
+ callbacksCmd.Flags().BoolVar(&sortByType, "type", false, "Sort by agent type")
+ callbacksCmd.Flags().BoolVar(&sortByHost, "host", false, "Sort by hostname")
+ callbacksCmd.Flags().BoolVar(&sortByUser, "user", false, "Sort by username")
+ callbacksCmd.Flags().BoolVar(&sortByProcess, "process", false, "Sort by process name")
+ callbacksCmd.Flags().BoolVar(&sortByLast, "last", false, "Sort by last checkin time")
+ callbacksCmd.Flags().BoolVar(&sortByDescription, "description", false, "Sort by description")
+
+ // Reverse sort flag
+ callbacksCmd.Flags().BoolVar(&sortReverse, "rev", false, "Reverse sort order")
}
// formatTimeSince formats a timestamp into a human-readable "time ago" format
@@ -79,6 +104,75 @@ func formatTimeSince(timestampStr string) string {
}
}
+// getSortField determines which field to sort by, returns "id" as default
+func getSortField() string {
+ if sortByType {
+ return "type"
+ }
+ if sortByHost {
+ return "host"
+ }
+ if sortByUser {
+ return "user"
+ }
+ if sortByProcess {
+ return "process"
+ }
+ if sortByLast {
+ return "last"
+ }
+ if sortByDescription {
+ return "description"
+ }
+ // Default to ID sort (newest/highest last)
+ return "id"
+}
+
+// sortCallbacks sorts callbacks based on the specified field
+func sortCallbacks(callbacks []mythic.Callback, sortField string, reverse bool) {
+ sort.Slice(callbacks, func(i, j int) bool {
+ var result bool
+
+ switch sortField {
+ case "type":
+ typeI := callbacks[i].Payload.PayloadType.Name
+ typeJ := callbacks[j].Payload.PayloadType.Name
+ if typeI == "" {
+ typeI = "unknown"
+ }
+ if typeJ == "" {
+ typeJ = "unknown"
+ }
+ result = strings.ToLower(typeI) < strings.ToLower(typeJ)
+ case "host":
+ result = strings.ToLower(callbacks[i].Host) < strings.ToLower(callbacks[j].Host)
+ case "user":
+ result = strings.ToLower(callbacks[i].User) < strings.ToLower(callbacks[j].User)
+ case "process":
+ result = strings.ToLower(callbacks[i].ProcessName) < strings.ToLower(callbacks[j].ProcessName)
+ case "last":
+ // Parse timestamps for comparison
+ timeI, errI := time.Parse(time.RFC3339, callbacks[i].LastCheckin)
+ timeJ, errJ := time.Parse(time.RFC3339, callbacks[j].LastCheckin)
+ if errI != nil || errJ != nil {
+ // Fallback to string comparison if parsing fails
+ result = callbacks[i].LastCheckin < callbacks[j].LastCheckin
+ } else {
+ result = timeI.Before(timeJ)
+ }
+ case "description":
+ result = strings.ToLower(callbacks[i].Description) < strings.ToLower(callbacks[j].Description)
+ default: // "id"
+ result = callbacks[i].DisplayID < callbacks[j].DisplayID
+ }
+
+ if reverse {
+ return !result
+ }
+ return result
+ })
+}
+
func runCallbacks(cmd *cobra.Command, args []string) error {
if err := validateConfig(); err != nil {
return err
@@ -98,6 +192,17 @@ func runCallbacks(cmd *cobra.Command, args []string) error {
return nil
}
+ // Sort callbacks based on flags
+ sortField := getSortField()
+ // For ID sort, default to newest (highest) last, so reverse=true by default
+ // For other sorts, use the --rev flag
+ shouldReverse := sortReverse
+ if sortField == "id" && !sortReverse {
+ shouldReverse = true // Default for ID is newest last (reverse order)
+ }
+
+ sortCallbacks(callbacks, sortField, shouldReverse)
+
// Create a tab writer for formatted output
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "ID\tTYPE\tHOST\tUSER\tPROCESS\tPID\tLAST CHECKIN\tDESCRIPTION")
mysh.gif
Binary file
README.md
@@ -1,5 +1,7 @@
# mysh
+
+
`mysh` (mɪ-ʃh) is a command-line interface for Mythic C2 servers.
## Installation
TODO.md
@@ -6,7 +6,9 @@
- ✅ task [callback] arg make it accept lists, comma seperated, or ranges e.g. 1,2,3-5,10
- ✅ make cb and callback aliases for callbacks
- ✅ update callbacks to show last-checkin as 0s or 0m00s ago
-- make tv --raw have short -r
+- ✅ make tv --raw have short -r
+- ✅ make tv have more task details in the header
+- ✅ make tv have a -d --details to only show the header
- make callbacks sortable with (--rev reverse) --id, --type, --host, --user, --process, --last, --description all exclusive - default should be --id with newest (highest) last
- create a .cache/mysh/ to store task result cached values for completed tasks - use whatever the right XDG env default dir should be
- invert cobra-cli nto functions (no global vars)