-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from MythicAgents/dev
Merge of added commands and refactors
- Loading branch information
Showing
70 changed files
with
3,400 additions
and
622 deletions.
There are no files selected for viewing
203 changes: 8 additions & 195 deletions
203
Payload_Type/sliverapi/sliverapi/SliverRequests/SliverAPI.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,214 +1,27 @@ | ||
from tabulate import tabulate | ||
from mythic_container.MythicCommandBase import * | ||
from mythic_container.MythicRPC import SendMythicRPCFileGetContent, MythicRPCFileGetContentMessage | ||
from sliver import SliverClientConfig, SliverClient, client_pb2 | ||
from mythic_container.MythicCommandBase import * | ||
from mythic_container.MythicRPC import * | ||
from mythic_container.PayloadBuilder import * | ||
import json | ||
|
||
|
||
sliver_clients = {} | ||
|
||
async def create_sliver_client(taskData: PTTaskMessageAllData): | ||
if (f"{taskData.Callback.ID}" in sliver_clients.keys()): | ||
return sliver_clients[f"{taskData.Callback.ID}"] | ||
|
||
filecontent = await SendMythicRPCFileGetContent(MythicRPCFileGetContentMessage( | ||
# TODO: could possibly mirror this in the implant create_client, and get rid of extraInfo? (payload vs callback....) | ||
AgentFileId=taskData.BuildParameters[0].Value | ||
)) | ||
|
||
config = SliverClientConfig.parse_config(filecontent.Content) | ||
client = SliverClient(config) | ||
|
||
# TODO: cache this (global dict?) - can verify in this function if need to re-create | ||
await client.connect() | ||
|
||
return client | ||
|
||
async def sessions_list(taskData: PTTaskMessageAllData): | ||
client = await create_sliver_client(taskData) | ||
sessions = await client.sessions() | ||
|
||
# This is the sliver formatting | ||
|
||
# ID Transport Remote Address Hostname Username Operating System Health | ||
# ========== =========== ====================== ========== ========== ================== ========= | ||
# 78c06ded mtls 192.168.17.129:51042 ubuntu root linux/amd64 [ALIVE] | ||
|
||
# TODO: match sliver formatting | ||
# what to show when no sessions? | ||
|
||
headers = ["ID", "Transport", "Remote Address", "Hostname", "Username", "Operating System", "Health"] | ||
data = [(session.ID, session.Transport, session.RemoteAddress, session.Hostname, session.Username, session.OS, "[DEAD]" if session.IsDead else "[ALIVE]") for session in sessions] | ||
table = tabulate(data, headers=headers) | ||
|
||
return table | ||
|
||
async def profiles_list(taskData: PTTaskMessageAllData): | ||
client = await create_sliver_client(taskData) | ||
profiles = await client.implant_profiles() | ||
|
||
# TODO: match sliver formatting | ||
# show nothing if no profiles | ||
|
||
return f"{profiles}" | ||
|
||
async def beacons_list(taskData: PTTaskMessageAllData): | ||
client = await create_sliver_client(taskData) | ||
beacons = await client.beacons() | ||
|
||
# TODO: match sliver formatting | ||
|
||
# ID Name Transport Hostname Username Operating System Last Check-In Next Check-In | ||
# ========== ============= =========== ========== ========== ================== =============== =============== | ||
# d90a2ec6 DARK_MITTEN mtls ubuntu ubuntu linux/amd64 2s 1m4s | ||
|
||
# What to show if no beacons? | ||
|
||
return f"{beacons}" | ||
|
||
async def implants_list(taskData: PTTaskMessageAllData): | ||
client = await create_sliver_client(taskData) | ||
implants = await client.implant_builds() | ||
|
||
# This is the sliver formatting | ||
|
||
# Name Implant Type Template OS/Arch Format Command & Control Debug | ||
# ================ ============== ========== ============= ============ =============================== ======= | ||
# DARK_MITTEN beacon sliver linux/amd64 EXECUTABLE [1] mtls://192.168.17.129:443 false | ||
|
||
# TODO: match sliver formatting | ||
# how to show Template? | ||
# implant.Format is ValueType? | ||
# C2 only shows first URL | ||
# What to show if no implants? | ||
|
||
headers = ["Name", "Implant Type", "OS/Arch", "Command & Control", "Debug"] | ||
data = [(implant.FileName, "beacon" if implant.IsBeacon else "session", f"{implant.GOOS}/{implant.GOARCH}", implant.C2[0].URL, implant.Debug) for implant in implants.values()] | ||
table = tabulate(data, headers=headers) | ||
|
||
return table | ||
|
||
async def jobs_list(taskData: PTTaskMessageAllData): | ||
client = await create_sliver_client(taskData) | ||
jobs = await client.jobs() | ||
|
||
# TODO: match sliver formatting | ||
|
||
# ID Name Protocol Port Stage Profile | ||
# ==== ====== ========== ====== =============== | ||
# 1 mtls tcp 443 | ||
|
||
# [*] No active jobs | ||
|
||
return f"{jobs}" | ||
sliver_clients[f"{taskData.Callback.ID}"] = client | ||
|
||
async def version(taskData: PTTaskMessageAllData): | ||
client = await create_sliver_client(taskData) | ||
version_results = await client.version() | ||
|
||
# TODO: match sliver formatting | ||
|
||
# [*] Client v1.5.42 - 85b0e870d05ec47184958dbcb871ddee2eb9e3df - linux/amd64 | ||
# Compiled at 2024-02-28 13:46:53 -0600 CST | ||
# Compiled with go version go1.20.7 linux/amd64 | ||
|
||
|
||
# [*] Server v1.5.42 - 85b0e870d05ec47184958dbcb871ddee2eb9e3df - linux/amd64 | ||
# Compiled at 2024-02-28 13:46:53 -0600 CST | ||
|
||
return f"{version_results}" | ||
|
||
async def jobs_kill(taskData: PTTaskMessageAllData, job_id: int): | ||
client = await create_sliver_client(taskData) | ||
kill_response = await client.kill_job(job_id=job_id) | ||
|
||
# TODO: match sliver formatting | ||
|
||
# [*] Killing job #1 ... | ||
# [!] Job #1 stopped (tcp/mtls) | ||
# [*] Successfully killed job #1 | ||
|
||
return f"{kill_response}" | ||
|
||
async def mtls_start(taskData: PTTaskMessageAllData, port: int): | ||
client = await create_sliver_client(taskData) | ||
|
||
mtls_start_result = await client.start_mtls_listener( | ||
host = "0.0.0.0", | ||
port = port, | ||
persistent = False, | ||
) | ||
|
||
# TODO: match sliver formatting | ||
|
||
# [*] Starting mTLS listener ... | ||
# [*] Successfully started job #1 | ||
|
||
return f"{mtls_start_result}" | ||
|
||
async def use(taskData: PTTaskMessageAllData, sliver_id: int): | ||
client = await create_sliver_client(taskData) | ||
|
||
beacon_info = await client.beacon_by_id(sliver_id) | ||
session_info = await client.session_by_id(sliver_id) | ||
|
||
if (not beacon_info and not session_info): | ||
# TODO: throw error and catch in use.py, and handle sending mythic errors gracefully | ||
# taskResponse = PTTaskCreateTaskingMessageResponse( | ||
# TaskID=taskData.Task.ID, | ||
# Success=False, | ||
# Completed=True, | ||
# Error="id not found in sliver", | ||
# TaskStatus=f"[!] no session or beacon found with ID {sliver_id}", | ||
# ) | ||
# return taskResponse | ||
return f"[!] no session or beacon found with ID {sliver_id}" | ||
|
||
# TODO: match sliver formatting | ||
# [*] Active session FUNNY_DRIVEWAY (586a4bdf-ffaf-4136-8387-45cc983ecc0f) | ||
|
||
isBeacon = beacon_info is not None | ||
implant_info = beacon_info or session_info | ||
|
||
# check if payload already exists, if so, skip to creating the callback | ||
search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage( | ||
PayloadUUID=sliver_id | ||
)) | ||
|
||
if (len(search.Payloads) == 0): | ||
# create the payload | ||
# TODO: figure out mappings for windows or mac... | ||
sliver_os_table = { | ||
'linux': 'Linux' | ||
} | ||
|
||
new_payload = MythicRPCPayloadCreateFromScratchMessage( | ||
TaskID=taskData.Task.ID, | ||
PayloadConfiguration=MythicRPCPayloadConfiguration( | ||
payload_type="sliverimplant", | ||
uuid=sliver_id, | ||
selected_os=sliver_os_table[implant_info.OS], | ||
description=f"(no download) using sliver {'beaconing' if isBeacon else 'interactive'} implant for {sliver_id}", | ||
build_parameters=[], | ||
c2_profiles=[], | ||
# TODO: figure out if possible to not specify these manually | ||
commands=['ifconfig', 'download', 'upload', 'ls', 'ps', 'ping', 'whoami', 'screenshot', 'netstat', 'getgid', 'getuid', 'getpid', 'cat', 'cd', 'pwd', 'info', 'execute', 'mkdir', 'shell', 'terminate', 'rm'] | ||
), | ||
) | ||
scratchBuild = await SendMythicRPCPayloadCreateFromScratch(new_payload) | ||
|
||
# create the callback | ||
extra_info = json.dumps({ | ||
# TODO: if buildparams changes, then this won't work anymore (could make it more resilient) | ||
"slivercfg_fileid": taskData.BuildParameters[0].Value, | ||
"type": 'beacon' if isBeacon else 'session' | ||
}) | ||
response = await SendMythicRPCCallbackCreate(MythicRPCCallbackCreateMessage( | ||
PayloadUUID=sliver_id, | ||
C2ProfileName="", | ||
IntegrityLevel=3, | ||
Host=implant_info.Hostname, | ||
User=implant_info.Username, | ||
Ip=implant_info.RemoteAddress.split(':')[0], | ||
ExtraInfo=extra_info, | ||
PID=implant_info.PID | ||
)) | ||
|
||
return f"[*] Active session FUNNY_DRIVEWAY ({sliver_id})" | ||
return client |
76 changes: 76 additions & 0 deletions
76
Payload_Type/sliverapi/sliverapi/agent_functions/armory.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from ..SliverRequests import SliverAPI | ||
|
||
from mythic_container.MythicCommandBase import * | ||
from mythic_container.MythicRPC import * | ||
from mythic_container.PayloadBuilder import * | ||
|
||
# from sliver import common_pb2 | ||
|
||
class ArmoryArguments(TaskArguments): | ||
def __init__(self, command_line, **kwargs): | ||
super().__init__(command_line, **kwargs) | ||
self.args = [] | ||
|
||
async def parse_arguments(self): | ||
pass | ||
|
||
|
||
class Armory(CommandBase): | ||
cmd = "armory" | ||
needs_admin = False | ||
help_cmd = "armory" | ||
description = "Automatically download and install extensions/aliases" | ||
version = 1 | ||
author = "Spencer Adolph" | ||
argument_class = ArmoryArguments | ||
attackmapping = [] | ||
|
||
async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllData) -> MythicCommandBase.PTTaskCreateTaskingMessageResponse: | ||
# Automatically download and install extensions/aliases | ||
|
||
# Usage: | ||
# ====== | ||
# armory [flags] | ||
|
||
# Flags: | ||
# ====== | ||
# TODO: -h, --help display help | ||
# TODO: -c, --ignore-cache ignore metadata cache, force refresh | ||
# TODO: -I, --insecure skip tls certificate validation | ||
# TODO: -p, --proxy string specify a proxy url (e.g. http://localhost:8080) | ||
# TODO: -t, --timeout string download timeout (default: 15m) | ||
|
||
# Sub Commands: | ||
# ============= | ||
# TODO: install Install an alias or extension | ||
# TODO: search Search for aliases and extensions by name (regex) | ||
# TODO: update Update installed an aliases and extensions | ||
|
||
response = await armory(taskData) | ||
|
||
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( | ||
TaskID=taskData.Task.ID, | ||
Response=response.encode("UTF8"), | ||
)) | ||
|
||
taskResponse = MythicCommandBase.PTTaskCreateTaskingMessageResponse( | ||
TaskID=taskData.Task.ID, | ||
Success=True, | ||
Completed=True | ||
) | ||
|
||
return taskResponse | ||
|
||
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: | ||
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) | ||
return resp | ||
|
||
|
||
async def armory(taskData: PTTaskMessageAllData): | ||
# client = await SliverAPI.create_sliver_client(taskData) | ||
|
||
# armory_results = await client.armory() | ||
|
||
# TODO: match sliver formatting | ||
|
||
return "This command not yet implemented..." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
Payload_Type/sliverapi/sliverapi/agent_functions/builders.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from ..SliverRequests import SliverAPI | ||
|
||
from mythic_container.MythicCommandBase import * | ||
from mythic_container.MythicRPC import * | ||
from mythic_container.PayloadBuilder import * | ||
|
||
|
||
class BuildersArguments(TaskArguments): | ||
def __init__(self, command_line, **kwargs): | ||
super().__init__(command_line, **kwargs) | ||
self.args = [] | ||
|
||
async def parse_arguments(self): | ||
pass | ||
|
||
|
||
class Builders(CommandBase): | ||
cmd = "builders" | ||
needs_admin = False | ||
help_cmd = "builders" | ||
description = "Lists external builders currently registered with the server." | ||
version = 1 | ||
author = "Spencer Adolph" | ||
argument_class = BuildersArguments | ||
attackmapping = [] | ||
|
||
async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllData) -> MythicCommandBase.PTTaskCreateTaskingMessageResponse: | ||
# Command: builders | ||
# About: Lists external builders currently registered with the server. | ||
|
||
# External builders allow the Sliver server offload implant builds onto external machines. | ||
# For more information: https://github.com/BishopFox/sliver/wiki/External-Builders | ||
|
||
|
||
# Usage: | ||
# ====== | ||
# builders [flags] | ||
|
||
# Flags: | ||
# ====== | ||
# TODO: -h, --help display help | ||
# TODO: -t, --timeout int command timeout in seconds (default: 60) | ||
|
||
|
||
response = await builders(taskData) | ||
|
||
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage( | ||
TaskID=taskData.Task.ID, | ||
Response=response.encode("UTF8"), | ||
)) | ||
|
||
taskResponse = MythicCommandBase.PTTaskCreateTaskingMessageResponse( | ||
TaskID=taskData.Task.ID, | ||
Success=True, | ||
Completed=True | ||
) | ||
|
||
return taskResponse | ||
|
||
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse: | ||
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True) | ||
return resp | ||
|
||
|
||
async def builders(taskData: PTTaskMessageAllData): | ||
# client = await SliverAPI.create_sliver_client(taskData) | ||
# client._stub.bu | ||
|
||
# TODO: match sliver formatting | ||
|
||
return "This command not yet implemented, requires re-build of gRPC (or sliver 1.6)" |
Oops, something went wrong.