mantis_api_client 5.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ import argparse
3
+ import datetime
4
+ import json
5
+ import sys
6
+ from typing import Any
7
+ from typing import Dict
8
+ from typing import List
9
+
10
+ from mantis_common_model.pagination import Pagination
11
+ from mantis_scenario_model.api_scenario_model import LabListReply
12
+ from mantis_scenario_model.lab_model import Lab
13
+
14
+ import mantis_api_client.scenario_api as scenario_api
15
+ from mantis_api_client import user_api
16
+
17
+
18
+ #
19
+ # 'labs_handler' handler
20
+ #
21
+ def labs_handler(args: Any) -> None:
22
+ all_labs = args.all_labs
23
+ offset = 100
24
+ labs_to_list: List[Lab] = []
25
+ try:
26
+ page: int = 1
27
+ pagination: Pagination = Pagination(**{"page": page, "limit": offset})
28
+ labs: LabListReply = scenario_api.fetch_labs(
29
+ all_labs=all_labs, pagination=pagination
30
+ )
31
+ labs_to_list = labs_to_list + [Lab(**lab) for lab in labs["data"]]
32
+ while pagination.page * pagination.limit < labs["pagination"]["total_records"]:
33
+ page = page + 1
34
+ pagination = Pagination(**{"page": page, "limit": offset})
35
+ labs = scenario_api.fetch_labs(all_labs=all_labs, pagination=pagination)
36
+ labs_to_list = labs_to_list + [Lab(**lab) for lab in labs["data"]]
37
+
38
+ except Exception as e:
39
+ print(f"Error when fetching labs: '{e}'")
40
+ sys.exit(1)
41
+
42
+ if args.json:
43
+ print(json.dumps(labs))
44
+ return
45
+
46
+ # Maintain a local cache of user_id and user_info, to avoid unecessary calls to Account API
47
+ cache_created_by: Dict[str, Any] = {}
48
+
49
+ print("[+] Available labs:")
50
+ for lab in labs_to_list:
51
+ lab_creation_timestamp = (
52
+ datetime.datetime.fromtimestamp(
53
+ lab.lab_creation_timestamp, datetime.timezone.utc
54
+ ).strftime("%Y-%m-%d %H:%M:%S")
55
+ + " UTC"
56
+ )
57
+
58
+ if lab.created_by in cache_created_by.keys():
59
+ created_by = cache_created_by[lab.created_by]
60
+ else:
61
+ created_by = user_api.fetch_user(lab.created_by)
62
+ cache_created_by[lab.created_by] = created_by
63
+
64
+ print(f" [+] \033[1mContent\033[0m: {lab.name}")
65
+ print(f" - \033[1mType\033[0m: {lab.content_type.value}")
66
+ print(f" - \033[1mLab ID\033[0m: {lab.runner_id}")
67
+ print(f" - \033[1mCreation time\033[0m: {lab_creation_timestamp}")
68
+ print(
69
+ f" - \033[1mCreated by\033[0m: {created_by['given_name']} {created_by['last_name']} ({lab.created_by})"
70
+ )
71
+ print(f" - \033[1mStatus\033[0m: {lab.status.value}")
72
+
73
+
74
+ def add_labs_parser(root_parser: argparse.ArgumentParser, subparsers: Any) -> None:
75
+ # Subparser labs
76
+ parser_labs = subparsers.add_parser(
77
+ "labs", help="List current labs", formatter_class=root_parser.formatter_class
78
+ )
79
+ parser_labs.set_defaults(func=labs_handler)
80
+ parser_labs.add_argument(
81
+ "-a",
82
+ "--all",
83
+ action="store_true",
84
+ dest="all_labs",
85
+ help="Include stopped labs",
86
+ )
87
+ parser_labs.add_argument("--json", help="Return JSON result.", action="store_true")
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ import argparse
3
+ import json
4
+ import sys
5
+ from typing import Any
6
+
7
+ from pydantic.json import pydantic_encoder
8
+
9
+ from mantis_api_client import scenario_api
10
+ from mantis_api_client.utils import colored
11
+
12
+
13
+ #
14
+ # 'log_collector_list_handler' handler
15
+ #
16
+ def log_collector_list_handler(args: Any) -> None:
17
+ try:
18
+
19
+ log_collectors = scenario_api.get_log_collectors()
20
+ log_collectors = sorted(log_collectors, key=lambda k: (k["collector_name"]))
21
+
22
+ if args.json:
23
+ print(json.dumps(log_collectors, default=pydantic_encoder))
24
+ return
25
+
26
+ print("[+] Available Logs collectors:")
27
+ for lg in log_collectors:
28
+ print(f" [+] \033[1mName\033[0m: {lg['collector_name']}")
29
+ print(f" [+] \033[1mType\033[0m: {lg['collector_type']}")
30
+ print(f" [+] \033[1mDescription\033[0m: {lg['description']}")
31
+ cpes = lg["cpe_os_constraints"]
32
+ if len(cpes) > 0:
33
+ print(" [+] \033[1mCPE OS constraints\033[0m:")
34
+ for cpe in cpes:
35
+ print(f" [+] {cpe}")
36
+ print()
37
+
38
+ except Exception as e:
39
+ print(
40
+ colored(
41
+ f"Error when fetching log collectors data: '{e}'", "red", "on_white"
42
+ )
43
+ )
44
+ sys.exit(1)
45
+
46
+
47
+ def add_log_collector_parser(root_parser: argparse.ArgumentParser, subparsers: Any):
48
+
49
+ # --------------------
50
+ # --- Scenario API options (log_collector)
51
+ # --------------------
52
+
53
+ parser_log_collector = subparsers.add_parser(
54
+ "log_collector",
55
+ help="Scenario API related commands (log_collector)",
56
+ formatter_class=root_parser.formatter_class,
57
+ )
58
+ subparsers_log_collector = parser_log_collector.add_subparsers()
59
+
60
+ # 'log_collector_list' command
61
+ parser_log_collector_list = subparsers_log_collector.add_parser(
62
+ "list",
63
+ help="List log collectors configuration information",
64
+ formatter_class=root_parser.formatter_class,
65
+ )
66
+ parser_log_collector_list.set_defaults(func=log_collector_list_handler)
67
+ parser_log_collector_list.add_argument(
68
+ "--json", help="Return JSON result.", action="store_true"
69
+ )
70
+
71
+ parser_log_collector.set_defaults(func=lambda _: parser_log_collector.print_help())
@@ -0,0 +1,375 @@
1
+ # -*- coding: utf-8 -*-
2
+ import argparse
3
+ import sys
4
+ import time
5
+ from pathlib import Path
6
+ from typing import Any
7
+ from typing import Dict
8
+ from typing import List
9
+
10
+ from colorama import Fore
11
+ from colorama import Style
12
+
13
+ from cr_api_client import redteam_api # type: ignore[attr-defined]
14
+
15
+
16
+ def select_attack_session() -> Dict:
17
+
18
+ attack_sessions = redteam_api.attack_sessions()
19
+ print("Choose one attack session in list:")
20
+
21
+ list_sessions(attack_sessions)
22
+
23
+ id_session = input("Select attack session index: ")
24
+ try:
25
+ attack_session = attack_sessions[int(id_session)]
26
+ except Exception as e:
27
+ print(f"Error when fetching command: '{e}'")
28
+ sys.exit(1)
29
+
30
+ return attack_session
31
+
32
+
33
+ def list_sessions(attack_sessions: List[Dict]) -> None:
34
+ i = 0
35
+ for a_s in attack_sessions:
36
+
37
+ # Check attack session link
38
+ if a_s.get("is_up", False):
39
+ connected = "UP"
40
+ else:
41
+ connected = "DOWN"
42
+
43
+ if a_s["privilege_level"] == 0:
44
+ privilege = "user"
45
+ elif a_s["privilege_level"] == 1:
46
+ privilege = "user UAC"
47
+ else:
48
+ privilege = "administrator"
49
+ print(
50
+ f""" [{connected}] {i} : {a_s["identifier"]}, {a_s["username"]} (privilege {privilege}) on {a_s["target"]["host"]["hostname"]} ({a_s["target"]["ip"]})"""
51
+ )
52
+ i = i + 1
53
+
54
+
55
+ def redteam_session_list_handler(args: Any) -> None:
56
+ sessions = redteam_api.attack_sessions()
57
+ list_sessions(sessions)
58
+
59
+
60
+ def redteam_command_history_handler(args: Any) -> None:
61
+ show_output = args.show_output
62
+
63
+ commands = redteam_api.get_commands()
64
+
65
+ for command in commands:
66
+ print(f"""[+] {command["id"]} ({command["status"]}): '{command["command"]}'""")
67
+
68
+ if show_output:
69
+ print(command["output"])
70
+
71
+
72
+ def redteam_command_get_handler(args: Any) -> None:
73
+ command_id = args.command_id
74
+ show_output = args.show_output
75
+
76
+ try:
77
+ command = redteam_api.get_command(command_id=command_id)
78
+ except Exception as e:
79
+ print(f"Error when fetching command: '{e}'")
80
+ sys.exit(1)
81
+
82
+ print(f"""[+] {command["id"]}: '{command["command"]}'""")
83
+
84
+ if show_output:
85
+ print(command["output"])
86
+
87
+
88
+ def redteam_command_execute_handler(args: Any) -> None:
89
+
90
+ if args.session_id is None:
91
+ attack_session = select_attack_session()
92
+ else:
93
+ attack_session = {}
94
+ attack_session["identifier"] = args.session_id
95
+
96
+ if args.command is None:
97
+ command = input("Command to execute (one line): ")
98
+ else:
99
+ command = args.command
100
+
101
+ if args.background is None:
102
+ background_input = (
103
+ input("Command need to be executed in background, y or n ? (n by default) ")
104
+ or "n"
105
+ )
106
+ if background_input not in ["y", "n"]:
107
+ print("You need to answer y or n.")
108
+ sys.exit(1)
109
+
110
+ if "y" in background_input:
111
+ background = True
112
+ else:
113
+ background = False
114
+ else:
115
+ if args.background.lower() == "true":
116
+ background = True
117
+ elif args.background.lower() == "false":
118
+ background = False
119
+ else:
120
+ print("Background argument is boolean (true or false)")
121
+ sys.exit(1)
122
+
123
+ if args.timeout is None:
124
+ max_time = (
125
+ input("Max waiting time for command result in seconds ? (60 by default) ")
126
+ or 60
127
+ )
128
+ try:
129
+ max_time = int(max_time)
130
+ except ValueError:
131
+ print("Error max waiting time is not an integer.")
132
+ sys.exit(1)
133
+ else:
134
+ try:
135
+ max_time = int(args.timeout)
136
+ except ValueError:
137
+ print("Timeout argument must be an integer.")
138
+ sys.exit(1)
139
+
140
+ manage_command(
141
+ command=command,
142
+ attack_session_identifier=attack_session["identifier"],
143
+ background=background,
144
+ max_time=max_time,
145
+ )
146
+
147
+
148
+ def redteam_shell_handler(args: Any) -> None:
149
+
150
+ print(f"{Style.BRIGHT}M{Fore.RED}&{Fore.RESET}NTIS SHELL{Style.NORMAL}")
151
+
152
+ attack_session = select_attack_session()
153
+
154
+ max_time = (
155
+ input("Max waiting time for command result in secondes ? (60 by default)") or 60
156
+ )
157
+ try:
158
+ max_time = int(max_time)
159
+ except ValueError:
160
+ print("Error max waiting time is not an integer.")
161
+ sys.exit(1)
162
+
163
+ stop = False
164
+
165
+ while not stop:
166
+ command = input("> ")
167
+ if command == "exit":
168
+ stop = True
169
+ break
170
+ manage_command(
171
+ command=command,
172
+ attack_session_identifier=attack_session["identifier"],
173
+ background=False,
174
+ max_time=max_time,
175
+ )
176
+
177
+
178
+ def manage_command(
179
+ command: str, attack_session_identifier: str, background: bool, max_time: int
180
+ ) -> None:
181
+ result, output = redteam_api.execute_command(
182
+ command, attack_session_identifier, background=background
183
+ )
184
+
185
+ id_command = output["idResultCommand"]
186
+
187
+ output = None
188
+ i = 0
189
+
190
+ while i < max_time:
191
+ result = redteam_api.get_command(id_command)
192
+ if result.get("output", "") != "":
193
+ output = result["output"]
194
+ break
195
+ time.sleep(1)
196
+ i = i + 1
197
+
198
+ if not output:
199
+ print(f"""Command timeout (more than {max_time} seconds)""")
200
+ print(output)
201
+
202
+
203
+ def redteam_upload_handler(args: Any) -> None:
204
+
205
+ attack_session = select_attack_session()
206
+
207
+ path = input("Local path for the file : ")
208
+ filename = Path(path).name
209
+
210
+ success = redteam_api.upload_file(
211
+ filepath=path, attack_session_identifier=attack_session["identifier"]
212
+ )
213
+
214
+ if success:
215
+ if attack_session["type"] == "powershell":
216
+ upload_path = f"""C:\\Users\\{attack_session["username"]}\\AppData\\Local\\Temp\\{filename}"""
217
+ else:
218
+ upload_path = f"""/tmp/{filename}"""
219
+
220
+ print(f"File upload successfully on {upload_path}")
221
+ else:
222
+ print("Upload failed.")
223
+
224
+
225
+ def redteam_atomic_handler(args: Any):
226
+ filepath = args.atr_file # Atomic Red Team file
227
+ session_id = args.session_id
228
+
229
+ if not Path(filepath).exists():
230
+ print(f"File {filepath} doesn't exist")
231
+ sys.exit(1)
232
+
233
+ success = redteam_api.execute_atomic(filepath=filepath, attack_session_identifier=session_id)
234
+ if success:
235
+ print("[+] Execution started")
236
+ else:
237
+ print("Error in script execution")
238
+ sys.exit(1)
239
+
240
+
241
+ def add_redteam_parser(root_parser: argparse.ArgumentParser, subparsers: Any) -> None:
242
+ # Subparser redteam
243
+ parser_redteam = subparsers.add_parser(
244
+ "redteam",
245
+ help="Redteam actions in running labs",
246
+ formatter_class=root_parser.formatter_class,
247
+ )
248
+ subparsers_redteam = parser_redteam.add_subparsers()
249
+ parser_redteam.set_defaults(func=lambda _: parser_redteam.print_help())
250
+
251
+ # Redteam command parser
252
+ parser_redteam_script = subparsers_redteam.add_parser(
253
+ name="atomic",
254
+ help="Execute atomic test on redteam session",
255
+ formatter_class=root_parser.formatter_class,
256
+ )
257
+ parser_redteam_script.set_defaults(func=redteam_atomic_handler)
258
+ parser_redteam_script.add_argument(
259
+ "--session_id",
260
+ "-s",
261
+ help="Attack session identifier",
262
+ required=True,
263
+ )
264
+ parser_redteam_script.add_argument(
265
+ "--atr_file",
266
+ "-f",
267
+ type=str,
268
+ help="Import and execute an ATR (Atomic Red Team) file containing commands",
269
+ )
270
+
271
+ # Redteam session parser
272
+ parser_redteam_session = subparsers_redteam.add_parser(
273
+ name="session",
274
+ help="Redteam session information",
275
+ formatter_class=root_parser.formatter_class,
276
+ )
277
+ subparsers_session = parser_redteam_session.add_subparsers()
278
+ parser_redteam_session_list = subparsers_session.add_parser(
279
+ "list",
280
+ help="List all redteam sessions.",
281
+ formatter_class=root_parser.formatter_class,
282
+ )
283
+ parser_redteam_session_list.set_defaults(func=redteam_session_list_handler)
284
+ parser_redteam_session.set_defaults(
285
+ func=lambda _: parser_redteam_session.print_help()
286
+ )
287
+
288
+ # Redteam command parser
289
+ parser_redteam_command = subparsers_redteam.add_parser(
290
+ name="command",
291
+ help="Custom command execution on redteam session",
292
+ formatter_class=root_parser.formatter_class,
293
+ )
294
+ subparsers_command = parser_redteam_command.add_subparsers()
295
+ parser_redteam_command.set_defaults(
296
+ func=lambda _: parser_redteam_command.print_help()
297
+ )
298
+
299
+ parser_redteam_command_history = subparsers_command.add_parser(
300
+ "history",
301
+ help="Custom command execution history",
302
+ formatter_class=root_parser.formatter_class,
303
+ )
304
+ parser_redteam_command_history.add_argument(
305
+ "--show_output",
306
+ "-o",
307
+ help="Display attack output (can result in lengthy output)",
308
+ action="store_true",
309
+ )
310
+ parser_redteam_command_history.set_defaults(func=redteam_command_history_handler)
311
+
312
+ parser_redteam_command_get = subparsers_command.add_parser(
313
+ "get",
314
+ help="Get information regarding a specific command",
315
+ formatter_class=root_parser.formatter_class,
316
+ )
317
+ parser_redteam_command_get.set_defaults(func=redteam_command_get_handler)
318
+ parser_redteam_command_get.add_argument(
319
+ "--command_id",
320
+ "-i",
321
+ help="The command id",
322
+ type=str,
323
+ required=True,
324
+ )
325
+ parser_redteam_command_get.add_argument(
326
+ "--show_output",
327
+ "-o",
328
+ help="Display attack output (can result in lengthy output)",
329
+ action="store_true",
330
+ )
331
+
332
+ parser_redteam_command_execute = subparsers_command.add_parser(
333
+ "execute",
334
+ help="Execute command on attack session",
335
+ formatter_class=root_parser.formatter_class,
336
+ )
337
+ parser_redteam_command_execute.add_argument(
338
+ "--background",
339
+ "-b",
340
+ help="Execute command in background or not (Invoke-WmiMethod on Windows and & on Linux.)",
341
+ default=None,
342
+ )
343
+ parser_redteam_command_execute.add_argument(
344
+ "--timeout",
345
+ "-t",
346
+ help="Maximum time (seconds) to wait result command before timeout",
347
+ default=None,
348
+ )
349
+ parser_redteam_command_execute.add_argument(
350
+ "--session_id",
351
+ "-id",
352
+ help="Attack session identifier",
353
+ default=None,
354
+ )
355
+ parser_redteam_command_execute.add_argument(
356
+ "--command",
357
+ "-c",
358
+ help="Command to execute, must be surrounded by quotation marks",
359
+ default=None,
360
+ )
361
+ parser_redteam_command_execute.set_defaults(func=redteam_command_execute_handler)
362
+
363
+ parser_redteam_upload = subparsers_redteam.add_parser(
364
+ "upload",
365
+ help="Upload a file on target with attack session",
366
+ formatter_class=root_parser.formatter_class,
367
+ )
368
+ parser_redteam_upload.set_defaults(func=redteam_upload_handler)
369
+
370
+ parser_redteam_shell = subparsers_redteam.add_parser(
371
+ "shell",
372
+ help="Redteam shell on attack session",
373
+ formatter_class=root_parser.formatter_class,
374
+ )
375
+ parser_redteam_shell.set_defaults(func=redteam_shell_handler)