Commit f17d5bd
Changed files (26)
cmd
pkg
cmd/cache.go
@@ -127,4 +127,4 @@ func formatBytes(bytes int64) string {
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
-}
\ No newline at end of file
+}
cmd/callbacks.go
@@ -222,12 +222,12 @@ func runCallbacks(cmd *cobra.Command, args []string) error {
// Define columns with priorities (1=highest, higher numbers can be dropped)
columns := []ColumnInfo{
- {Header: "ID", Number: 1, MinWidth: 3, MaxWidth: 6, Priority: 1}, // Always show
- {Header: "TYPE", Number: 2, MinWidth: 6, MaxWidth: 10, Priority: 3}, // High priority
- {Header: "HOST", Number: 3, MinWidth: 8, MaxWidth: 16, Priority: 2}, // Very important
- {Header: "USER", Number: 4, MinWidth: 6, MaxWidth: 12, Priority: 4}, // Important
- {Header: "PROCESS", Number: 5, MinWidth: 8, MaxWidth: 12, Priority: 6}, // Can drop
- {Header: "PID", Number: 6, MinWidth: 3, MaxWidth: 6, Priority: 7}, // Can drop
+ {Header: "ID", Number: 1, MinWidth: 3, MaxWidth: 6, Priority: 1}, // Always show
+ {Header: "TYPE", Number: 2, MinWidth: 6, MaxWidth: 10, Priority: 3}, // High priority
+ {Header: "HOST", Number: 3, MinWidth: 8, MaxWidth: 16, Priority: 2}, // Very important
+ {Header: "USER", Number: 4, MinWidth: 6, MaxWidth: 12, Priority: 4}, // Important
+ {Header: "PROCESS", Number: 5, MinWidth: 8, MaxWidth: 12, Priority: 6}, // Can drop
+ {Header: "PID", Number: 6, MinWidth: 3, MaxWidth: 6, Priority: 7}, // Can drop
{Header: "LAST CHECKIN", Number: 7, MinWidth: 8, MaxWidth: 10, Priority: 5},
{Header: "DESCRIPTION", Number: 8, MinWidth: 10, MaxWidth: 25, Priority: 8}, // First to drop
}
@@ -261,4 +261,4 @@ func runCallbacks(cmd *cobra.Command, args []string) error {
t.Render()
return nil
-}
\ No newline at end of file
+}
cmd/files.go
@@ -32,7 +32,6 @@ var filesCmd = &cobra.Command{
RunE: runFilesList, // Default to list command
}
-
var filesDownloadCmd = &cobra.Command{
Use: "download <file_uuid_or_task_id>",
Short: "Download a file by UUID or task ID",
@@ -127,15 +126,15 @@ func runFilesList(cmd *cobra.Command, args []string) error {
// Define columns with priorities for responsive display
columns := []ColumnInfo{
- {Header: "UUID", Number: 1, MinWidth: 8, MaxWidth: 36, Priority: 5}, // Can drop on small screens
- {Header: "TYPE", Number: 2, MinWidth: 6, MaxWidth: 10, Priority: 3}, // Important
- {Header: "FILENAME", Number: 3, MinWidth: 10, MaxWidth: 30, Priority: 1}, // Never drop - most important
+ {Header: "UUID", Number: 1, MinWidth: 8, MaxWidth: 36, Priority: 5}, // Can drop on small screens
+ {Header: "TYPE", Number: 2, MinWidth: 6, MaxWidth: 10, Priority: 3}, // Important
+ {Header: "FILENAME", Number: 3, MinWidth: 10, MaxWidth: 30, Priority: 1}, // Never drop - most important
{Header: "REMOTE PATH", Number: 4, MinWidth: 12, MaxWidth: 40, Priority: 6}, // First to drop
- {Header: "HOST", Number: 5, MinWidth: 8, MaxWidth: 20, Priority: 4}, // Medium priority
- {Header: "SIZE", Number: 6, MinWidth: 6, MaxWidth: 10, Priority: 3}, // Important
- {Header: "COMPLETE", Number: 7, MinWidth: 6, MaxWidth: 8, Priority: 2}, // High priority
- {Header: "TASK", Number: 8, MinWidth: 4, MaxWidth: 8, Priority: 5}, // Can drop
- {Header: "TIMESTAMP", Number: 9, MinWidth: 12, MaxWidth: 16, Priority: 7}, // First to drop
+ {Header: "HOST", Number: 5, MinWidth: 8, MaxWidth: 20, Priority: 4}, // Medium priority
+ {Header: "SIZE", Number: 6, MinWidth: 6, MaxWidth: 10, Priority: 3}, // Important
+ {Header: "COMPLETE", Number: 7, MinWidth: 6, MaxWidth: 8, Priority: 2}, // High priority
+ {Header: "TASK", Number: 8, MinWidth: 4, MaxWidth: 8, Priority: 5}, // Can drop
+ {Header: "TIMESTAMP", Number: 9, MinWidth: 12, MaxWidth: 16, Priority: 7}, // First to drop
}
// Configure table for current terminal width
@@ -362,4 +361,4 @@ func extractUUIDFromTaskResponse(response string) []string {
}
return uuids
-}
\ No newline at end of file
+}
cmd/grep.go
@@ -59,7 +59,6 @@ func init() {
grepCmd.Flags().BoolVar(&grepRaw, "raw", false, "Display raw output with grep -R style single-line file delimiters")
}
-
func runGrep(cmd *cobra.Command, args []string) error {
if err := validateConfig(); err != nil {
return err
@@ -244,7 +243,6 @@ func runGrep(cmd *cobra.Command, args []string) error {
return nil
}
-
func showRawMatches(matches []rawMatch) {
for i, match := range matches {
// Format like tail -f: ==> filename <==
@@ -286,11 +284,11 @@ func showMatchingTasksTable(tasks []mythic.Task) {
// Define columns with priorities (1=highest, higher numbers can be dropped)
columns := []ColumnInfo{
- {Header: "TASK ID", Number: 1, MinWidth: 6, MaxWidth: 8, Priority: 1}, // Always show
- {Header: "COMMAND", Number: 2, MinWidth: 8, MaxWidth: 12, Priority: 2}, // High priority
- {Header: "PARAMS", Number: 3, MinWidth: 10, MaxWidth: 30, Priority: 5}, // Can drop
- {Header: "STATUS", Number: 4, MinWidth: 6, MaxWidth: 10, Priority: 3}, // Important
- {Header: "CALLBACK", Number: 5, MinWidth: 6, MaxWidth: 8, Priority: 4}, // Important
+ {Header: "TASK ID", Number: 1, MinWidth: 6, MaxWidth: 8, Priority: 1}, // Always show
+ {Header: "COMMAND", Number: 2, MinWidth: 8, MaxWidth: 12, Priority: 2}, // High priority
+ {Header: "PARAMS", Number: 3, MinWidth: 10, MaxWidth: 30, Priority: 5}, // Can drop
+ {Header: "STATUS", Number: 4, MinWidth: 6, MaxWidth: 10, Priority: 3}, // Important
+ {Header: "CALLBACK", Number: 5, MinWidth: 6, MaxWidth: 8, Priority: 4}, // Important
{Header: "TIMESTAMP", Number: 6, MinWidth: 10, MaxWidth: 14, Priority: 6}, // First to drop
}
@@ -462,4 +460,4 @@ func getCachedTasks(taskCache *cache.TaskCache) []mythic.Task {
}
return tasks
-}
\ No newline at end of file
+}
cmd/grep_test.go
@@ -141,4 +141,4 @@ func TestGrepCommandStringConstruction(t *testing.T) {
}
})
}
-}
\ No newline at end of file
+}
cmd/interact.go
@@ -49,10 +49,10 @@ func runInteract(cmd *cobra.Command, args []string) error {
agentType = "agent" // fallback if payload type is not available
}
- fmt.Printf("Interacting with callback %d (%s@%s - %s [%s])\n",
- targetCallback.DisplayID,
- targetCallback.User,
- targetCallback.Host,
+ fmt.Printf("Interacting with callback %d (%s@%s - %s [%s])\n",
+ targetCallback.DisplayID,
+ targetCallback.User,
+ targetCallback.Host,
targetCallback.ProcessName,
agentType)
fmt.Println("Type 'exit' to quit the interactive session")
@@ -116,4 +116,4 @@ func executeCommand(ctx context.Context, client *mythic.Client, callbackID int,
fmt.Printf("\nTask completed with status: %s\n", updatedTask.Status)
}
return nil
-}
\ No newline at end of file
+}
cmd/payload.go
@@ -80,7 +80,7 @@ func showPayloadList(ctx context.Context, client *mythic.Client) error {
// Define columns with priorities (1=highest, higher numbers can be dropped)
columns := []ColumnInfo{
- {Header: "ID", Number: 1, MinWidth: 3, MaxWidth: 6, Priority: 1}, // Always show
+ {Header: "ID", Number: 1, MinWidth: 3, MaxWidth: 6, Priority: 1}, // Always show
{Header: "TYPE", Number: 2, MinWidth: 6, MaxWidth: 10, Priority: 2}, // High priority
{Header: "DESCRIPTION", Number: 3, MinWidth: 10, MaxWidth: 20, Priority: 5}, // Can drop
{Header: "FILENAME", Number: 4, MinWidth: 8, MaxWidth: 16, Priority: 3}, // Important
@@ -244,4 +244,4 @@ func getValueOrDefault(value, defaultValue string) string {
return defaultValue
}
return value
-}
\ No newline at end of file
+}
cmd/root.go
@@ -115,7 +115,7 @@ func configureTableForTerminal(t table.Writer, columns []ColumnInfo) *DynamicTab
for i, col := range visibleColumns {
headers = append(headers, col.Header)
configs = append(configs, table.ColumnConfig{
- Number: i + 1, // Use sequential numbers for visible columns
+ Number: i + 1, // Use sequential numbers for visible columns
WidthMin: columnWidths[i], // Force minimum width
WidthMax: columnWidths[i], // Force maximum width
WidthMaxEnforcer: truncateWithEllipsis,
@@ -260,4 +260,3 @@ func truncateWithEllipsis(col string, maxLen int) string {
}
return col[:maxLen-3] + "..."
}
-
cmd/task.go
@@ -229,4 +229,4 @@ func runTask(cmd *cobra.Command, args []string) error {
}
return nil
-}
\ No newline at end of file
+}
cmd/task_list.go
@@ -111,20 +111,20 @@ func runTaskList(cmd *cobra.Command, args []string) error {
if showingAllCallbacks {
// Define columns with priorities for all callbacks view
columns = []ColumnInfo{
- {Header: "TASK ID", Number: 1, MinWidth: 6, MaxWidth: 8, Priority: 1}, // Always show
- {Header: "CALLBACK", Number: 2, MinWidth: 6, MaxWidth: 8, Priority: 2}, // High priority
- {Header: "COMMAND", Number: 3, MinWidth: 8, MaxWidth: 15, Priority: 3}, // Important
- {Header: "PARAMS", Number: 4, MinWidth: 10, MaxWidth: 40, Priority: 5}, // Can drop
- {Header: "STATUS", Number: 5, MinWidth: 8, MaxWidth: 12, Priority: 4}, // Important
+ {Header: "TASK ID", Number: 1, MinWidth: 6, MaxWidth: 8, Priority: 1}, // Always show
+ {Header: "CALLBACK", Number: 2, MinWidth: 6, MaxWidth: 8, Priority: 2}, // High priority
+ {Header: "COMMAND", Number: 3, MinWidth: 8, MaxWidth: 15, Priority: 3}, // Important
+ {Header: "PARAMS", Number: 4, MinWidth: 10, MaxWidth: 40, Priority: 5}, // Can drop
+ {Header: "STATUS", Number: 5, MinWidth: 8, MaxWidth: 12, Priority: 4}, // Important
{Header: "TIMESTAMP", Number: 6, MinWidth: 12, MaxWidth: 19, Priority: 6}, // First to drop
}
} else {
// Define columns with priorities for single callback view
columns = []ColumnInfo{
- {Header: "TASK ID", Number: 1, MinWidth: 6, MaxWidth: 8, Priority: 1}, // Always show
- {Header: "COMMAND", Number: 2, MinWidth: 8, MaxWidth: 15, Priority: 2}, // High priority
- {Header: "PARAMS", Number: 3, MinWidth: 10, MaxWidth: 50, Priority: 4}, // Can drop
- {Header: "STATUS", Number: 4, MinWidth: 8, MaxWidth: 12, Priority: 3}, // Important
+ {Header: "TASK ID", Number: 1, MinWidth: 6, MaxWidth: 8, Priority: 1}, // Always show
+ {Header: "COMMAND", Number: 2, MinWidth: 8, MaxWidth: 15, Priority: 2}, // High priority
+ {Header: "PARAMS", Number: 3, MinWidth: 10, MaxWidth: 50, Priority: 4}, // Can drop
+ {Header: "STATUS", Number: 4, MinWidth: 8, MaxWidth: 12, Priority: 3}, // Important
{Header: "TIMESTAMP", Number: 5, MinWidth: 12, MaxWidth: 19, Priority: 5}, // First to drop
}
}
@@ -172,4 +172,4 @@ func runTaskList(cmd *cobra.Command, args []string) error {
t.Render()
return nil
-}
\ No newline at end of file
+}
cmd/task_view.go
@@ -150,4 +150,4 @@ func runTaskView(cmd *cobra.Command, args []string) error {
}
return nil
-}
\ No newline at end of file
+}
cmd/upload.go
@@ -173,4 +173,4 @@ func runUpload(cmd *cobra.Command, args []string) error {
}
return nil
-}
\ No newline at end of file
+}
pkg/cache/cache.go
@@ -82,7 +82,6 @@ type CachedTask struct {
ServerURL string `json:"server_url"`
}
-
// generateCacheKey creates a unique cache key for a task
func (tc *TaskCache) generateCacheKey(taskID int, serverURL string) string {
// Create a hash based on task ID and server URL to ensure uniqueness
@@ -91,7 +90,6 @@ func (tc *TaskCache) generateCacheKey(taskID int, serverURL string) string {
return fmt.Sprintf("task_%d_%x.json", taskID, h.Sum(nil)[:8])
}
-
// GetCachedTask retrieves a cached task if it exists and is for a completed task
func (tc *TaskCache) GetCachedTask(taskID int, serverURL string) (*mythic.Task, bool) {
cacheKey := tc.generateCacheKey(taskID, serverURL)
@@ -238,4 +236,3 @@ func (tc *TaskCache) ClearCache() error {
return nil
}
-
pkg/mythic/api/callbacks.go
@@ -25,4 +25,4 @@ func FindActiveCallback(ctx context.Context, client MythicClient, callbackID int
// ValidateCallbackExists verifies that a callback exists and is accessible
func ValidateCallbackExists(ctx context.Context, client MythicClient, callbackID int) (*mythic.Callback, error) {
return FindActiveCallback(ctx, client, callbackID)
-}
\ No newline at end of file
+}
pkg/mythic/api/callbacks_test.go
@@ -37,11 +37,11 @@ func (m *MockClient) GetAllTasksWithResponses(ctx context.Context, limit int) ([
func TestFindActiveCallback(t *testing.T) {
tests := []struct {
- name string
- callbacks []mythic.Callback
- callbackID int
- expectErr bool
- expectedID int
+ name string
+ callbacks []mythic.Callback
+ callbackID int
+ expectErr bool
+ expectedID int
}{
{
name: "callback found",
@@ -121,4 +121,4 @@ func TestValidateCallbackExists(t *testing.T) {
if result != nil {
t.Error("Expected nil result for non-existent callback")
}
-}
\ No newline at end of file
+}
pkg/mythic/api/constants.go
@@ -28,7 +28,7 @@ const (
// UUID format constants
const (
- UUIDLength = 36
+ UUIDLength = 36
UUIDLengthNoHyphens = 32
- UUIDDisplayLength = 8 // Number of characters to show in truncated displays
-)
\ No newline at end of file
+ UUIDDisplayLength = 8 // Number of characters to show in truncated displays
+)
pkg/mythic/api/constants_test.go
@@ -44,4 +44,4 @@ func TestConstants(t *testing.T) {
if UUIDDisplayLength != 8 {
t.Errorf("Expected UUIDDisplayLength to be 8, got %d", UUIDDisplayLength)
}
-}
\ No newline at end of file
+}
pkg/mythic/api/interfaces.go
@@ -15,4 +15,4 @@ type MythicClient interface {
}
// Ensure that the real Client implements the interface
-var _ MythicClient = (*mythic.Client)(nil)
\ No newline at end of file
+var _ MythicClient = (*mythic.Client)(nil)
pkg/mythic/api/tasks.go
@@ -62,4 +62,4 @@ func ExecuteTaskAndWait(ctx context.Context, client MythicClient, callbackID int
// Poll for response
return PollTaskResponse(ctx, client, task.ID, config)
-}
\ No newline at end of file
+}
pkg/mythic/api/tasks_test.go
@@ -169,4 +169,4 @@ func TestExecuteTaskAndWait_Timeout(t *testing.T) {
if result != nil {
t.Error("Expected nil result on timeout")
}
-}
\ No newline at end of file
+}
pkg/mythic/client.go
@@ -43,16 +43,16 @@ type Callback struct {
}
type Task struct {
- ID int `json:"id"`
- DisplayID int `json:"display_id"`
- Command string `json:"command"`
- Params string `json:"params"`
- Status string `json:"status"`
- Response string `json:"response,omitempty"`
- CallbackID int `json:"callback_id"`
- OperatorID int `json:"operator_id"`
- Timestamp string `json:"timestamp,omitempty"`
- Completed bool `json:"completed"`
+ ID int `json:"id"`
+ DisplayID int `json:"display_id"`
+ Command string `json:"command"`
+ Params string `json:"params"`
+ Status string `json:"status"`
+ Response string `json:"response,omitempty"`
+ CallbackID int `json:"callback_id"`
+ OperatorID int `json:"operator_id"`
+ Timestamp string `json:"timestamp,omitempty"`
+ Completed bool `json:"completed"`
}
type File struct {
@@ -97,9 +97,9 @@ type Payload struct {
PayloadTypeName string `json:"payloadtype_name"`
// File info
- FileID int `json:"file_id"`
- Filename string `json:"filename"`
- AgentFileID string `json:"agent_file_id"`
+ FileID int `json:"file_id"`
+ Filename string `json:"filename"`
+ AgentFileID string `json:"agent_file_id"`
// C2 Profiles
C2Profiles []string `json:"c2_profiles"`
@@ -144,30 +144,30 @@ func isBase64(s string) bool {
if len(s) < 4 {
return false
}
-
+
// Check if it matches base64 pattern
base64Regex := regexp.MustCompile(`^[A-Za-z0-9+/]*={0,2}$`)
if !base64Regex.MatchString(s) {
return false
}
-
+
// Try to decode
decoded, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return false
}
-
+
// If decoded content is not valid UTF-8, it's likely binary data
if !utf8.Valid(decoded) {
return true
}
-
- // If it's valid UTF-8 but the original string looks like base64
+
+ // If it's valid UTF-8 but the original string looks like base64
// and is significantly longer than the decoded version, it's likely base64
if float64(len(s)) > float64(len(decoded))*1.2 {
return true
}
-
+
return false
}
@@ -182,24 +182,24 @@ func decodeResponseText(responseText string) string {
// If it's binary data, return a placeholder message
return fmt.Sprintf("[Binary data: %d bytes]", len(decoded))
}
-
+
// If base64 decode fails, return original text
return responseText
}
func (c *Client) GetCallbacks(ctx context.Context) ([]Callback, error) {
req := graphql.NewRequest(GetCallbacks)
-
+
req.Header.Set("apitoken", c.token)
-
+
var resp struct {
Callback []Callback `json:"callback"`
}
-
+
if err := c.client.Run(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("failed to get callbacks: %w", err)
}
-
+
return resp.Callback, nil
}
@@ -208,26 +208,26 @@ func (c *Client) GetActiveCallbacks(ctx context.Context) ([]Callback, error) {
if err != nil {
return nil, err
}
-
+
var active []Callback
for _, callback := range callbacks {
if callback.Active {
active = append(active, callback)
}
}
-
+
return active, nil
}
func (c *Client) CreateTask(ctx context.Context, callbackID int, command, params string) (*Task, error) {
req := graphql.NewRequest(CreateTask)
-
+
req.Var("callback_id", callbackID)
req.Var("command", command)
req.Var("params", params)
req.Var("tasking_location", "command_line")
req.Header.Set("apitoken", c.token)
-
+
var resp struct {
CreateTask struct {
Status string `json:"status"`
@@ -236,68 +236,68 @@ func (c *Client) CreateTask(ctx context.Context, callbackID int, command, params
Error string `json:"error"`
} `json:"createTask"`
}
-
+
if err := c.client.Run(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("failed to create task: %w", err)
}
-
+
if resp.CreateTask.Status != "success" {
return nil, fmt.Errorf("task creation failed: %s", resp.CreateTask.Error)
}
-
+
return &Task{
- ID: resp.CreateTask.ID,
- DisplayID: resp.CreateTask.DisplayID,
- Command: command,
- Params: params,
- Status: "submitted",
+ ID: resp.CreateTask.ID,
+ DisplayID: resp.CreateTask.DisplayID,
+ Command: command,
+ Params: params,
+ Status: "submitted",
CallbackID: callbackID,
}, nil
}
func (c *Client) GetTaskResponse(ctx context.Context, taskID int) (*Task, error) {
req := graphql.NewRequest(GetTask)
-
+
req.Var("id", taskID)
req.Header.Set("apitoken", c.token)
-
+
var resp struct {
Task struct {
- ID int `json:"id"`
- DisplayID int `json:"display_id"`
- CommandName string `json:"command_name"`
+ ID int `json:"id"`
+ DisplayID int `json:"display_id"`
+ CommandName string `json:"command_name"`
OriginalParams string `json:"original_params"`
DisplayParams string `json:"display_params"`
- Status string `json:"status"`
- Completed bool `json:"completed"`
- Callback struct {
+ Status string `json:"status"`
+ Completed bool `json:"completed"`
+ Callback struct {
ID int `json:"id"`
DisplayID int `json:"display_id"`
} `json:"callback"`
} `json:"task_by_pk"`
}
-
+
if err := c.client.Run(ctx, req, &resp); err != nil {
return nil, fmt.Errorf("failed to get task: %w", err)
}
-
+
// Get task responses
responseReq := graphql.NewRequest(GetTaskResponses)
-
+
responseReq.Var("task_display_id", resp.Task.DisplayID)
responseReq.Header.Set("apitoken", c.token)
-
+
var responseResp struct {
Response []struct {
ResponseText string `json:"response_text"`
Timestamp string `json:"timestamp"`
} `json:"response"`
}
-
+
if err := c.client.Run(ctx, responseReq, &responseResp); err != nil {
return nil, fmt.Errorf("failed to get task response: %w", err)
}
-
+
// Concatenate all response chunks in chronological order
var responseBuilder strings.Builder
for _, resp := range responseResp.Response {
@@ -305,12 +305,12 @@ func (c *Client) GetTaskResponse(ctx context.Context, taskID int) (*Task, error)
responseBuilder.WriteString(decodedText)
}
response := responseBuilder.String()
-
+
status := resp.Task.Status
if resp.Task.Completed {
status = "completed"
}
-
+
return &Task{
ID: resp.Task.ID,
DisplayID: resp.Task.DisplayID,
@@ -399,15 +399,15 @@ func (c *Client) GetCallbackTasks(ctx context.Context, callbackID int) ([]Task,
var resp struct {
Task []struct {
- ID int `json:"id"`
- DisplayID int `json:"display_id"`
- CommandName string `json:"command_name"`
+ ID int `json:"id"`
+ DisplayID int `json:"display_id"`
+ CommandName string `json:"command_name"`
OriginalParams string `json:"original_params"`
DisplayParams string `json:"display_params"`
- Status string `json:"status"`
- Completed bool `json:"completed"`
- Timestamp string `json:"timestamp"`
- Callback struct {
+ Status string `json:"status"`
+ Completed bool `json:"completed"`
+ Timestamp string `json:"timestamp"`
+ Callback struct {
ID int `json:"id"`
DisplayID int `json:"display_id"`
} `json:"callback"`
@@ -615,15 +615,15 @@ func (c *Client) GetTasksWithResponses(ctx context.Context, callbackID int, limi
var resp struct {
Task []struct {
- ID int `json:"id"`
- DisplayID int `json:"display_id"`
- CommandName string `json:"command_name"`
+ ID int `json:"id"`
+ DisplayID int `json:"display_id"`
+ CommandName string `json:"command_name"`
OriginalParams string `json:"original_params"`
DisplayParams string `json:"display_params"`
- Status string `json:"status"`
- Completed bool `json:"completed"`
- Timestamp string `json:"timestamp"`
- Callback struct {
+ Status string `json:"status"`
+ Completed bool `json:"completed"`
+ Timestamp string `json:"timestamp"`
+ Callback struct {
ID int `json:"id"`
DisplayID int `json:"display_id"`
Host string `json:"host"`
@@ -681,15 +681,15 @@ func (c *Client) GetAllTasks(ctx context.Context, limit int) ([]Task, error) {
var resp struct {
Task []struct {
- ID int `json:"id"`
- DisplayID int `json:"display_id"`
- CommandName string `json:"command_name"`
+ ID int `json:"id"`
+ DisplayID int `json:"display_id"`
+ CommandName string `json:"command_name"`
OriginalParams string `json:"original_params"`
DisplayParams string `json:"display_params"`
- Status string `json:"status"`
- Completed bool `json:"completed"`
- Timestamp string `json:"timestamp"`
- Callback struct {
+ Status string `json:"status"`
+ Completed bool `json:"completed"`
+ Timestamp string `json:"timestamp"`
+ Callback struct {
ID int `json:"id"`
DisplayID int `json:"display_id"`
Host string `json:"host"`
@@ -737,15 +737,15 @@ func (c *Client) GetAllTasksWithResponses(ctx context.Context, limit int) ([]Tas
var resp struct {
Task []struct {
- ID int `json:"id"`
- DisplayID int `json:"display_id"`
- CommandName string `json:"command_name"`
+ ID int `json:"id"`
+ DisplayID int `json:"display_id"`
+ CommandName string `json:"command_name"`
OriginalParams string `json:"original_params"`
DisplayParams string `json:"display_params"`
- Status string `json:"status"`
- Completed bool `json:"completed"`
- Timestamp string `json:"timestamp"`
- Callback struct {
+ Status string `json:"status"`
+ Completed bool `json:"completed"`
+ Timestamp string `json:"timestamp"`
+ Callback struct {
ID int `json:"id"`
DisplayID int `json:"display_id"`
Host string `json:"host"`
@@ -869,4 +869,4 @@ func (c *Client) GetPayloads(ctx context.Context) ([]Payload, error) {
}
return payloads, nil
-}
\ No newline at end of file
+}
pkg/mythic/client_test.go
@@ -19,7 +19,7 @@ func TestIsBase64(t *testing.T) {
{
name: "valid base64 text that decodes to text",
input: "SGVsbG8gV29ybGQ=", // "Hello World" in base64
- expected: true, // Actually returns true because the original is longer than decoded
+ expected: true, // Actually returns true because the original is longer than decoded
},
{
name: "short string",
@@ -39,7 +39,7 @@ func TestIsBase64(t *testing.T) {
{
name: "valid base64 but short",
input: "dGVzdA==", // "test" in base64
- expected: true, // Actually returns true because original is longer than decoded
+ expected: true, // Actually returns true because original is longer than decoded
},
}
@@ -156,4 +156,4 @@ func TestClientMethodsExist(t *testing.T) {
if err == nil {
t.Error("Expected error due to canceled context")
}
-}
\ No newline at end of file
+}
pkg/mythic/queries.go
@@ -616,4 +616,4 @@ query GetAllTasksWithResponsesFromAllCallbacks($limit: Int) {
timestamp
}
}
-}`
\ No newline at end of file
+}`
main.go
@@ -4,4 +4,4 @@ import "mysh/cmd"
func main() {
cmd.Execute()
-}
\ No newline at end of file
+}
README.md
@@ -107,6 +107,13 @@ mysh interact 1
## Project Structure
+### contrib Directory
+The `contrib/` directory contains checkouts of related Mythic repositories for reference during development:
+- `contrib/Mythic/` - Main Mythic server source code
+- `contrib/Mythic_Scripting/` - Python scripting library for Mythic
+- Located in this repository for easy reference when implementing GraphQL queries and mutations
+- Check `contrib/Mythic/hasura-docker/metadata/metadata/actions.graphql` for available GraphQL operations
+
### GraphQL Queries Synchronization
The project maintains a 1:1 copy of GraphQL queries from the Python Mythic_Scripting library:
- **Source:** `contrib/Mythic_Scripting/mythic/graphql_queries.py`
TODO.md
@@ -1,15 +1,11 @@
- grep --delete - flag that deletes the tasking from the server that match the grep - require confirmation
-
-
- forge payloads - equivalency with execute_assembly and inline_assembly
+
- MYTHIC_API_INSECURE= boolean --insecure flag - and invert default = false
- update output table formats to respect terminal width
- invert cobra-cli nto functions (no global vars)
-
-
-
- โ
grep --raw - only diplay of all matching items raw output follow grep -R style single-line file dilimeters in the output
- โ
use per-server cache dirs
- โ
make sure raw output has a newline at the end