Skip to content

Commit fcc4c24

Browse files
Merge pull request #1 from MythicAgents/dev
Merge of added commands and refactors
2 parents 7584285 + 5f5d022 commit fcc4c24

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3400
-622
lines changed
Lines changed: 8 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -1,214 +1,27 @@
1-
from tabulate import tabulate
21
from mythic_container.MythicCommandBase import *
32
from mythic_container.MythicRPC import SendMythicRPCFileGetContent, MythicRPCFileGetContentMessage
43
from sliver import SliverClientConfig, SliverClient, client_pb2
54
from mythic_container.MythicCommandBase import *
65
from mythic_container.MythicRPC import *
76
from mythic_container.PayloadBuilder import *
8-
import json
97

108

9+
sliver_clients = {}
10+
1111
async def create_sliver_client(taskData: PTTaskMessageAllData):
12+
if (f"{taskData.Callback.ID}" in sliver_clients.keys()):
13+
return sliver_clients[f"{taskData.Callback.ID}"]
14+
1215
filecontent = await SendMythicRPCFileGetContent(MythicRPCFileGetContentMessage(
16+
# TODO: could possibly mirror this in the implant create_client, and get rid of extraInfo? (payload vs callback....)
1317
AgentFileId=taskData.BuildParameters[0].Value
1418
))
1519

1620
config = SliverClientConfig.parse_config(filecontent.Content)
1721
client = SliverClient(config)
1822

19-
# TODO: cache this (global dict?) - can verify in this function if need to re-create
2023
await client.connect()
2124

22-
return client
23-
24-
async def sessions_list(taskData: PTTaskMessageAllData):
25-
client = await create_sliver_client(taskData)
26-
sessions = await client.sessions()
27-
28-
# This is the sliver formatting
29-
30-
# ID Transport Remote Address Hostname Username Operating System Health
31-
# ========== =========== ====================== ========== ========== ================== =========
32-
# 78c06ded mtls 192.168.17.129:51042 ubuntu root linux/amd64 [ALIVE]
33-
34-
# TODO: match sliver formatting
35-
# what to show when no sessions?
36-
37-
headers = ["ID", "Transport", "Remote Address", "Hostname", "Username", "Operating System", "Health"]
38-
data = [(session.ID, session.Transport, session.RemoteAddress, session.Hostname, session.Username, session.OS, "[DEAD]" if session.IsDead else "[ALIVE]") for session in sessions]
39-
table = tabulate(data, headers=headers)
40-
41-
return table
42-
43-
async def profiles_list(taskData: PTTaskMessageAllData):
44-
client = await create_sliver_client(taskData)
45-
profiles = await client.implant_profiles()
46-
47-
# TODO: match sliver formatting
48-
# show nothing if no profiles
49-
50-
return f"{profiles}"
51-
52-
async def beacons_list(taskData: PTTaskMessageAllData):
53-
client = await create_sliver_client(taskData)
54-
beacons = await client.beacons()
55-
56-
# TODO: match sliver formatting
57-
58-
# ID Name Transport Hostname Username Operating System Last Check-In Next Check-In
59-
# ========== ============= =========== ========== ========== ================== =============== ===============
60-
# d90a2ec6 DARK_MITTEN mtls ubuntu ubuntu linux/amd64 2s 1m4s
61-
62-
# What to show if no beacons?
63-
64-
return f"{beacons}"
65-
66-
async def implants_list(taskData: PTTaskMessageAllData):
67-
client = await create_sliver_client(taskData)
68-
implants = await client.implant_builds()
69-
70-
# This is the sliver formatting
71-
72-
# Name Implant Type Template OS/Arch Format Command & Control Debug
73-
# ================ ============== ========== ============= ============ =============================== =======
74-
# DARK_MITTEN beacon sliver linux/amd64 EXECUTABLE [1] mtls://192.168.17.129:443 false
75-
76-
# TODO: match sliver formatting
77-
# how to show Template?
78-
# implant.Format is ValueType?
79-
# C2 only shows first URL
80-
# What to show if no implants?
81-
82-
headers = ["Name", "Implant Type", "OS/Arch", "Command & Control", "Debug"]
83-
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()]
84-
table = tabulate(data, headers=headers)
85-
86-
return table
87-
88-
async def jobs_list(taskData: PTTaskMessageAllData):
89-
client = await create_sliver_client(taskData)
90-
jobs = await client.jobs()
91-
92-
# TODO: match sliver formatting
93-
94-
# ID Name Protocol Port Stage Profile
95-
# ==== ====== ========== ====== ===============
96-
# 1 mtls tcp 443
97-
98-
# [*] No active jobs
99-
100-
return f"{jobs}"
25+
sliver_clients[f"{taskData.Callback.ID}"] = client
10126

