Skip to content

Commit

Permalink
Merge pull request #2 from MythicMeta/v0.2.9-rc01
Browse files Browse the repository at this point in the history
v0.2.9
  • Loading branch information
its-a-feature authored May 23, 2023
2 parents 37dab07 + 7bb7f34 commit 857c68f
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 13 deletions.
174 changes: 174 additions & 0 deletions mythic_container/C2ProfileBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .logging import logger
import base64
import sys
from typing import List


class C2OPSECMessage:
Expand Down Expand Up @@ -136,6 +137,154 @@ def __str__(self):
return json.dumps(self.to_json(), sort_keys=True, indent=2)


class C2GetIOCMessage:
"""Payload's C2 Profile configuration for getting IOCs
Attributes:
Name (str): Name of the C2 Profile
Parameters (dict): Dictionary of C2 Parameter name:value
Functions:
to_json(self): return dictionary form of class
"""

def __init__(self,
c2_profile_name: str,
parameters: dict,
**kwargs):
self.Name = c2_profile_name
self.Parameters = parameters
for k, v in kwargs.items():
logger.error(f"unknown kwarg {k} {v}")

def to_json(self):
return {
"c2_profile_name": self.Name,
"parameters": self.Parameters
}

def __str__(self):
return json.dumps(self.to_json(), sort_keys=True, indent=2)


class C2GetIOCMessageResponseIOC:
"""An IOC to report back as part of a C2 Profile's get_ioc function
Attributes:
Type (str): The type of IOC (ex: Hash, URL)
IOC (str): The actual IOC value
"""
def __init__(self,
Type: str,
IOC: str):
self.Type = Type
self.IOC = IOC

def to_json(self):
return {
"type": self.Type,
"ioc": self.IOC
}


class C2GetIOCMessageResponse:
"""Status of running C2 Profile's get_ioc function
Attributes:
Success (bool): Did the get ioc request succeed or fail
Error (str): Error message if the get ioc request failed
IOCs (List[C2GetIOCMessageResponseIOC]): List of the IOC values
Functions:
to_json(self): return dictionary form of class
"""

def __init__(self,
Success: bool,
Error: str = "",
IOCs: List[C2GetIOCMessageResponseIOC] = [],
**kwargs):
self.Success = Success
self.Error = Error
self.IOCs = IOCs
for k, v in kwargs.items():
logger.error(f"unknown kwarg {k} {v}")

def to_json(self):
return {
"success": self.Success,
"error": self.Error,
"iocs": self.IOCs
}

def __str__(self):
return json.dumps(self.to_json(), sort_keys=True, indent=2)


class C2SampleMessageMessage:
"""Sample C2 message based on the Payload's configuration
Attributes:
Name (str): Name of the C2 Profile
Parameters (dict): Dictionary of C2 Parameter name:value
Functions:
to_json(self): return dictionary form of class
"""

def __init__(self,
c2_profile_name: str,
parameters: dict,
**kwargs):
self.Name = c2_profile_name
self.Parameters = parameters
for k, v in kwargs.items():
logger.error(f"unknown kwarg {k} {v}")

def to_json(self):
return {
"c2_profile_name": self.Name,
"parameters": self.Parameters
}

def __str__(self):
return json.dumps(self.to_json(), sort_keys=True, indent=2)


class C2SampleMessageMessageResponse:
"""Sample C2 Message based on the payload's configuration
Attributes:
Success (bool): Did the sample message generation succeed or fail
Error (str): Error message if the sample message generation failed
Message (str): Sample message to represent that C2 traffic would look like for this payload
Functions:
to_json(self): return dictionary form of class
"""

def __init__(self,
Success: bool,
Error: str = "",
Message: str = "",
**kwargs):
self.Success = Success
self.Error = Error
self.Message = Message
for k, v in kwargs.items():
logger.error(f"unknown kwarg {k} {v}")

def to_json(self):
return {
"success": self.Success,
"error": self.Error,
"message": self.Message
}

def __str__(self):
return json.dumps(self.to_json(), sort_keys=True, indent=2)


class C2GetDebugOutputMessage:
"""Try to get debug output from subprocess running internal server code
Expand Down Expand Up @@ -870,6 +1019,10 @@ class C2Profile:
redirect_rules
get_ioc
sample_message
"""

async def opsec(self, inputMsg: C2OPSECMessage) -> C2OPSECMessageResponse:
Expand Down Expand Up @@ -905,6 +1058,27 @@ async def redirect_rules(self, inputMsg: C2GetRedirectorRulesMessage) -> C2GetRe
response.Message += f"\nInput: {json.dumps(inputMsg.to_json(), indent=4)}"
return response

async def get_ioc(self, inputMsg: C2GetIOCMessage) -> C2GetIOCMessageResponse:
"""Generate IOCs for the network traffic associated with the specified c2 configuration
:param inputMsg: Payload's C2 Profile configuration
:return: C2GetIOCMessageResponse detailing some IOCs
"""
response = C2GetIOCMessageResponse(Success=True)
response.IOCs = []
return response

async def sample_message(self, inputMsg: C2SampleMessageMessage) -> C2SampleMessageMessageResponse:
"""Generate a sample message for this c2 profile based on the configuration specified
:param inputMsg: Payload's C2 Profile configuration
:return: C2SampleMessageMessageResponse detailing a sample message
"""
response = C2SampleMessageMessageResponse(Success=True)
response.Message = "Not Implemented"
response.Message += f"\nInput: {json.dumps(inputMsg.to_json(), indent=4)}"
return response

