Skip to content

Commit 20953f0

Browse files
author
Patrick Bareiss
committed
GCP support
1 parent 23720b2 commit 20953f0

File tree

27 files changed

+851
-1482
lines changed

27 files changed

+851
-1482
lines changed

attack_range.py

Lines changed: 158 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
from modules.config_handler import ConfigHandler
66
from modules.aws_controller import AwsController
77
from modules.azure_controller import AzureController
8+
from modules.gcp_controller import GCPController
89
from modules import configuration
910

1011
# need to set this ENV var due to a OSX High Sierra forking bug
1112
# see this discussion for more details: https://github.com/ansible/ansible/issues/34056#issuecomment-352862252
12-
os.environ['OBJC_DISABLE_INITIALIZE_FORK_SAFETY'] = 'YES'
13+
os.environ["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES"
1314

1415

1516
def init(args):
1617
config_path = args.config
17-
print("""
18+
print(
19+
"""
1820
__
1921
.d$$b
2022
.' TO$;\\
@@ -44,41 +46,42 @@ def init(args):
4446
4547
By: Splunk Threat Research Team [STRT] - research@splunk.com
4648
47-
""")
49+
"""
50+
)
4851

4952
# parse config
5053
config = ConfigHandler.read_config(config_path)
5154
ConfigHandler.validate_config(config)
5255

53-
if config['general']['cloud_provider'] == 'aws':
54-
config.pop('azure')
55-
config.pop('local')
56-
config.pop('gcp')
56+
if config["general"]["cloud_provider"] == "aws":
57+
config.pop("azure")
58+
config.pop("local")
59+
config.pop("gcp")
5760
controller = AwsController(config)
58-
elif config['general']['cloud_provider'] == 'azure':
59-
config.pop('aws')
60-
config.pop('local')
61-
config.pop('gcp')
61+
elif config["general"]["cloud_provider"] == "azure":
62+
config.pop("aws")
63+
config.pop("local")
64+
config.pop("gcp")
6265
controller = AzureController(config)
63-
elif config['general']['cloud_provider'] == 'gcp':
64-
config.pop('aws')
65-
config.pop('local')
66-
config.pop('azure')
66+
elif config["general"]["cloud_provider"] == "gcp":
67+
config.pop("aws")
68+
config.pop("local")
69+
config.pop("azure")
6770
controller = GCPController(config)
68-
elif config['general']['cloud_provider'] == 'local':
71+
elif config["general"]["cloud_provider"] == "local":
6972
from modules.vagrant_controller import VagrantController
70-
config.pop('azure')
71-
config.pop('aws')
72-
config.pop('gcp')
73+
74+
config.pop("azure")
75+
config.pop("aws")
76+
config.pop("gcp")
7377
controller = VagrantController(config)
7478

7579
return controller
7680

7781

7882
def simulate(args):
7983
controller = init(args)
80-
controller.simulate(args.engine, args.target,
81-
args.technique, args.playbook)
84+
controller.simulate(args.engine, args.target, args.technique, args.playbook)
8285

8386

8487
def dump(args):
@@ -103,15 +106,21 @@ def destroy(args):
103106

104107
def stop(args):
105108
controller = init(args)
106-
instance_ids = [id.strip() for id in args.instance_ids.split(
107-
',')] if args.instance_ids else None
109+
instance_ids = (
110+
[id.strip() for id in args.instance_ids.split(",")]
111+
if args.instance_ids
112+
else None
113+
)
108114
controller.stop(instance_ids)
109115

110116

111117
def resume(args):
112118
controller = init(args)
113-
instance_ids = [id.strip() for id in args.instance_ids.split(
114-
',')] if args.instance_ids else None
119+
instance_ids = (
120+
[id.strip() for id in args.instance_ids.split(",")]
121+
if args.instance_ids
122+
else None
123+
)
115124
controller.resume(instance_ids)
116125

117126

@@ -149,42 +158,55 @@ def main(args):
149158
main function parses the arguments passed to the script and calls the respctive method.
150159
151160
:param args: Arguments passed by the user on command line while calling the script.
152-
:return: returns the output of the function called.
161+
:return: returns the output of the function called.
153162
"""
154163
# grab arguments
155164
parser = argparse.ArgumentParser(
156-
description="Use `attack_range.py action -h` to get help with any Attack Range action")
157-
parser.add_argument("-c", "--config", required=False, default="attack_range.yml",
158-
help="path to the configuration file of the attack range")
165+
description="Use `attack_range.py action -h` to get help with any Attack Range action"
166+
)
167+
parser.add_argument(
168+
"-c",
169+
"--config",
170+
required=False,
171+
default="attack_range.yml",
172+
help="path to the configuration file of the attack range",
173+
)
159174
parser.set_defaults(func=lambda _: parser.print_help())
160175

161-
actions_parser = parser.add_subparsers(
162-
title="attack Range actions", dest="action")
176+
actions_parser = parser.add_subparsers(title="attack Range actions", dest="action")
163177
configure_parser = actions_parser.add_parser(
164-
"configure", help="configure a new attack range")
178+
"configure", help="configure a new attack range"
179+
)
165180
build_parser = actions_parser.add_parser(
166-
"build", help="builds attack range instances")
181+
"build", help="builds attack range instances"
182+
)
167183
simulate_parser = actions_parser.add_parser(
168-
"simulate", help="simulates attack techniques")
184+
"simulate", help="simulates attack techniques"
185+
)
169186
destroy_parser = actions_parser.add_parser(
170-
"destroy", help="destroy attack range instances")
171-
stop_parser = actions_parser.add_parser(
172-
"stop", help="stops attack range instances")
187+
"destroy", help="destroy attack range instances"
188+
)
189+
stop_parser = actions_parser.add_parser("stop", help="stops attack range instances")
173190
resume_parser = actions_parser.add_parser(
174-
"resume", help="resumes previously stopped attack range instances")
175-
packer_parser = actions_parser.add_parser(
176-
"packer", help="create golden images")
191+
"resume", help="resumes previously stopped attack range instances"
192+
)
193+
packer_parser = actions_parser.add_parser("packer", help="create golden images")
177194
show_parser = actions_parser.add_parser("show", help="list machines")
178195
dump_parser = actions_parser.add_parser(
179-
"dump", help="dump locally logs from attack range instances")
196+
"dump", help="dump locally logs from attack range instances"
197+
)
180198
replay_parser = actions_parser.add_parser(
181-
"replay", help="replay dumps into the splunk server")
199+
"replay", help="replay dumps into the splunk server"
200+
)
182201
create_remote_backend_parser = actions_parser.add_parser(
183-
"create_remote_backend", help="Create a Remote Backend")
202+
"create_remote_backend", help="Create a Remote Backend"
203+
)
184204
delete_remote_backend_parser = actions_parser.add_parser(
185-
"delete_remote_backend", help="Delete a Remote Backend")
205+
"delete_remote_backend", help="Delete a Remote Backend"
206+
)
186207
init_remote_backend_parser = actions_parser.add_parser(
187-
"init_remote_backend", help="Init a Remote Backend")
208+
"init_remote_backend", help="Init a Remote Backend"
209+
)
188210

189211
# Build arguments
190212
build_parser.set_defaults(func=build)
@@ -194,75 +216,127 @@ def main(args):
194216

195217
# Stop arguments
196218
stop_parser.set_defaults(func=stop)
197-
stop_parser.add_argument("--instance_ids", required=False,
198-
type=str, help="comma-separated list of instance IDs to stop")
219+
stop_parser.add_argument(
220+
"--instance_ids",
221+
required=False,
222+
type=str,
223+
help="comma-separated list of instance IDs to stop",
224+
)
199225

200226
# Resume arguments
201227
resume_parser.set_defaults(func=resume)
202-
resume_parser.add_argument("--instance_ids", required=False,
203-
type=str, help="comma-separated list of instance IDs to resume")
228+
resume_parser.add_argument(
229+
"--instance_ids",
230+
required=False,
231+
type=str,
232+
help="comma-separated list of instance IDs to resume",
233+
)
204234

205235
# Packer agruments
206-
packer_parser.add_argument("-in", "--image_name", required=True, type=str,
207-
help="provide image name such as splunk, linux, windows-2016, windows-2019, nginx, windows-10, windows-11")
236+
packer_parser.add_argument(
237+
"-in",
238+
"--image_name",
239+
required=True,
240+
type=str,
241+
help="provide image name such as splunk, linux, windows-2016, windows-2019, nginx, windows-10, windows-11",
242+
)
208243
packer_parser.set_defaults(func=packer)
209244

210245
# Configure arguments
211-
configure_parser.add_argument("-c", "--config", required=False, type=str, default='attack_range.yml',
212-
help="provide path to write configuration to")
246+
configure_parser.add_argument(
247+
"-c",
248+
"--config",
249+
required=False,
250+
type=str,
251+
default="attack_range.yml",
252+
help="provide path to write configuration to",
253+
)
213254
configure_parser.set_defaults(func=configure)
214255

215256
# Simulation arguments
216-
simulate_parser.add_argument("-e", "--engine", required=False, default="ART",
217-
help="simulation engine to use. Available options are: PurpleSharp and ART (default)")
218-
simulate_parser.add_argument("-t", "--target", required=True,
219-
help="target for attack simulation. Use the name of the aws EC2 name")
220-
simulate_parser.add_argument("-te", "--technique", required=False, type=str, default="",
221-
help="comma delimited list of MITRE ATT&CK technique ID to simulate in the "
222-
"attack_range, example: T1117, T1118")
223-
simulate_parser.add_argument("-p", "--playbook", required=False, type=str, default="",
224-
help="file path for a simulation playbook")
257+
simulate_parser.add_argument(
258+
"-e",
259+
"--engine",
260+
required=False,
261+
default="ART",
262+
help="simulation engine to use. Available options are: PurpleSharp and ART (default)",
263+
)
264+
simulate_parser.add_argument(
265+
"-t",
266+
"--target",
267+
required=True,
268+
help="target for attack simulation. Use the name of the aws EC2 name",
269+
)
270+
simulate_parser.add_argument(
271+
"-te",
272+
"--technique",
273+
required=False,
274+
type=str,
275+
default="",
276+
help="comma delimited list of MITRE ATT&CK technique ID to simulate in the "
277+
"attack_range, example: T1117, T1118",
278+
)
279+
simulate_parser.add_argument(
280+
"-p",
281+
"--playbook",
282+
required=False,
283+
type=str,
284+
default="",
285+
help="file path for a simulation playbook",
286+
)
225287

226288
simulate_parser.set_defaults(func=simulate)
227289

228290
# Dump Arguments
229-
dump_parser.add_argument("-fn", "--file_name", required=True,
230-
help="file name of the attack_data")
231-
dump_parser.add_argument("--search", required=True,
232-
help="splunk search to export")
233-
dump_parser.add_argument("--earliest", required=True,
234-
help="earliest time of the splunk search")
235-
dump_parser.add_argument("--latest", required=False, default="now",
236-
help="latest time of the splunk search")
291+
dump_parser.add_argument(
292+
"-fn", "--file_name", required=True, help="file name of the attack_data"
293+
)
294+
dump_parser.add_argument("--search", required=True, help="splunk search to export")
295+
dump_parser.add_argument(
296+
"--earliest", required=True, help="earliest time of the splunk search"
297+
)
298+
dump_parser.add_argument(
299+
"--latest",
300+
required=False,
301+
default="now",
302+
help="latest time of the splunk search",
303+
)
237304
dump_parser.set_defaults(func=dump)
238305

239306
# Replay Arguments
240-
replay_parser.add_argument("-fn", "--file_name", required=True,
241-
help="file name of the attack_data")
242-
replay_parser.add_argument("--source", required=True,
243-
help="source of replayed data")
244-
replay_parser.add_argument("--sourcetype", required=True,
245-
help="sourcetype of replayed data")
246-
replay_parser.add_argument("--index", required=False, default="test",
247-
help="index of replayed data")
307+
replay_parser.add_argument(
308+
"-fn", "--file_name", required=True, help="file name of the attack_data"
309+
)
310+
replay_parser.add_argument(
311+
"--source", required=True, help="source of replayed data"
312+
)
313+
replay_parser.add_argument(
314+
"--sourcetype", required=True, help="sourcetype of replayed data"
315+
)
316+
replay_parser.add_argument(
317+
"--index", required=False, default="test", help="index of replayed data"
318+
)
248319
replay_parser.set_defaults(func=replay)
249320

250321
# Show arguments
251322
show_parser.set_defaults(func=show, machines=True)
252323

253324
# Create Remote Backend
254-
create_remote_backend_parser.add_argument("-bn", "--backend_name", required=True,
255-
help="name of the remote backend")
325+
create_remote_backend_parser.add_argument(
326+
"-bn", "--backend_name", required=True, help="name of the remote backend"
327+
)
256328
create_remote_backend_parser.set_defaults(func=create_remote_backend)
257329

258330
# Delete Remote Backend
259-
delete_remote_backend_parser.add_argument("-bn", "--backend_name", required=True,
260-
help="name of the remote backend")
331+
delete_remote_backend_parser.add_argument(
332+
"-bn", "--backend_name", required=True, help="name of the remote backend"
333+
)
261334
delete_remote_backend_parser.set_defaults(func=delete_remote_backend)
262335

263336
# Init Remote Backend
264-
init_remote_backend_parser.add_argument("-bn", "--backend_name", required=True,
265-
help="name of the remote backend")
337+
init_remote_backend_parser.add_argument(
338+
"-bn", "--backend_name", required=True, help="name of the remote backend"
339+
)
266340
init_remote_backend_parser.set_defaults(func=init_remote_backend)
267341

268342
# # parse them

configs/attack_range_default.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ azure:
9393
event_hub_host_name: "xxx"
9494
# All these fields are needed to configure the Azure logs. See the chapter Azure Logs in the docs page Attack Range Cloud.
9595

96+
gcp:
97+
region: "europe-west3"
98+
zone: "europe-west3-a"
99+
project_id: "project-id"
100+
public_key_path: "~/.ssh/id_rsa.pub"
101+
private_key_path: "~/.ssh/id_rsa"
102+
use_static_ip: "0"
103+
96104
local:
97105
provider: "Virtual Box"
98106
# Attack Range Local used Virtualbox and Vagrant to build the Attack Range.

0 commit comments

Comments
 (0)