102-
async def version(taskData: PTTaskMessageAllData):
103-
client = await create_sliver_client(taskData)
104-
version_results = await client.version()
105-
106-
# TODO: match sliver formatting
107-
108-
# [*] Client v1.5.42 - 85b0e870d05ec47184958dbcb871ddee2eb9e3df - linux/amd64
109-
# Compiled at 2024-02-28 13:46:53 -0600 CST
110-
# Compiled with go version go1.20.7 linux/amd64
111-
112-
113-
# [*] Server v1.5.42 - 85b0e870d05ec47184958dbcb871ddee2eb9e3df - linux/amd64
114-
# Compiled at 2024-02-28 13:46:53 -0600 CST
115-
116-
return f"{version_results}"
117-
118-
async def jobs_kill(taskData: PTTaskMessageAllData, job_id: int):
119-
client = await create_sliver_client(taskData)
120-
kill_response = await client.kill_job(job_id=job_id)
121-
122-
# TODO: match sliver formatting
123-
124-
# [*] Killing job #1 ...
125-
# [!] Job #1 stopped (tcp/mtls)
126-
# [*] Successfully killed job #1
127-
128-
return f"{kill_response}"
129-
130-
async def mtls_start(taskData: PTTaskMessageAllData, port: int):
131-
client = await create_sliver_client(taskData)
132-
133-
mtls_start_result = await client.start_mtls_listener(
134-
host = "0.0.0.0",
135-
port = port,
136-
persistent = False,
137-
)
138-
139-
# TODO: match sliver formatting
140-
141-
# [*] Starting mTLS listener ...
142-
# [*] Successfully started job #1
143-
144-
return f"{mtls_start_result}"
145-
146-
async def use(taskData: PTTaskMessageAllData, sliver_id: int):
147-
client = await create_sliver_client(taskData)
148-
149-
beacon_info = await client.beacon_by_id(sliver_id)
150-
session_info = await client.session_by_id(sliver_id)
151-
152-
if (not beacon_info and not session_info):
153-
# TODO: throw error and catch in use.py, and handle sending mythic errors gracefully
154-
# taskResponse = PTTaskCreateTaskingMessageResponse(
155-
# TaskID=taskData.Task.ID,
156-
# Success=False,
157-
# Completed=True,
158-
# Error="id not found in sliver",
159-
# TaskStatus=f"[!] no session or beacon found with ID {sliver_id}",
160-
# )
161-
# return taskResponse
162-
return f"[!] no session or beacon found with ID {sliver_id}"
163-
164-
# TODO: match sliver formatting
165-
# [*] Active session FUNNY_DRIVEWAY (586a4bdf-ffaf-4136-8387-45cc983ecc0f)
166-
167-
isBeacon = beacon_info is not None
168-
implant_info = beacon_info or session_info
169-
170-
# check if payload already exists, if so, skip to creating the callback
171-
search = await SendMythicRPCPayloadSearch(MythicRPCPayloadSearchMessage(
172-
PayloadUUID=sliver_id
173-
))
174-
175-
if (len(search.Payloads) == 0):
176-
# create the payload
177-
# TODO: figure out mappings for windows or mac...
178-
sliver_os_table = {
179-
'linux': 'Linux'
180-
}
181-
182-
new_payload = MythicRPCPayloadCreateFromScratchMessage(
183-
TaskID=taskData.Task.ID,
184-
PayloadConfiguration=MythicRPCPayloadConfiguration(
185-
payload_type="sliverimplant",
186-
uuid=sliver_id,
187-
selected_os=sliver_os_table[implant_info.OS],
188-
description=f"(no download) using sliver {'beaconing' if isBeacon else 'interactive'} implant for {sliver_id}",
189-
build_parameters=[],
190-
c2_profiles=[],
191-
# TODO: figure out if possible to not specify these manually
192-
commands=['ifconfig', 'download', 'upload', 'ls', 'ps', 'ping', 'whoami', 'screenshot', 'netstat', 'getgid', 'getuid', 'getpid', 'cat', 'cd', 'pwd', 'info', 'execute', 'mkdir', 'shell', 'terminate', 'rm']
193-
),
194-
)
195-
scratchBuild = await SendMythicRPCPayloadCreateFromScratch(new_payload)
196-
197-
# create the callback
198-
extra_info = json.dumps({
199-
# TODO: if buildparams changes, then this won't work anymore (could make it more resilient)
200-
"slivercfg_fileid": taskData.BuildParameters[0].Value,
201-
"type": 'beacon' if isBeacon else 'session'
202-
})
203-
response = await SendMythicRPCCallbackCreate(MythicRPCCallbackCreateMessage(
204-
PayloadUUID=sliver_id,
205-
C2ProfileName="",
206-
IntegrityLevel=3,
207-
Host=implant_info.Hostname,
208-
User=implant_info.Username,
209-
Ip=implant_info.RemoteAddress.split(':')[0],
210-
ExtraInfo=extra_info,
211-
PID=implant_info.PID
212-
))
213-
214-
return f"[*] Active session FUNNY_DRIVEWAY ({sliver_id})"
27+
return client
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from ..SliverRequests import SliverAPI
2+
3+
from mythic_container.MythicCommandBase import *
4+
from mythic_container.MythicRPC import *
5+
from mythic_container.PayloadBuilder import *
6+
7+
# from sliver import common_pb2
8+
9+
class ArmoryArguments(TaskArguments):
10+
def __init__(self, command_line, **kwargs):
11+
super().__init__(command_line, **kwargs)
12+
self.args = []
13+
14+
async def parse_arguments(self):
15+
pass
16+
17+
18+
class Armory(CommandBase):
19+
cmd = "armory"
20+
needs_admin = False
21+
help_cmd = "armory"
22+
description = "Automatically download and install extensions/aliases"
23+
version = 1
24+
author = "Spencer Adolph"
25+
argument_class = ArmoryArguments
26+
attackmapping = []
27+
28+
async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllData) -> MythicCommandBase.PTTaskCreateTaskingMessageResponse:
29+
# Automatically download and install extensions/aliases
30+
31+
# Usage:
32+
# ======
33+
# armory [flags]
34+
35+
# Flags:
36+
# ======
37+
# TODO: -h, --help display help
38+
# TODO: -c, --ignore-cache ignore metadata cache, force refresh
39+
# TODO: -I, --insecure skip tls certificate validation
40+
# TODO: -p, --proxy string specify a proxy url (e.g. http://localhost:8080)
41+
# TODO: -t, --timeout string download timeout (default: 15m)
42+
43+
# Sub Commands:
44+
# =============
45+
# TODO: install Install an alias or extension
46+
# TODO: search Search for aliases and extensions by name (regex)
47+
# TODO: update Update installed an aliases and extensions
48+
49+
response = await armory(taskData)
50+
51+
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage(
52+
TaskID=taskData.Task.ID,
53+
Response=response.encode("UTF8"),
54+
))
55+
56+
taskResponse = MythicCommandBase.PTTaskCreateTaskingMessageResponse(
57+
TaskID=taskData.Task.ID,
58+
Success=True,
59+
Completed=True
60+
)
61+
62+
return taskResponse
63+
64+
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
65+
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
66+
return resp
67+
68+
69+
async def armory(taskData: PTTaskMessageAllData):
70+
# client = await SliverAPI.create_sliver_client(taskData)
71+
72+
# armory_results = await client.armory()
73+
74+
# TODO: match sliver formatting
75+
76+
return "This command not yet implemented..."