custom_rpc_functions: dict[
str, Callable[[C2OtherServiceRPCMessage], Awaitable[C2OtherServiceRPCMessageResponse]]] = {}
server_folder_path: pathlib.Path
Expand Down
20 changes: 11 additions & 9 deletions mythic_container/MythicCommandBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,14 @@ def __init__(self,
command_line: str = "",
tasking_location: str = "command_line",
raw_command_line: str = "",
task_dictionary: dict = {}):
task_dictionary: dict = {},
initial_parameter_group: str = ""):
self.command_line = str(command_line)
self.tasking_location = tasking_location
self.raw_command_line = raw_command_line
self.task_dictionary = task_dictionary
self.parameter_group_name = None
self.initial_parameter_group = initial_parameter_group

@property
def args(self) -> list[CommandParameter]:
Expand Down Expand Up @@ -713,6 +715,8 @@ def get_parameter_group_name(self) -> str:
raise ValueError(
f"Supplied Arguments, {suppliedArgNames}, match more than one parameter group, {groupNameOptions}, and all require at least one more value from the user")
elif len(finalMatchingGroupNames) > 1:
if self.initial_parameter_group in finalMatchingGroupNames:
return self.initial_parameter_group
raise ValueError(
f"Supplied Arguments, {suppliedArgNames}, match more than one parameter group, {finalMatchingGroupNames}")
else:
Expand Down Expand Up @@ -838,12 +842,12 @@ def to_json(self, base_path: Path):
/ "{}.js".format(self.script_name)
)
if code_file.exists():
code = code_file.read_bytes()
code = base64.b64encode(code).decode()
code = code_file.read_bytes().decode()
#code = base64.b64encode(code).decode()
return {"script": code, "name": self.script_name, "author": self.author}
elif Path(self.script_name).exists():
code = Path(self.script_name).read_bytes()
code = base64.b64encode(code).decode()
code = Path(self.script_name).read_bytes().decode()
#code = base64.b64encode(code).decode()
return {"script": code, "name": self.script_name, "author": self.author}
else:
raise Exception(
Expand All @@ -852,9 +856,6 @@ def to_json(self, base_path: Path):
except Exception as e:
raise e

def __str__(self):
return json.dumps(self.to_json(), sort_keys=True, indent=2)


class MythicTask:
"""Instance of Mythic Tasking used with `create_tasking`. Use `create_go_tasking` instead.
Expand Down Expand Up @@ -1502,7 +1503,8 @@ def __init__(self,
self.args = args(command_line=task["params"],
tasking_location=task["tasking_location"],
raw_command_line=task["original_params"],
task_dictionary=task, )
task_dictionary=task,
initial_parameter_group=task["parameter_group_name"])
else:
self.args = args
for k, v in kwargs.items():
Expand Down
5 changes: 4 additions & 1 deletion mythic_container/MythicGoRPC/send_mythic_rpc_file_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ def __init__(self,
AppendContents: bytes = None,
ReplaceContents: bytes = None,
Delete: bool = None,
DeleteAfterFetch: bool = None,
**kwargs):
self.AgentFileID = AgentFileID
self.Filename = Filename
self.Comment = Comment
self.AppendContents = AppendContents
self.ReplaceContents = ReplaceContents
self.Delete = Delete
self.DeleteAfterFetch = DeleteAfterFetch
for k, v in kwargs.items():
logger.info(f"Unknown kwarg {k} - {v}")

Expand All @@ -29,7 +31,8 @@ def to_json(self):
"filename": self.Filename,
"comment": self.Comment,
"append_contents": self.AppendContents,
"delete": self.Delete
"delete": self.Delete,
"delete_after_fetch": self.DeleteAfterFetch
}


Expand Down
6 changes: 4 additions & 2 deletions mythic_container/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from .rabbitmq import rabbitmqConnectionClass
from .mythic_service import start_and_run_forever, test_command

containerVersion = "v1.0.6"
containerVersion = "v1.0.7"

PyPi_version = "0.2.8"
PyPi_version = "0.2.9"

RabbitmqConnection = rabbitmqConnectionClass()

Expand Down Expand Up @@ -31,6 +31,8 @@
C2_RPC_RESYNC_ROUTING_KEY = "c2_rpc_resync"
C2_RPC_OPSEC_CHECKS_ROUTING_KEY = "c2_rpc_opsec_check"
C2_RPC_CONFIG_CHECK_ROUTING_KEY = "c2_rpc_config_check"
C2_RPC_GET_IOC_ROUTING_KEY = "c2_rpc_get_ioc"
C2_RPC_SAMPLE_MESSAGE_ROUTING_KEY = "c2_rpc_sample_message"
C2_RPC_REDIRECTOR_RULES_ROUTING_KEY = "c2_rpc_redirector_rules"
C2_RPC_START_SERVER_ROUTING_KEY = "c2_rpc_start_server"
C2_RPC_STOP_SERVER_ROUTING_KEY = "c2_rpc_stop_server"
Expand Down
1 change: 1 addition & 0 deletions mythic_container/agent_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ async def initialize_task(
command_line=message_json["task"]["params"],
tasking_location=message_json["task"]["tasking_location"],
raw_command_line=message_json["task"]["original_params"],
initial_parameter_group=message_json["task"]["parameter_group_name"],
task_dictionary=message_json["task"],
),
callback_info=message_json["callback"]
Expand Down
Loading

0 comments on commit 857c68f

Please sign in to comment.