Payload_Type/sliverapi/sliverapi/agent_functions/beacons.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllDa
4848

4949

5050
# 'beacons' with no options
51-
response = await SliverAPI.beacons_list(taskData)
51+
response = await beacons_list(taskData)
5252

5353
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage(
5454
TaskID=taskData.Task.ID,
@@ -66,3 +66,18 @@ async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllDa
6666
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
6767
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
6868
return resp
69+
70+
async def beacons_list(taskData: PTTaskMessageAllData):
71+
client = await SliverAPI.create_sliver_client(taskData)
72+
beacons = await client.beacons()
73+
74+
# TODO: match sliver formatting
75+
76+
# ID Name Transport Hostname Username Operating System Last Check-In Next Check-In
77+
# ========== ============= =========== ========== ========== ================== =============== ===============
78+
# d90a2ec6 DARK_MITTEN mtls ubuntu ubuntu linux/amd64 2s 1m4s
79+
80+
# What to show if no beacons?
81+
82+
return f"{beacons}"
83+
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
from ..SliverRequests import SliverAPI
2+
3+
from mythic_container.MythicCommandBase import *
4+
from mythic_container.MythicRPC import *
5+
from mythic_container.PayloadBuilder import *
6+
7+
8+
class BuildersArguments(TaskArguments):
9+
def __init__(self, command_line, **kwargs):
10+
super().__init__(command_line, **kwargs)
11+
self.args = []
12+
13+
async def parse_arguments(self):
14+
pass
15+
16+
17+
class Builders(CommandBase):
18+
cmd = "builders"
19+
needs_admin = False
20+
help_cmd = "builders"
21+
description = "Lists external builders currently registered with the server."
22+
version = 1
23+
author = "Spencer Adolph"
24+
argument_class = BuildersArguments
25+
attackmapping = []
26+
27+
async def create_go_tasking(self, taskData: MythicCommandBase.PTTaskMessageAllData) -> MythicCommandBase.PTTaskCreateTaskingMessageResponse:
28+
# Command: builders
29+
# About: Lists external builders currently registered with the server.
30+
31+
# External builders allow the Sliver server offload implant builds onto external machines.
32+
# For more information: https://github.com/BishopFox/sliver/wiki/External-Builders
33+
34+
35+
# Usage:
36+
# ======
37+
# builders [flags]
38+
39+
# Flags:
40+
# ======
41+
# TODO: -h, --help display help
42+
# TODO: -t, --timeout int command timeout in seconds (default: 60)
43+
44+
45+
response = await builders(taskData)
46+
47+
await SendMythicRPCResponseCreate(MythicRPCResponseCreateMessage(
48+
TaskID=taskData.Task.ID,
49+
Response=response.encode("UTF8"),
50+
))
51+
52+
taskResponse = MythicCommandBase.PTTaskCreateTaskingMessageResponse(
53+
TaskID=taskData.Task.ID,
54+
Success=True,
55+
Completed=True
56+
)
57+
58+
return taskResponse
59+
60+
async def process_response(self, task: PTTaskMessageAllData, response: any) -> PTTaskProcessResponseMessageResponse:
61+
resp = PTTaskProcessResponseMessageResponse(TaskID=task.Task.ID, Success=True)
62+
return resp
63+
64+
65+
async def builders(taskData: PTTaskMessageAllData):
66+
# client = await SliverAPI.create_sliver_client(taskData)
67+
# client._stub.bu
68+
69+
# TODO: match sliver formatting
70+
71+
return "This command not yet implemented, requires re-build of gRPC (or sliver 1.6)"

0 commit comments

Comments
 (0)